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          String method = request.getMethod();
70          if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method))
71          {
72              baseRequest.setHandled(true);
73              return;
74          }
75          if (_errorPages!=null)
76          {
77              String error_page= null;
78              Class<?> exClass= (Class<?>)request.getAttribute(Dispatcher.ERROR_EXCEPTION_TYPE);
79  
80              if (ServletException.class.equals(exClass))
81              {
82                  error_page= (String)_errorPages.get(exClass.getName());
83                  if (error_page == null)
84                  {
85                      Throwable th= (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
86                      while (th instanceof ServletException)
87                          th= ((ServletException)th).getRootCause();
88                      if (th != null)
89                          exClass= th.getClass();
90                  }
91              }
92  
93              while (error_page == null && exClass != null )
94              {
95                  error_page= (String)_errorPages.get(exClass.getName());
96                  exClass= exClass.getSuperclass();
97              }
98  
99              if (error_page == null)
100             {
101                 // look for an exact code match
102                 Integer code=(Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
103                 if (code!=null)
104                 {
105                     error_page= (String)_errorPages.get(Integer.toString(code));
106 
107                     // if still not found
108                     if ((error_page == null) && (_errorPageList != null))
109                     {
110                         // look for an error code range match.
111                         for (int i = 0; i < _errorPageList.size(); i++)
112                         {
113                             ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
114                             if (errCode.isInRange(code))
115                             {
116                                 error_page = errCode.getUri();
117                                 break;
118                             }
119                         }
120                     }
121                 }
122             }
123             
124             //try new servlet 3.0 global error page
125             if (error_page == null)
126             {
127                 error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
128             }
129 
130             if (error_page!=null)
131             {
132                 String old_error_page=(String)request.getAttribute(ERROR_PAGE);
133                 if (old_error_page==null || !old_error_page.equals(error_page))
134                 {
135                     request.setAttribute(ERROR_PAGE, error_page);
136 
137                     Dispatcher dispatcher = (Dispatcher) _servletContext.getRequestDispatcher(error_page);
138                     try
139                     {
140                         if(dispatcher!=null)
141                         {
142                             dispatcher.error(request, response);
143                             return;
144                         }
145                         else
146                         {
147                             LOG.warn("No error page "+error_page);
148                         }
149                     }
150                     catch (ServletException e)
151                     {
152                         LOG.warn(Log.EXCEPTION, e);
153                         return;
154                     }
155                 }
156             }
157         }
158 
159         super.handle(target, baseRequest, request, response);
160     }
161 
162     /* ------------------------------------------------------------ */
163     /**
164      * @return Returns the errorPages.
165      */
166     public Map<String,String> getErrorPages()
167     {
168         return _errorPages;
169     }
170 
171     /* ------------------------------------------------------------ */
172     /**
173      * @param errorPages The errorPages to set. A map of Exception class name  or error code as a string to URI string
174      */
175     public void setErrorPages(Map<String,String> errorPages)
176     {
177         _errorPages.clear();
178         if (errorPages!=null)
179             _errorPages.putAll(errorPages);
180     }
181 
182     /* ------------------------------------------------------------ */
183     /** Add Error Page mapping for an exception class
184      * This method is called as a result of an exception-type element in a web.xml file
185      * or may be called directly
186      * @param exception The exception
187      * @param uri The URI of the error page.
188      */
189     public void addErrorPage(Class<? extends Throwable> exception,String uri)
190     {
191         _errorPages.put(exception.getName(),uri);
192     }
193 
194     /* ------------------------------------------------------------ */
195     /** Add Error Page mapping for an exception class
196      * This method is called as a result of an exception-type element in a web.xml file
197      * or may be called directly
198      * @param exceptionClassName The exception
199      * @param uri The URI of the error page.
200      */
201     public void addErrorPage(String exceptionClassName,String uri)
202     {
203         _errorPages.put(exceptionClassName,uri);
204     }
205 
206     /* ------------------------------------------------------------ */
207     /** Add Error Page mapping for a status code.
208      * This method is called as a result of an error-code element in a web.xml file
209      * or may be called directly
210      * @param code The HTTP status code to match
211      * @param uri The URI of the error page.
212      */
213     public void addErrorPage(int code,String uri)
214     {
215         _errorPages.put(Integer.toString(code),uri);
216     }
217 
218     /* ------------------------------------------------------------ */
219     /** Add Error Page mapping for a status code range.
220      * This method is not available from web.xml and must be called
221      * directly.
222      * @param from The lowest matching status code
223      * @param to The highest matching status code
224      * @param uri The URI of the error page.
225      */
226     public void addErrorPage(int from, int to, String uri)
227     {
228         _errorPageList.add(new ErrorCodeRange(from, to, uri));
229     }
230 
231     /* ------------------------------------------------------------ */
232     @Override
233     protected void doStart() throws Exception
234     {
235         super.doStart();
236         _servletContext=ContextHandler.getCurrentContext();
237     }
238 
239     /* ------------------------------------------------------------ */
240     /* ------------------------------------------------------------ */
241     private class ErrorCodeRange
242     {
243         private int _from;
244         private int _to;
245         private String _uri;
246 
247         ErrorCodeRange(int from, int to, String uri)
248             throws IllegalArgumentException
249         {
250             if (from > to)
251                 throw new IllegalArgumentException("from>to");
252 
253             _from = from;
254             _to = to;
255             _uri = uri;
256         }
257 
258         boolean isInRange(int value)
259         {
260             if ((value >= _from) && (value <= _to))
261             {
262                 return true;
263             }
264 
265             return false;
266         }
267 
268         String getUri()
269         {
270             return _uri;
271         }
272 
273         @Override
274         public String toString()
275         {
276             return "from: " + _from + ",to: " + _to + ",uri: " + _uri;
277         }
278     }
279 }