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 }