1   // ========================================================================
2   // Copyright 2006 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.jetty.servlet;
16  
17  import java.io.IOException;
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.servlet.ServletContext;
24  import javax.servlet.ServletException;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  
28  import org.mortbay.jetty.handler.ContextHandler;
29  import org.mortbay.jetty.handler.ErrorHandler;
30  import org.mortbay.jetty.webapp.WebAppContext;
31  import org.mortbay.log.Log;
32  import org.mortbay.util.TypeUtil;
33  
34  /** Error Page Error Handler
35   * 
36   * An ErrorHandler that maps exceptions and status codes to URIs for dispatch using
37   * the internal ERROR style of dispatch.
38   * @author gregw
39   *
40   */
41  public class ErrorPageErrorHandler extends ErrorHandler
42  {
43      protected ServletContext _servletContext;
44      protected Map _errorPages; // code or exception to URL
45      protected List _errorPageList; // list of ErrorCode by range 
46  
47      /* ------------------------------------------------------------ */
48      /**
49       * @param context
50       */
51      public ErrorPageErrorHandler()
52      {}
53  
54      /* ------------------------------------------------------------ */
55      /* 
56       * @see org.mortbay.jetty.handler.ErrorHandler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
57       */
58      public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException
59      {
60          if (_errorPages!=null)
61          {
62              String error_page= null;
63              Class exClass= (Class)request.getAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE);
64              
65              if (ServletException.class.equals(exClass))
66              {
67                  error_page= (String)_errorPages.get(exClass.getName());
68                  if (error_page == null)
69                  {
70                      Throwable th= (Throwable)request.getAttribute(ServletHandler.__J_S_ERROR_EXCEPTION);
71                      while (th instanceof ServletException)
72                          th= ((ServletException)th).getRootCause();
73                      if (th != null)
74                          exClass= th.getClass();
75                  }
76              }
77              
78              while (error_page == null && exClass != null )
79              {
80                  error_page= (String)_errorPages.get(exClass.getName());
81                  exClass= exClass.getSuperclass();
82              }
83              
84              if (error_page == null)
85              {
86                  // look for an exact code match
87                  Integer code=(Integer)request.getAttribute(ServletHandler.__J_S_ERROR_STATUS_CODE);
88                  if (code!=null)
89                  {
90                      error_page= (String)_errorPages.get(TypeUtil.toString(code.intValue()));
91  
92                      // if still not found
93                      if ((error_page == null) && (_errorPageList != null))
94                      {
95                          // look for an error code range match.
96                          for (int i = 0; i < _errorPageList.size(); i++)
97                          {
98                              ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
99                              if (errCode.isInRange(code.intValue()))
100                             {
101                                 error_page = errCode.getUri();
102                                 break;
103                             }
104                         }
105                     }
106                 }
107             }
108             
109             if (error_page!=null)
110             {
111                 String old_error_page=(String)request.getAttribute(WebAppContext.ERROR_PAGE);
112                 if (old_error_page==null || !old_error_page.equals(error_page))
113                 {
114                     request.setAttribute(WebAppContext.ERROR_PAGE, error_page);
115                     
116                     Dispatcher dispatcher = (Dispatcher) _servletContext.getRequestDispatcher(error_page);
117                     try
118                     {
119                         if(dispatcher!=null)
120                         {    
121                             dispatcher.error(request, response);
122                             return;
123                         }
124                         else
125                         {
126                             Log.warn("No error page "+error_page);
127                         }
128                     }
129                     catch (ServletException e)
130                     {
131                         Log.warn(Log.EXCEPTION, e);
132                         return;
133                     }
134                 }
135             }
136         }
137         
138         super.handle(target, request, response, dispatch);
139     }
140 
141     /* ------------------------------------------------------------ */
142     /**
143      * @return Returns the errorPages.
144      */
145     public Map getErrorPages()
146     {
147         return _errorPages;
148     }
149     
150     /* ------------------------------------------------------------ */
151     /**
152      * @param errorPages The errorPages to set. A map of Exception class name  or error code as a string to URI string
153      */
154     public void setErrorPages(Map errorPages)
155     {
156         _errorPages = errorPages;
157     }
158 
159     /* ------------------------------------------------------------ */
160     /** Add Error Page mapping for an exception class
161      * This method is called as a result of an exception-type element in a web.xml file
162      * or may be called directly
163      * @param code The class (or superclass) of the matching exceptions
164      * @param uri The URI of the error page.
165      */
166     public void addErrorPage(Class exception,String uri)
167     {
168         if (_errorPages==null)
169             _errorPages=new HashMap();
170         _errorPages.put(exception.getName(),uri);
171     }
172     
173     /* ------------------------------------------------------------ */
174     /** Add Error Page mapping for a status code.
175      * This method is called as a result of an error-code element in a web.xml file
176      * or may be called directly
177      * @param code The HTTP status code to match
178      * @param uri The URI of the error page.
179      */
180     public void addErrorPage(int code,String uri)
181     {
182         if (_errorPages==null)
183             _errorPages=new HashMap();
184         _errorPages.put(TypeUtil.toString(code),uri);
185     }
186     
187     /* ------------------------------------------------------------ */
188     /** Add Error Page mapping for a status code range.
189      * This method is not available from web.xml and must be called
190      * directly.
191      * @param from The lowest matching status code
192      * @param to The highest matching status code
193      * @param uri The URI of the error page.
194      */
195     public void addErrorPage(int from, int to, String uri)
196     {
197         if (_errorPageList == null)
198         {
199             _errorPageList = new ArrayList();
200         }
201         _errorPageList.add(new ErrorCodeRange(from, to, uri));
202     }
203 
204     /* ------------------------------------------------------------ */
205     protected void doStart() throws Exception
206     {
207         super.doStart();
208         _servletContext=ContextHandler.getCurrentContext();
209     }
210 
211     /* ------------------------------------------------------------ */
212     protected void doStop() throws Exception
213     {
214         // TODO Auto-generated method stub
215         super.doStop();
216     }
217 
218     /* ------------------------------------------------------------ */
219     /* ------------------------------------------------------------ */
220     private class ErrorCodeRange
221     {
222         private int _from;
223         private int _to;
224         private String _uri;
225         
226         ErrorCodeRange(int from, int to, String uri)
227             throws IllegalArgumentException
228         {
229             if (from > to)
230                 throw new IllegalArgumentException("from>to");
231             
232             _from = from;
233             _to = to;
234             _uri = uri;
235         }
236         
237         boolean isInRange(int value)
238         {
239             if ((value >= _from) && (value <= _to))
240             {
241                 return true;
242             }
243             
244             return false;
245         }
246         
247         String getUri()
248         {
249             return _uri;
250         }
251         
252         public String toString()
253         {
254             return "from: " + _from + ",to: " + _to + ",uri: " + _uri;
255         }
256     }    
257 }