View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.servlet;
15  
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Map;
21  
22  import javax.servlet.ServletContext;
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.eclipse.jetty.http.HttpMethods;
28  import org.eclipse.jetty.server.AbstractHttpConnection;
29  import org.eclipse.jetty.server.Dispatcher;
30  import org.eclipse.jetty.server.Request;
31  import org.eclipse.jetty.server.handler.ContextHandler;
32  import org.eclipse.jetty.server.handler.ErrorHandler;
33  import org.eclipse.jetty.util.log.Log;
34  import org.eclipse.jetty.util.log.Logger;
35  
36  /* ------------------------------------------------------------ */
37  /** Error Page Error Handler
38   *
39   * An ErrorHandler that maps exceptions and status codes to URIs for dispatch using
40   * the internal ERROR style of dispatch.
41   *
42   */
43  public class ErrorPageErrorHandler extends ErrorHandler
44  {
45      private static final Logger LOG = Log.getLogger(ErrorPageErrorHandler.class);
46  
47      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
48  
49      protected ServletContext _servletContext;
50      private final Map<String,String> _errorPages= new HashMap<String,String>(); // code or exception to URL
51      private final List<ErrorCodeRange> _errorPageList=new ArrayList<ErrorCodeRange>(); // list of ErrorCode by range
52  
53      /* ------------------------------------------------------------ */
54      public ErrorPageErrorHandler()
55      {}
56  
57      /* ------------------------------------------------------------ */
58      /**
59       * @see org.eclipse.jetty.server.handler.ErrorHandler#handle(String, Request, HttpServletRequest, HttpServletResponse)
60       */
61      @Override
62      public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
63      {
64          String method = request.getMethod();
65          if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD))
66          {
67              AbstractHttpConnection.getCurrentConnection().getRequest().setHandled(true);
68              return;
69          }
70          if (_errorPages!=null)
71          {
72              String error_page= null;
73              Class<?> exClass= (Class<?>)request.getAttribute(Dispatcher.ERROR_EXCEPTION_TYPE);
74  
75              if (ServletException.class.equals(exClass))
76              {
77                  error_page= (String)_errorPages.get(exClass.getName());
78                  if (error_page == null)
79                  {
80                      Throwable th= (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
81                      while (th instanceof ServletException)
82                          th= ((ServletException)th).getRootCause();
83                      if (th != null)
84                          exClass= th.getClass();
85                  }
86              }
87  
88              while (error_page == null && exClass != null )
89              {
90                  error_page= (String)_errorPages.get(exClass.getName());
91                  exClass= exClass.getSuperclass();
92              }
93  
94              if (error_page == null)
95              {
96                  // look for an exact code match
97                  Integer code=(Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
98                  if (code!=null)
99                  {
100                     error_page= (String)_errorPages.get(Integer.toString(code));
101 
102                     // if still not found
103                     if ((error_page == null) && (_errorPageList != null))
104                     {
105                         // look for an error code range match.
106                         for (int i = 0; i < _errorPageList.size(); i++)
107                         {
108                             ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
109                             if (errCode.isInRange(code))
110                             {
111                                 error_page = errCode.getUri();
112                                 break;
113                             }
114                         }
115                     }
116                 }
117             }
118 
119             if (error_page!=null)
120             {
121                 String old_error_page=(String)request.getAttribute(ERROR_PAGE);
122                 if (old_error_page==null || !old_error_page.equals(error_page))
123                 {
124                     request.setAttribute(ERROR_PAGE, error_page);
125 
126                     Dispatcher dispatcher = (Dispatcher) _servletContext.getRequestDispatcher(error_page);
127                     try
128                     {
129                         if(dispatcher!=null)
130                         {
131                             dispatcher.error(request, response);
132                             return;
133                         }
134                         else
135                         {
136                             LOG.warn("No error page "+error_page);
137                         }
138                     }
139                     catch (ServletException e)
140                     {
141                         LOG.warn(Log.EXCEPTION, e);
142                         return;
143                     }
144                 }
145             }
146         }
147 
148         super.handle(target, baseRequest, request, response);
149     }
150 
151     /* ------------------------------------------------------------ */
152     /**
153      * @return Returns the errorPages.
154      */
155     public Map<String,String> getErrorPages()
156     {
157         return _errorPages;
158     }
159 
160     /* ------------------------------------------------------------ */
161     /**
162      * @param errorPages The errorPages to set. A map of Exception class name  or error code as a string to URI string
163      */
164     public void setErrorPages(Map<String,String> errorPages)
165     {
166         _errorPages.clear();
167         if (errorPages!=null)
168             _errorPages.putAll(errorPages);
169     }
170 
171     /* ------------------------------------------------------------ */
172     /** Add Error Page mapping for an exception class
173      * This method is called as a result of an exception-type element in a web.xml file
174      * or may be called directly
175      * @param exception The exception
176      * @param uri The URI of the error page.
177      */
178     public void addErrorPage(Class<? extends Throwable> exception,String uri)
179     {
180         _errorPages.put(exception.getName(),uri);
181     }
182     
183     /* ------------------------------------------------------------ */
184     /** Add Error Page mapping for an exception class
185      * This method is called as a result of an exception-type element in a web.xml file
186      * or may be called directly
187      * @param exceptionClassName The exception
188      * @param uri The URI of the error page.
189      */
190     public void addErrorPage(String exceptionClassName,String uri)
191     {
192         _errorPages.put(exceptionClassName,uri);
193     }
194 
195     /* ------------------------------------------------------------ */
196     /** Add Error Page mapping for a status code.
197      * This method is called as a result of an error-code element in a web.xml file
198      * or may be called directly
199      * @param code The HTTP status code to match
200      * @param uri The URI of the error page.
201      */
202     public void addErrorPage(int code,String uri)
203     {
204         _errorPages.put(Integer.toString(code),uri);
205     }
206 
207     /* ------------------------------------------------------------ */
208     /** Add Error Page mapping for a status code range.
209      * This method is not available from web.xml and must be called
210      * directly.
211      * @param from The lowest matching status code
212      * @param to The highest matching status code
213      * @param uri The URI of the error page.
214      */
215     public void addErrorPage(int from, int to, String uri)
216     {
217         _errorPageList.add(new ErrorCodeRange(from, to, uri));
218     }
219 
220     /* ------------------------------------------------------------ */
221     @Override
222     protected void doStart() throws Exception
223     {
224         super.doStart();
225         _servletContext=ContextHandler.getCurrentContext();
226     }
227 
228     /* ------------------------------------------------------------ */
229     @Override
230     protected void doStop() throws Exception
231     {
232         // TODO Auto-generated method stub
233         super.doStop();
234     }
235 
236     /* ------------------------------------------------------------ */
237     /* ------------------------------------------------------------ */
238     private class ErrorCodeRange
239     {
240         private int _from;
241         private int _to;
242         private String _uri;
243 
244         ErrorCodeRange(int from, int to, String uri)
245             throws IllegalArgumentException
246         {
247             if (from > to)
248                 throw new IllegalArgumentException("from>to");
249 
250             _from = from;
251             _to = to;
252             _uri = uri;
253         }
254 
255         boolean isInRange(int value)
256         {
257             if ((value >= _from) && (value <= _to))
258             {
259                 return true;
260             }
261 
262             return false;
263         }
264 
265         String getUri()
266         {
267             return _uri;
268         }
269 
270         @Override
271         public String toString()
272         {
273             return "from: " + _from + ",to: " + _to + ",uri: " + _uri;
274         }
275     }
276 }