1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.core;
20
21
22 import java.io.IOException;
23
24 import javax.servlet.ServletException;
25 import javax.servlet.ServletRequest;
26 import javax.servlet.ServletRequestEvent;
27 import javax.servlet.ServletRequestListener;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.catalina.CometEvent;
31 import org.apache.catalina.Container;
32 import org.apache.catalina.Globals;
33 import org.apache.catalina.Wrapper;
34 import org.apache.catalina.connector.Request;
35 import org.apache.catalina.connector.Response;
36 import org.apache.catalina.util.StringManager;
37 import org.apache.catalina.valves.ValveBase;
38 import org.apache.tomcat.util.buf.MessageBytes;
39
40 /**
41 * Valve that implements the default basic behavior for the
42 * <code>StandardContext</code> container implementation.
43 * <p>
44 * <b>USAGE CONSTRAINT</b>: This implementation is likely to be useful only
45 * when processing HTTP requests.
46 *
47 * @author Craig R. McClanahan
48 * @version $Revision: 673834 $ $Date: 2008-07-04 00:03:08 +0200 (Fri, 04 Jul 2008) $
49 */
50
51 final class StandardContextValve
52 extends ValveBase {
53
54
55 // ----------------------------------------------------- Instance Variables
56
57
58 /**
59 * The descriptive information related to this implementation.
60 */
61 private static final String info =
62 "org.apache.catalina.core.StandardContextValve/1.0";
63
64
65 /**
66 * The string manager for this package.
67 */
68 private static final StringManager sm =
69 StringManager.getManager(Constants.Package);
70
71
72 private StandardContext context = null;
73
74
75 // ------------------------------------------------------------- Properties
76
77
78 /**
79 * Return descriptive information about this Valve implementation.
80 */
81 public String getInfo() {
82
83 return (info);
84
85 }
86
87
88 // --------------------------------------------------------- Public Methods
89
90
91 /**
92 * Cast to a StandardContext right away, as it will be needed later.
93 *
94 * @see org.apache.catalina.Contained#setContainer(org.apache.catalina.Container)
95 */
96 public void setContainer(Container container) {
97 super.setContainer(container);
98 context = (StandardContext) container;
99 }
100
101
102 /**
103 * Select the appropriate child Wrapper to process this request,
104 * based on the specified request URI. If no matching Wrapper can
105 * be found, return an appropriate HTTP error.
106 *
107 * @param request Request to be processed
108 * @param response Response to be produced
109 * @param valveContext Valve context used to forward to the next Valve
110 *
111 * @exception IOException if an input/output error occurred
112 * @exception ServletException if a servlet error occurred
113 */
114 public final void invoke(Request request, Response response)
115 throws IOException, ServletException {
116
117 // Disallow any direct access to resources under WEB-INF or META-INF
118 MessageBytes requestPathMB = request.getRequestPathMB();
119 if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
120 || (requestPathMB.equalsIgnoreCase("/META-INF"))
121 || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
122 || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
123 notFound(response);
124 return;
125 }
126
127 // Wait if we are reloading
128 boolean reloaded = false;
129 while (context.getPaused()) {
130 reloaded = true;
131 try {
132 Thread.sleep(1000);
133 } catch (InterruptedException e) {
134 ;
135 }
136 }
137
138 // Reloading will have stopped the old webappclassloader and
139 // created a new one
140 if (reloaded &&
141 context.getLoader() != null &&
142 context.getLoader().getClassLoader() != null) {
143 Thread.currentThread().setContextClassLoader(
144 context.getLoader().getClassLoader());
145 }
146
147 // Select the Wrapper to be used for this Request
148 Wrapper wrapper = request.getWrapper();
149 if (wrapper == null) {
150 notFound(response);
151 return;
152 } else if (wrapper.isUnavailable()) {
153 // May be as a result of a reload, try and find the new wrapper
154 wrapper = (Wrapper) container.findChild(wrapper.getName());
155 if (wrapper == null) {
156 notFound(response);
157 return;
158 }
159 }
160
161 // Normal request processing
162 Object instances[] = context.getApplicationEventListeners();
163
164 ServletRequestEvent event = null;
165
166 if ((instances != null)
167 && (instances.length > 0)) {
168 event = new ServletRequestEvent
169 (((StandardContext) container).getServletContext(),
170 request.getRequest());
171 // create pre-service event
172 for (int i = 0; i < instances.length; i++) {
173 if (instances[i] == null)
174 continue;
175 if (!(instances[i] instanceof ServletRequestListener))
176 continue;
177 ServletRequestListener listener =
178 (ServletRequestListener) instances[i];
179 try {
180 listener.requestInitialized(event);
181 } catch (Throwable t) {
182 container.getLogger().error(sm.getString("standardContext.requestListener.requestInit",
183 instances[i].getClass().getName()), t);
184 ServletRequest sreq = request.getRequest();
185 sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
186 return;
187 }
188 }
189 }
190
191 wrapper.getPipeline().getFirst().invoke(request, response);
192
193 if ((instances !=null ) &&
194 (instances.length > 0)) {
195 // create post-service event
196 for (int i = 0; i < instances.length; i++) {
197 if (instances[i] == null)
198 continue;
199 if (!(instances[i] instanceof ServletRequestListener))
200 continue;
201 ServletRequestListener listener =
202 (ServletRequestListener) instances[i];
203 try {
204 listener.requestDestroyed(event);
205 } catch (Throwable t) {
206 container.getLogger().error(sm.getString("standardContext.requestListener.requestDestroy",
207 instances[i].getClass().getName()), t);
208 ServletRequest sreq = request.getRequest();
209 sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
210 }
211 }
212 }
213
214 }
215
216
217 /**
218 * Select the appropriate child Wrapper to process this request,
219 * based on the specified request URI. If no matching Wrapper can
220 * be found, return an appropriate HTTP error.
221 *
222 * @param request Request to be processed
223 * @param response Response to be produced
224 * @param valveContext Valve context used to forward to the next Valve
225 *
226 * @exception IOException if an input/output error occurred
227 * @exception ServletException if a servlet error occurred
228 */
229 public final void event(Request request, Response response, CometEvent event)
230 throws IOException, ServletException {
231
232 // Select the Wrapper to be used for this Request
233 Wrapper wrapper = request.getWrapper();
234
235 // Normal request processing
236 // FIXME: This could be an addition to the core API too
237 /*
238 Object instances[] = context.getApplicationEventListeners();
239
240 ServletRequestEvent event = null;
241
242 if ((instances != null)
243 && (instances.length > 0)) {
244 event = new ServletRequestEvent
245 (((StandardContext) container).getServletContext(),
246 request.getRequest());
247 // create pre-service event
248 for (int i = 0; i < instances.length; i++) {
249 if (instances[i] == null)
250 continue;
251 if (!(instances[i] instanceof ServletRequestListener))
252 continue;
253 ServletRequestListener listener =
254 (ServletRequestListener) instances[i];
255 try {
256 listener.requestInitialized(event);
257 } catch (Throwable t) {
258 container.getLogger().error(sm.getString("requestListenerValve.requestInit",
259 instances[i].getClass().getName()), t);
260 ServletRequest sreq = request.getRequest();
261 sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
262 return;
263 }
264 }
265 }
266 */
267
268 wrapper.getPipeline().getFirst().event(request, response, event);
269
270 /*
271 if ((instances !=null ) &&
272 (instances.length > 0)) {
273 // create post-service event
274 for (int i = 0; i < instances.length; i++) {
275 if (instances[i] == null)
276 continue;
277 if (!(instances[i] instanceof ServletRequestListener))
278 continue;
279 ServletRequestListener listener =
280 (ServletRequestListener) instances[i];
281 try {
282 listener.requestDestroyed(event);
283 } catch (Throwable t) {
284 container.getLogger().error(sm.getString("requestListenerValve.requestDestroy",
285 instances[i].getClass().getName()), t);
286 ServletRequest sreq = request.getRequest();
287 sreq.setAttribute(Globals.EXCEPTION_ATTR,t);
288 }
289 }
290 }
291 */
292
293 }
294
295
296 // -------------------------------------------------------- Private Methods
297
298
299 /**
300 * Report a "not found" error for the specified resource. FIXME: We
301 * should really be using the error reporting settings for this web
302 * application, but currently that code runs at the wrapper level rather
303 * than the context level.
304 *
305 * @param response The response we are creating
306 */
307 private void notFound(HttpServletResponse response) {
308
309 try {
310 response.sendError(HttpServletResponse.SC_NOT_FOUND);
311 } catch (IllegalStateException e) {
312 ;
313 } catch (IOException e) {
314 ;
315 }
316
317 }
318
319
320 }