001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.core;
019:
020: import java.io.IOException;
021:
022: import javax.management.MalformedObjectNameException;
023: import javax.management.ObjectName;
024: import javax.servlet.Servlet;
025: import javax.servlet.ServletException;
026: import javax.servlet.UnavailableException;
027: import javax.servlet.http.HttpServletResponse;
028:
029: import org.apache.catalina.CometEvent;
030: import org.apache.catalina.CometProcessor;
031: import org.apache.catalina.Context;
032: import org.apache.catalina.Globals;
033: import org.apache.catalina.connector.ClientAbortException;
034: import org.apache.catalina.connector.Request;
035: import org.apache.catalina.connector.Response;
036: import org.apache.catalina.util.StringManager;
037: import org.apache.catalina.valves.ValveBase;
038: import org.apache.tomcat.util.buf.MessageBytes;
039: import org.apache.tomcat.util.log.SystemLogHandler;
040:
041: /**
042: * Valve that implements the default basic behavior for the
043: * <code>StandardWrapper</code> container implementation.
044: *
045: * @author Craig R. McClanahan
046: * @version $Revision: 557818 $ $Date: 2007-07-20 01:48:06 +0200 (ven., 20 juil. 2007) $
047: */
048:
049: final class StandardWrapperValve extends ValveBase {
050:
051: // ----------------------------------------------------- Instance Variables
052:
053: // Some JMX statistics. This vavle is associated with a StandardWrapper.
054: // We expose the StandardWrapper as JMX ( j2eeType=Servlet ). The fields
055: // are here for performance.
056: private volatile long processingTime;
057: private volatile long maxTime;
058: private volatile long minTime = Long.MAX_VALUE;
059: private volatile int requestCount;
060: private volatile int errorCount;
061:
062: /**
063: * The string manager for this package.
064: */
065: private static final StringManager sm = StringManager
066: .getManager(Constants.Package);
067:
068: // --------------------------------------------------------- Public Methods
069:
070: /**
071: * Invoke the servlet we are managing, respecting the rules regarding
072: * servlet lifecycle and SingleThreadModel support.
073: *
074: * @param request Request to be processed
075: * @param response Response to be produced
076: * @param valveContext Valve context used to forward to the next Valve
077: *
078: * @exception IOException if an input/output error occurred
079: * @exception ServletException if a servlet error occurred
080: */
081: public final void invoke(Request request, Response response)
082: throws IOException, ServletException {
083:
084: // Initialize local variables we may need
085: boolean unavailable = false;
086: Throwable throwable = null;
087: // This should be a Request attribute...
088: long t1 = System.currentTimeMillis();
089: requestCount++;
090: StandardWrapper wrapper = (StandardWrapper) getContainer();
091: Servlet servlet = null;
092: Context context = (Context) wrapper.getParent();
093:
094: // Check for the application being marked unavailable
095: if (!context.getAvailable()) {
096: response
097: .sendError(
098: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
099: sm
100: .getString("standardContext.isUnavailable"));
101: unavailable = true;
102: }
103:
104: // Check for the servlet being marked unavailable
105: if (!unavailable && wrapper.isUnavailable()) {
106: container.getLogger().info(
107: sm.getString("standardWrapper.isUnavailable",
108: wrapper.getName()));
109: long available = wrapper.getAvailable();
110: if ((available > 0L) && (available < Long.MAX_VALUE)) {
111: response.setDateHeader("Retry-After", available);
112: response.sendError(
113: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
114: sm.getString("standardWrapper.isUnavailable",
115: wrapper.getName()));
116: } else if (available == Long.MAX_VALUE) {
117: response.sendError(HttpServletResponse.SC_NOT_FOUND, sm
118: .getString("standardWrapper.notFound", wrapper
119: .getName()));
120: }
121: unavailable = true;
122: }
123:
124: // Allocate a servlet instance to process this request
125: try {
126: if (!unavailable) {
127: servlet = wrapper.allocate();
128: }
129: } catch (UnavailableException e) {
130: container.getLogger().error(
131: sm.getString("standardWrapper.allocateException",
132: wrapper.getName()), e);
133: long available = wrapper.getAvailable();
134: if ((available > 0L) && (available < Long.MAX_VALUE)) {
135: response.setDateHeader("Retry-After", available);
136: response.sendError(
137: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
138: sm.getString("standardWrapper.isUnavailable",
139: wrapper.getName()));
140: } else if (available == Long.MAX_VALUE) {
141: response.sendError(HttpServletResponse.SC_NOT_FOUND, sm
142: .getString("standardWrapper.notFound", wrapper
143: .getName()));
144: }
145: } catch (ServletException e) {
146: container.getLogger().error(
147: sm.getString("standardWrapper.allocateException",
148: wrapper.getName()),
149: StandardWrapper.getRootCause(e));
150: throwable = e;
151: exception(request, response, e);
152: servlet = null;
153: } catch (Throwable e) {
154: container.getLogger().error(
155: sm.getString("standardWrapper.allocateException",
156: wrapper.getName()), e);
157: throwable = e;
158: exception(request, response, e);
159: servlet = null;
160: }
161:
162: // Identify if the request is Comet related now that the servlet has been allocated
163: boolean comet = false;
164: if (servlet instanceof CometProcessor
165: && request
166: .getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE) {
167: comet = true;
168: request.setComet(true);
169: }
170:
171: // Acknowledge the request
172: try {
173: response.sendAcknowledgement();
174: } catch (IOException e) {
175: request.removeAttribute(Globals.JSP_FILE_ATTR);
176: container.getLogger().warn(
177: sm.getString(
178: "standardWrapper.acknowledgeException",
179: wrapper.getName()), e);
180: throwable = e;
181: exception(request, response, e);
182: } catch (Throwable e) {
183: container.getLogger().error(
184: sm.getString(
185: "standardWrapper.acknowledgeException",
186: wrapper.getName()), e);
187: throwable = e;
188: exception(request, response, e);
189: servlet = null;
190: }
191: MessageBytes requestPathMB = null;
192: if (request != null) {
193: requestPathMB = request.getRequestPathMB();
194: }
195: request.setAttribute(
196: ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
197: ApplicationFilterFactory.REQUEST_INTEGER);
198: request.setAttribute(
199: ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
200: requestPathMB);
201: // Create the filter chain for this request
202: ApplicationFilterFactory factory = ApplicationFilterFactory
203: .getInstance();
204: ApplicationFilterChain filterChain = factory.createFilterChain(
205: request, wrapper, servlet);
206: // Reset comet flag value after creating the filter chain
207: request.setComet(false);
208:
209: // Call the filter chain for this request
210: // NOTE: This also calls the servlet's service() method
211: try {
212: String jspFile = wrapper.getJspFile();
213: if (jspFile != null)
214: request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
215: else
216: request.removeAttribute(Globals.JSP_FILE_ATTR);
217: if ((servlet != null) && (filterChain != null)) {
218: // Swallow output if needed
219: if (context.getSwallowOutput()) {
220: try {
221: SystemLogHandler.startCapture();
222: if (comet) {
223: filterChain.doFilterEvent(request
224: .getEvent());
225: request.setComet(true);
226: } else {
227: filterChain.doFilter(request.getRequest(),
228: response.getResponse());
229: }
230: } finally {
231: String log = SystemLogHandler.stopCapture();
232: if (log != null && log.length() > 0) {
233: context.getLogger().info(log);
234: }
235: }
236: } else {
237: if (comet) {
238: filterChain.doFilterEvent(request.getEvent());
239: request.setComet(true);
240: } else {
241: filterChain.doFilter(request.getRequest(),
242: response.getResponse());
243: }
244: }
245:
246: }
247: request.removeAttribute(Globals.JSP_FILE_ATTR);
248: } catch (ClientAbortException e) {
249: request.removeAttribute(Globals.JSP_FILE_ATTR);
250: throwable = e;
251: exception(request, response, e);
252: } catch (IOException e) {
253: request.removeAttribute(Globals.JSP_FILE_ATTR);
254: container.getLogger().error(
255: sm.getString("standardWrapper.serviceException",
256: wrapper.getName()), e);
257: throwable = e;
258: exception(request, response, e);
259: } catch (UnavailableException e) {
260: request.removeAttribute(Globals.JSP_FILE_ATTR);
261: container.getLogger().error(
262: sm.getString("standardWrapper.serviceException",
263: wrapper.getName()), e);
264: // throwable = e;
265: // exception(request, response, e);
266: wrapper.unavailable(e);
267: long available = wrapper.getAvailable();
268: if ((available > 0L) && (available < Long.MAX_VALUE)) {
269: response.setDateHeader("Retry-After", available);
270: response.sendError(
271: HttpServletResponse.SC_SERVICE_UNAVAILABLE,
272: sm.getString("standardWrapper.isUnavailable",
273: wrapper.getName()));
274: } else if (available == Long.MAX_VALUE) {
275: response.sendError(HttpServletResponse.SC_NOT_FOUND, sm
276: .getString("standardWrapper.notFound", wrapper
277: .getName()));
278: }
279: // Do not save exception in 'throwable', because we
280: // do not want to do exception(request, response, e) processing
281: } catch (ServletException e) {
282: request.removeAttribute(Globals.JSP_FILE_ATTR);
283: Throwable rootCause = StandardWrapper.getRootCause(e);
284: if (!(rootCause instanceof ClientAbortException)) {
285: container.getLogger().error(
286: sm.getString(
287: "standardWrapper.serviceException",
288: wrapper.getName()), rootCause);
289: }
290: throwable = e;
291: exception(request, response, e);
292: } catch (Throwable e) {
293: request.removeAttribute(Globals.JSP_FILE_ATTR);
294: container.getLogger().error(
295: sm.getString("standardWrapper.serviceException",
296: wrapper.getName()), e);
297: throwable = e;
298: exception(request, response, e);
299: }
300:
301: // Release the filter chain (if any) for this request
302: if (filterChain != null) {
303: if (request.isComet()) {
304: // If this is a Comet request, then the same chain will be used for the
305: // processing of all subsequent events.
306: filterChain.reuse();
307: } else {
308: filterChain.release();
309: }
310: }
311:
312: // Deallocate the allocated servlet instance
313: try {
314: if (servlet != null) {
315: wrapper.deallocate(servlet);
316: }
317: } catch (Throwable e) {
318: container.getLogger().error(
319: sm.getString("standardWrapper.deallocateException",
320: wrapper.getName()), e);
321: if (throwable == null) {
322: throwable = e;
323: exception(request, response, e);
324: }
325: }
326:
327: // If this servlet has been marked permanently unavailable,
328: // unload it and release this instance
329: try {
330: if ((servlet != null)
331: && (wrapper.getAvailable() == Long.MAX_VALUE)) {
332: wrapper.unload();
333: }
334: } catch (Throwable e) {
335: container.getLogger().error(
336: sm.getString("standardWrapper.unloadException",
337: wrapper.getName()), e);
338: if (throwable == null) {
339: throwable = e;
340: exception(request, response, e);
341: }
342: }
343: long t2 = System.currentTimeMillis();
344:
345: long time = t2 - t1;
346: processingTime += time;
347: if (time > maxTime)
348: maxTime = time;
349: if (time < minTime)
350: minTime = time;
351:
352: }
353:
354: /**
355: * Process a Comet event. The main differences here are to not use sendError
356: * (the response is committed), to avoid creating a new filter chain
357: * (which would work but be pointless), and a few very minor tweaks.
358: *
359: * @param request The servlet request to be processed
360: * @param response The servlet response to be created
361: *
362: * @exception IOException if an input/output error occurs, or is thrown
363: * by a subsequently invoked Valve, Filter, or Servlet
364: * @exception ServletException if a servlet error occurs, or is thrown
365: * by a subsequently invoked Valve, Filter, or Servlet
366: */
367: public void event(Request request, Response response,
368: CometEvent event) throws IOException, ServletException {
369:
370: // Initialize local variables we may need
371: Throwable throwable = null;
372: // This should be a Request attribute...
373: long t1 = System.currentTimeMillis();
374: // FIXME: Add a flag to count the total amount of events processed ? requestCount++;
375: StandardWrapper wrapper = (StandardWrapper) getContainer();
376: Servlet servlet = null;
377: Context context = (Context) wrapper.getParent();
378:
379: // Check for the application being marked unavailable
380: boolean unavailable = !context.getAvailable()
381: || wrapper.isUnavailable();
382:
383: // Allocate a servlet instance to process this request
384: try {
385: if (!unavailable) {
386: servlet = wrapper.allocate();
387: }
388: } catch (UnavailableException e) {
389: // The response is already committed, so it's not possible to do anything
390: } catch (ServletException e) {
391: container.getLogger().error(
392: sm.getString("standardWrapper.allocateException",
393: wrapper.getName()),
394: StandardWrapper.getRootCause(e));
395: throwable = e;
396: exception(request, response, e);
397: servlet = null;
398: } catch (Throwable e) {
399: container.getLogger().error(
400: sm.getString("standardWrapper.allocateException",
401: wrapper.getName()), e);
402: throwable = e;
403: exception(request, response, e);
404: servlet = null;
405: }
406:
407: MessageBytes requestPathMB = null;
408: if (request != null) {
409: requestPathMB = request.getRequestPathMB();
410: }
411: request.setAttribute(
412: ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
413: ApplicationFilterFactory.REQUEST_INTEGER);
414: request.setAttribute(
415: ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
416: requestPathMB);
417: // Get the current (unchanged) filter chain for this request
418: ApplicationFilterChain filterChain = (ApplicationFilterChain) request
419: .getFilterChain();
420:
421: // Call the filter chain for this request
422: // NOTE: This also calls the servlet's event() method
423: try {
424: String jspFile = wrapper.getJspFile();
425: if (jspFile != null)
426: request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
427: else
428: request.removeAttribute(Globals.JSP_FILE_ATTR);
429: if ((servlet != null) && (filterChain != null)) {
430:
431: // Swallow output if needed
432: if (context.getSwallowOutput()) {
433: try {
434: SystemLogHandler.startCapture();
435: filterChain.doFilterEvent(request.getEvent());
436: } finally {
437: String log = SystemLogHandler.stopCapture();
438: if (log != null && log.length() > 0) {
439: context.getLogger().info(log);
440: }
441: }
442: } else {
443: filterChain.doFilterEvent(request.getEvent());
444: }
445:
446: }
447: request.removeAttribute(Globals.JSP_FILE_ATTR);
448: } catch (ClientAbortException e) {
449: request.removeAttribute(Globals.JSP_FILE_ATTR);
450: throwable = e;
451: exception(request, response, e);
452: } catch (IOException e) {
453: request.removeAttribute(Globals.JSP_FILE_ATTR);
454: container.getLogger().error(
455: sm.getString("standardWrapper.serviceException",
456: wrapper.getName()), e);
457: throwable = e;
458: exception(request, response, e);
459: } catch (UnavailableException e) {
460: request.removeAttribute(Globals.JSP_FILE_ATTR);
461: container.getLogger().error(
462: sm.getString("standardWrapper.serviceException",
463: wrapper.getName()), e);
464: // Do not save exception in 'throwable', because we
465: // do not want to do exception(request, response, e) processing
466: } catch (ServletException e) {
467: request.removeAttribute(Globals.JSP_FILE_ATTR);
468: Throwable rootCause = StandardWrapper.getRootCause(e);
469: if (!(rootCause instanceof ClientAbortException)) {
470: container.getLogger().error(
471: sm.getString(
472: "standardWrapper.serviceException",
473: wrapper.getName()), rootCause);
474: }
475: throwable = e;
476: exception(request, response, e);
477: } catch (Throwable e) {
478: request.removeAttribute(Globals.JSP_FILE_ATTR);
479: container.getLogger().error(
480: sm.getString("standardWrapper.serviceException",
481: wrapper.getName()), e);
482: throwable = e;
483: exception(request, response, e);
484: }
485:
486: // Release the filter chain (if any) for this request
487: if (filterChain != null) {
488: filterChain.reuse();
489: }
490:
491: // Deallocate the allocated servlet instance
492: try {
493: if (servlet != null) {
494: wrapper.deallocate(servlet);
495: }
496: } catch (Throwable e) {
497: container.getLogger().error(
498: sm.getString("standardWrapper.deallocateException",
499: wrapper.getName()), e);
500: if (throwable == null) {
501: throwable = e;
502: exception(request, response, e);
503: }
504: }
505:
506: // If this servlet has been marked permanently unavailable,
507: // unload it and release this instance
508: try {
509: if ((servlet != null)
510: && (wrapper.getAvailable() == Long.MAX_VALUE)) {
511: wrapper.unload();
512: }
513: } catch (Throwable e) {
514: container.getLogger().error(
515: sm.getString("standardWrapper.unloadException",
516: wrapper.getName()), e);
517: if (throwable == null) {
518: throwable = e;
519: exception(request, response, e);
520: }
521: }
522:
523: long t2 = System.currentTimeMillis();
524:
525: long time = t2 - t1;
526: processingTime += time;
527: if (time > maxTime)
528: maxTime = time;
529: if (time < minTime)
530: minTime = time;
531:
532: }
533:
534: // -------------------------------------------------------- Private Methods
535:
536: /**
537: * Handle the specified ServletException encountered while processing
538: * the specified Request to produce the specified Response. Any
539: * exceptions that occur during generation of the exception report are
540: * logged and swallowed.
541: *
542: * @param request The request being processed
543: * @param response The response being generated
544: * @param exception The exception that occurred (which possibly wraps
545: * a root cause exception
546: */
547: private void exception(Request request, Response response,
548: Throwable exception) {
549: request.setAttribute(Globals.EXCEPTION_ATTR, exception);
550: response
551: .setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
552:
553: }
554:
555: public long getProcessingTime() {
556: return processingTime;
557: }
558:
559: public void setProcessingTime(long processingTime) {
560: this .processingTime = processingTime;
561: }
562:
563: public long getMaxTime() {
564: return maxTime;
565: }
566:
567: public void setMaxTime(long maxTime) {
568: this .maxTime = maxTime;
569: }
570:
571: public long getMinTime() {
572: return minTime;
573: }
574:
575: public void setMinTime(long minTime) {
576: this .minTime = minTime;
577: }
578:
579: public int getRequestCount() {
580: return requestCount;
581: }
582:
583: public void setRequestCount(int requestCount) {
584: this .requestCount = requestCount;
585: }
586:
587: public int getErrorCount() {
588: return errorCount;
589: }
590:
591: public void setErrorCount(int errorCount) {
592: this .errorCount = errorCount;
593: }
594:
595: // Don't register in JMX
596:
597: public ObjectName createObjectName(String domain, ObjectName parent)
598: throws MalformedObjectNameException {
599: return null;
600: }
601: }
|