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