001: /*
002: * Copyright 1999-2001,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.core;
018:
019: import java.io.IOException;
020:
021: import javax.management.MalformedObjectNameException;
022: import javax.management.ObjectName;
023: import javax.servlet.Servlet;
024: import javax.servlet.ServletException;
025: import javax.servlet.ServletRequest;
026: import javax.servlet.ServletResponse;
027: import javax.servlet.UnavailableException;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import org.apache.catalina.Context;
032: import org.apache.catalina.Globals;
033: import org.apache.catalina.HttpRequest;
034: import org.apache.catalina.Logger;
035: import org.apache.catalina.Request;
036: import org.apache.catalina.Response;
037: import org.apache.catalina.ValveContext;
038: import org.apache.catalina.connector.ClientAbortException;
039: import org.apache.catalina.util.StringManager;
040: import org.apache.catalina.valves.ValveBase;
041: import org.apache.commons.beanutils.PropertyUtils;
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044: import org.apache.tomcat.util.buf.MessageBytes;
045:
046: /**
047: * Valve that implements the default basic behavior for the
048: * <code>StandardWrapper</code> container implementation.
049: *
050: * @author Craig R. McClanahan
051: * @version $Revision: 1.26 $ $Date: 2004/03/04 21:23:18 $
052: */
053:
054: final class StandardWrapperValve extends ValveBase {
055:
056: private static Log log = LogFactory
057: .getLog(StandardWrapperValve.class);
058:
059: // ----------------------------------------------------- Instance Variables
060:
061: // Some JMX statistics. This vavle is associated with a StandardWrapper.
062: // We exponse the StandardWrapper as JMX ( j2eeType=Servlet ). The fields
063: // are here for performance.
064: private volatile long processingTime;
065: private volatile long maxTime;
066: private volatile long minTime = Long.MAX_VALUE;
067: private volatile int requestCount;
068: private volatile int errorCount;
069:
070: /**
071: * The string manager for this package.
072: */
073: private static final StringManager sm = StringManager
074: .getManager(Constants.Package);
075:
076: // --------------------------------------------------------- Public Methods
077:
078: /**
079: * Invoke the servlet we are managing, respecting the rules regarding
080: * servlet lifecycle and SingleThreadModel support.
081: *
082: * @param request Request to be processed
083: * @param response Response to be produced
084: * @param valveContext Valve context used to forward to the next Valve
085: *
086: * @exception IOException if an input/output error occurred
087: * @exception ServletException if a servlet error occurred
088: */
089: public final void invoke(Request request, Response response,
090: ValveContext valveContext) throws IOException,
091: ServletException {
092:
093: // Initialize local variables we may need
094: boolean unavailable = false;
095: Throwable throwable = null;
096: // This should be a Request attribute...
097: long t1 = System.currentTimeMillis();
098: requestCount++;
099: StandardWrapper wrapper = (StandardWrapper) getContainer();
100: HttpRequest hrequest = (HttpRequest) request;
101: Servlet servlet = null;
102: HttpServletRequest hreq = (HttpServletRequest) request
103: .getRequest();
104: HttpServletResponse hres = (HttpServletResponse) response
105: .getResponse();
106:
107: // Check for the application being marked unavailable
108: if (!((Context) wrapper.getParent()).getAvailable()) {
109: hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
110: sm.getString("standardContext.isUnavailable"));
111: unavailable = true;
112: }
113:
114: // Check for the servlet being marked unavailable
115: if (!unavailable && wrapper.isUnavailable()) {
116: log(sm.getString("standardWrapper.isUnavailable", wrapper
117: .getName()));
118: if (hres == null) {
119: ; // NOTE - Not much we can do generically
120: } else {
121: long available = wrapper.getAvailable();
122: if ((available > 0L) && (available < Long.MAX_VALUE)) {
123: hres.setDateHeader("Retry-After", available);
124: hres.sendError(
125: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
126: sm.getString(
127: "standardWrapper.isUnavailable",
128: wrapper.getName()));
129: } else if (available == Long.MAX_VALUE) {
130: hres.sendError(HttpServletResponse.SC_NOT_FOUND, sm
131: .getString("standardWrapper.notFound",
132: wrapper.getName()));
133: }
134: }
135: unavailable = true;
136: }
137:
138: // Allocate a servlet instance to process this request
139: try {
140: if (!unavailable) {
141: servlet = wrapper.allocate();
142: }
143: } catch (UnavailableException e) {
144: long available = wrapper.getAvailable();
145: if ((available > 0L) && (available < Long.MAX_VALUE)) {
146: hres.setDateHeader("Retry-After", available);
147: hres.sendError(
148: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
149: sm.getString("standardWrapper.isUnavailable",
150: wrapper.getName()));
151: } else if (available == Long.MAX_VALUE) {
152: hres.sendError(HttpServletResponse.SC_NOT_FOUND, sm
153: .getString("standardWrapper.notFound", wrapper
154: .getName()));
155: }
156: } catch (ServletException e) {
157: log(sm.getString("standardWrapper.allocateException",
158: wrapper.getName()), e);
159: throwable = e;
160: exception(request, response, e);
161: servlet = null;
162: } catch (Throwable e) {
163: log(sm.getString("standardWrapper.allocateException",
164: wrapper.getName()), e);
165: throwable = e;
166: exception(request, response, e);
167: servlet = null;
168: }
169:
170: // Acknowlege the request
171: try {
172: response.sendAcknowledgement();
173: } catch (IOException e) {
174: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
175: log(sm.getString("standardWrapper.acknowledgeException",
176: wrapper.getName()), e);
177: throwable = e;
178: exception(request, response, e);
179: } catch (Throwable e) {
180: log(sm.getString("standardWrapper.acknowledgeException",
181: wrapper.getName()), e);
182: throwable = e;
183: exception(request, response, e);
184: servlet = null;
185: }
186: MessageBytes requestPathMB = null;
187: if (hreq != null) {
188: requestPathMB = hrequest.getRequestPathMB();
189: }
190: hreq.setAttribute(
191: ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
192: ApplicationFilterFactory.REQUEST_INTEGER);
193: hreq.setAttribute(
194: ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
195: requestPathMB);
196: // Create the filter chain for this request
197: ApplicationFilterFactory factory = ApplicationFilterFactory
198: .getInstance();
199: ApplicationFilterChain filterChain = factory.createFilterChain(
200: (ServletRequest) request, wrapper, servlet);
201:
202: // Call the filter chain for this request
203: // NOTE: This also calls the servlet's service() method
204: try {
205: String jspFile = wrapper.getJspFile();
206: if (jspFile != null)
207: hreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
208: else
209: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
210: if ((servlet != null) && (filterChain != null)) {
211: filterChain.doFilter(hreq, hres);
212: }
213: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
214: } catch (ClientAbortException e) {
215: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
216: throwable = e;
217: exception(request, response, e);
218: } catch (IOException e) {
219: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
220: log(sm.getString("standardWrapper.serviceException",
221: wrapper.getName()), e);
222: throwable = e;
223: exception(request, response, e);
224: } catch (UnavailableException e) {
225: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
226: log(sm.getString("standardWrapper.serviceException",
227: wrapper.getName()), e);
228: // throwable = e;
229: // exception(request, response, e);
230: wrapper.unavailable(e);
231: long available = wrapper.getAvailable();
232: if ((available > 0L) && (available < Long.MAX_VALUE)) {
233: hres.setDateHeader("Retry-After", available);
234: hres.sendError(
235: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
236: sm.getString("standardWrapper.isUnavailable",
237: wrapper.getName()));
238: } else if (available == Long.MAX_VALUE) {
239: hres.sendError(HttpServletResponse.SC_NOT_FOUND, sm
240: .getString("standardWrapper.notFound", wrapper
241: .getName()));
242: }
243: // Do not save exception in 'throwable', because we
244: // do not want to do exception(request, response, e) processing
245: } catch (ServletException e) {
246: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
247: Throwable rootCause = e;
248: Throwable rootCauseCheck = null;
249:
250: // Extra aggressive rootCause finding
251: do {
252: try {
253: rootCauseCheck = (Throwable) PropertyUtils
254: .getProperty(rootCause, "rootCause");
255: if (rootCauseCheck != null)
256: rootCause = rootCauseCheck;
257:
258: } catch (ClassCastException ex) {
259: rootCauseCheck = null;
260: } catch (IllegalAccessException ex) {
261: rootCauseCheck = null;
262: } catch (NoSuchMethodException ex) {
263: rootCauseCheck = null;
264: } catch (java.lang.reflect.InvocationTargetException ex) {
265: rootCauseCheck = null;
266: }
267: } while (rootCauseCheck != null);
268:
269: if (!(rootCause instanceof ClientAbortException)) {
270: log(sm.getString("standardWrapper.serviceException",
271: wrapper.getName()), rootCause);
272: }
273: throwable = e;
274: exception(request, response, e);
275: } catch (Throwable e) {
276: hreq.removeAttribute(Globals.JSP_FILE_ATTR);
277: log(sm.getString("standardWrapper.serviceException",
278: wrapper.getName()), e);
279: throwable = e;
280: exception(request, response, e);
281: }
282:
283: // Release the filter chain (if any) for this request
284: try {
285: if (filterChain != null)
286: filterChain.release();
287: } catch (Throwable e) {
288: log(sm.getString("standardWrapper.releaseFilters", wrapper
289: .getName()), e);
290: if (throwable == null) {
291: throwable = e;
292: exception(request, response, e);
293: }
294: }
295:
296: // Deallocate the allocated servlet instance
297: try {
298: if (servlet != null) {
299: wrapper.deallocate(servlet);
300: }
301: } catch (Throwable e) {
302: log(sm.getString("standardWrapper.deallocateException",
303: wrapper.getName()), e);
304: if (throwable == null) {
305: throwable = e;
306: exception(request, response, e);
307: }
308: }
309:
310: // If this servlet has been marked permanently unavailable,
311: // unload it and release this instance
312: try {
313: if ((servlet != null)
314: && (wrapper.getAvailable() == Long.MAX_VALUE)) {
315: wrapper.unload();
316: }
317: } catch (Throwable e) {
318: log(sm.getString("standardWrapper.unloadException", wrapper
319: .getName()), e);
320: if (throwable == null) {
321: throwable = e;
322: exception(request, response, e);
323: }
324: }
325: long t2 = System.currentTimeMillis();
326:
327: long time = t2 - t1;
328: processingTime += time;
329: if (time > maxTime)
330: maxTime = time;
331: if (time < minTime)
332: minTime = time;
333:
334: }
335:
336: // -------------------------------------------------------- Private Methods
337:
338: /**
339: * Log a message on the Logger associated with our Container (if any)
340: *
341: * @param message Message to be logged
342: */
343: private void log(String message) {
344:
345: Logger logger = null;
346: if (container != null)
347: logger = container.getLogger();
348: if (logger != null)
349: logger.log("StandardWrapperValve[" + container.getName()
350: + "]: " + message);
351: else {
352: String containerName = null;
353: if (container != null)
354: containerName = container.getName();
355: System.out.println("StandardWrapperValve[" + containerName
356: + "]: " + message);
357: }
358:
359: }
360:
361: /**
362: * Log a message on the Logger associated with our Container (if any)
363: *
364: * @param message Message to be logged
365: * @param throwable Associated exception
366: */
367: private void log(String message, Throwable throwable) {
368:
369: Logger logger = null;
370: if (container != null)
371: logger = container.getLogger();
372: if (logger != null)
373: logger.log("StandardWrapperValve[" + container.getName()
374: + "]: " + message, throwable);
375: else {
376: String containerName = null;
377: if (container != null)
378: containerName = container.getName();
379: System.out.println("StandardWrapperValve[" + containerName
380: + "]: " + message);
381: System.out.println("" + throwable);
382: throwable.printStackTrace(System.out);
383: }
384:
385: }
386:
387: /**
388: * Handle the specified ServletException encountered while processing
389: * the specified Request to produce the specified Response. Any
390: * exceptions that occur during generation of the exception report are
391: * logged and swallowed.
392: *
393: * @param request The request being processed
394: * @param response The response being generated
395: * @param exception The exception that occurred (which possibly wraps
396: * a root cause exception
397: */
398: private void exception(Request request, Response response,
399: Throwable exception) {
400: ServletRequest sreq = request.getRequest();
401: sreq.setAttribute(Globals.EXCEPTION_ATTR, exception);
402:
403: ServletResponse sresponse = response.getResponse();
404: if (sresponse instanceof HttpServletResponse)
405: ((HttpServletResponse) sresponse)
406: .setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
407:
408: }
409:
410: public long getProcessingTime() {
411: return processingTime;
412: }
413:
414: public void setProcessingTime(long processingTime) {
415: this .processingTime = processingTime;
416: }
417:
418: public long getMaxTime() {
419: return maxTime;
420: }
421:
422: public void setMaxTime(long maxTime) {
423: this .maxTime = maxTime;
424: }
425:
426: public long getMinTime() {
427: return minTime;
428: }
429:
430: public void setMinTime(long minTime) {
431: this .minTime = minTime;
432: }
433:
434: public int getRequestCount() {
435: return requestCount;
436: }
437:
438: public void setRequestCount(int requestCount) {
439: this .requestCount = requestCount;
440: }
441:
442: public int getErrorCount() {
443: return errorCount;
444: }
445:
446: public void setErrorCount(int errorCount) {
447: this .errorCount = errorCount;
448: }
449:
450: // Don't register in JMX
451:
452: public ObjectName createObjectName(String domain, ObjectName parent)
453: throws MalformedObjectNameException {
454: return null;
455: }
456: }
|