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