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 }