View Javadoc

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