001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardPipeline.java,v 1.8 2002/06/09 02:19:42 remm Exp $
003: * $Revision: 1.8 $
004: * $Date: 2002/06/09 02:19:42 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999-2001 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.core;
065:
066: import java.io.IOException;
067: import javax.servlet.ServletException;
068: import org.apache.catalina.Contained;
069: import org.apache.catalina.Container;
070: import org.apache.catalina.Lifecycle;
071: import org.apache.catalina.LifecycleEvent;
072: import org.apache.catalina.LifecycleException;
073: import org.apache.catalina.LifecycleListener;
074: import org.apache.catalina.Logger;
075: import org.apache.catalina.Pipeline;
076: import org.apache.catalina.Request;
077: import org.apache.catalina.Response;
078: import org.apache.catalina.Valve;
079: import org.apache.catalina.ValveContext;
080: import org.apache.catalina.util.LifecycleSupport;
081: import org.apache.catalina.util.StringManager;
082:
083: /**
084: * Standard implementation of a processing <b>Pipeline</b> that will invoke
085: * a series of Valves that have been configured to be called in order. This
086: * implementation can be used for any type of Container.
087: *
088: * <b>IMPLEMENTATION WARNING</b> - This implementation assumes that no
089: * calls to <code>addValve()</code> or <code>removeValve</code> are allowed
090: * while a request is currently being processed. Otherwise, the mechanism
091: * by which per-thread state is maintained will need to be modified.
092: *
093: * @author Craig R. McClanahan
094: */
095:
096: public class StandardPipeline implements Pipeline, Contained, Lifecycle {
097:
098: // ----------------------------------------------------------- Constructors
099:
100: /**
101: * Construct a new StandardPipeline instance with no associated Container.
102: */
103: public StandardPipeline() {
104:
105: this (null);
106:
107: }
108:
109: /**
110: * Construct a new StandardPipeline instance that is associated with the
111: * specified Container.
112: *
113: * @param container The container we should be associated with
114: */
115: public StandardPipeline(Container container) {
116:
117: super ();
118: setContainer(container);
119:
120: }
121:
122: // ----------------------------------------------------- Instance Variables
123:
124: /**
125: * The basic Valve (if any) associated with this Pipeline.
126: */
127: protected Valve basic = null;
128:
129: /**
130: * The Container with which this Pipeline is associated.
131: */
132: protected Container container = null;
133:
134: /**
135: * The debugging detail level for this component.
136: */
137: protected int debug = 0;
138:
139: /**
140: * Descriptive information about this implementation.
141: */
142: protected String info = "org.apache.catalina.core.StandardPipeline/1.0";
143:
144: /**
145: * The lifecycle event support for this component.
146: */
147: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
148:
149: /**
150: * The string manager for this package.
151: */
152: protected static StringManager sm = StringManager
153: .getManager(Constants.Package);
154:
155: /**
156: * Has this component been started yet?
157: */
158: protected boolean started = false;
159:
160: /**
161: * The set of Valves (not including the Basic one, if any) associated with
162: * this Pipeline.
163: */
164: protected Valve valves[] = new Valve[0];
165:
166: // --------------------------------------------------------- Public Methods
167:
168: /**
169: * Return descriptive information about this implementation class.
170: */
171: public String getInfo() {
172:
173: return (this .info);
174:
175: }
176:
177: // ------------------------------------------------------ Contained Methods
178:
179: /**
180: * Return the Container with which this Pipeline is associated.
181: */
182: public Container getContainer() {
183:
184: return (this .container);
185:
186: }
187:
188: /**
189: * Set the Container with which this Pipeline is associated.
190: *
191: * @param container The new associated container
192: */
193: public void setContainer(Container container) {
194:
195: this .container = container;
196:
197: }
198:
199: // ------------------------------------------------------ Lifecycle Methods
200:
201: /**
202: * Add a lifecycle event listener to this component.
203: *
204: * @param listener The listener to add
205: */
206: public void addLifecycleListener(LifecycleListener listener) {
207:
208: lifecycle.addLifecycleListener(listener);
209:
210: }
211:
212: /**
213: * Get the lifecycle listeners associated with this lifecycle. If this
214: * Lifecycle has no listeners registered, a zero-length array is returned.
215: */
216: public LifecycleListener[] findLifecycleListeners() {
217:
218: return lifecycle.findLifecycleListeners();
219:
220: }
221:
222: /**
223: * Remove a lifecycle event listener from this component.
224: *
225: * @param listener The listener to remove
226: */
227: public void removeLifecycleListener(LifecycleListener listener) {
228:
229: lifecycle.removeLifecycleListener(listener);
230:
231: }
232:
233: /**
234: * Prepare for active use of the public methods of this Component.
235: *
236: * @exception LifecycleException if this component detects a fatal error
237: * that prevents it from being started
238: */
239: public synchronized void start() throws LifecycleException {
240:
241: // Validate and update our current component state
242: if (started)
243: throw new LifecycleException(sm
244: .getString("standardPipeline.alreadyStarted"));
245:
246: // Notify our interested LifecycleListeners
247: lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
248:
249: started = true;
250:
251: // Start the Valves in our pipeline (including the basic), if any
252: for (int i = 0; i < valves.length; i++) {
253: if (valves[i] instanceof Lifecycle)
254: ((Lifecycle) valves[i]).start();
255: }
256: if ((basic != null) && (basic instanceof Lifecycle))
257: ((Lifecycle) basic).start();
258:
259: // Notify our interested LifecycleListeners
260: lifecycle.fireLifecycleEvent(START_EVENT, null);
261:
262: // Notify our interested LifecycleListeners
263: lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
264:
265: }
266:
267: /**
268: * Gracefully shut down active use of the public methods of this Component.
269: *
270: * @exception LifecycleException if this component detects a fatal error
271: * that needs to be reported
272: */
273: public synchronized void stop() throws LifecycleException {
274:
275: // Validate and update our current component state
276: if (!started)
277: throw new LifecycleException(sm
278: .getString("standardPipeline.notStarted"));
279:
280: // Notify our interested LifecycleListeners
281: lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
282:
283: // Notify our interested LifecycleListeners
284: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
285: started = false;
286:
287: // Stop the Valves in our pipeline (including the basic), if any
288: if ((basic != null) && (basic instanceof Lifecycle))
289: ((Lifecycle) basic).stop();
290: for (int i = 0; i < valves.length; i++) {
291: if (valves[i] instanceof Lifecycle)
292: ((Lifecycle) valves[i]).stop();
293: }
294:
295: // Notify our interested LifecycleListeners
296: lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
297:
298: }
299:
300: // ------------------------------------------------------- Pipeline Methods
301:
302: /**
303: * <p>Return the Valve instance that has been distinguished as the basic
304: * Valve for this Pipeline (if any).
305: */
306: public Valve getBasic() {
307:
308: return (this .basic);
309:
310: }
311:
312: /**
313: * <p>Set the Valve instance that has been distinguished as the basic
314: * Valve for this Pipeline (if any). Prioer to setting the basic Valve,
315: * the Valve's <code>setContainer()</code> will be called, if it
316: * implements <code>Contained</code>, with the owning Container as an
317: * argument. The method may throw an <code>IllegalArgumentException</code>
318: * if this Valve chooses not to be associated with this Container, or
319: * <code>IllegalStateException</code> if it is already associated with
320: * a different Container.</p>
321: *
322: * @param valve Valve to be distinguished as the basic Valve
323: */
324: public void setBasic(Valve valve) {
325:
326: // Change components if necessary
327: Valve oldBasic = this .basic;
328: if (oldBasic == valve)
329: return;
330:
331: // Stop the old component if necessary
332: if (oldBasic != null) {
333: if (started && (oldBasic instanceof Lifecycle)) {
334: try {
335: ((Lifecycle) oldBasic).stop();
336: } catch (LifecycleException e) {
337: log("StandardPipeline.setBasic: stop", e);
338: }
339: }
340: if (oldBasic instanceof Contained) {
341: try {
342: ((Contained) oldBasic).setContainer(null);
343: } catch (Throwable t) {
344: ;
345: }
346: }
347: }
348:
349: // Start the new component if necessary
350: if (valve == null)
351: return;
352: if (valve instanceof Contained) {
353: ((Contained) valve).setContainer(this .container);
354: }
355: if (valve instanceof Lifecycle) {
356: try {
357: ((Lifecycle) valve).start();
358: } catch (LifecycleException e) {
359: log("StandardPipeline.setBasic: start", e);
360: return;
361: }
362: }
363: this .basic = valve;
364:
365: }
366:
367: /**
368: * <p>Add a new Valve to the end of the pipeline associated with this
369: * Container. Prior to adding the Valve, the Valve's
370: * <code>setContainer()</code> method will be called, if it implements
371: * <code>Contained</code>, with the owning Container as an argument.
372: * The method may throw an
373: * <code>IllegalArgumentException</code> if this Valve chooses not to
374: * be associated with this Container, or <code>IllegalStateException</code>
375: * if it is already associated with a different Container.</p>
376: *
377: * @param valve Valve to be added
378: *
379: * @exception IllegalArgumentException if this Container refused to
380: * accept the specified Valve
381: * @exception IllegalArgumentException if the specifie Valve refuses to be
382: * associated with this Container
383: * @exception IllegalStateException if the specified Valve is already
384: * associated with a different Container
385: */
386: public void addValve(Valve valve) {
387:
388: // Validate that we can add this Valve
389: if (valve instanceof Contained)
390: ((Contained) valve).setContainer(this .container);
391:
392: // Start the new component if necessary
393: if (started && (valve instanceof Lifecycle)) {
394: try {
395: ((Lifecycle) valve).start();
396: } catch (LifecycleException e) {
397: log("StandardPipeline.addValve: start: ", e);
398: }
399: }
400:
401: // Add this Valve to the set associated with this Pipeline
402: synchronized (valves) {
403: Valve results[] = new Valve[valves.length + 1];
404: System.arraycopy(valves, 0, results, 0, valves.length);
405: results[valves.length] = valve;
406: valves = results;
407: }
408:
409: }
410:
411: /**
412: * Return the set of Valves in the pipeline associated with this
413: * Container, including the basic Valve (if any). If there are no
414: * such Valves, a zero-length array is returned.
415: */
416: public Valve[] getValves() {
417:
418: if (basic == null)
419: return (valves);
420: synchronized (valves) {
421: Valve results[] = new Valve[valves.length + 1];
422: System.arraycopy(valves, 0, results, 0, valves.length);
423: results[valves.length] = basic;
424: return (results);
425: }
426:
427: }
428:
429: /**
430: * Cause the specified request and response to be processed by the Valves
431: * associated with this pipeline, until one of these valves causes the
432: * response to be created and returned. The implementation must ensure
433: * that multiple simultaneous requests (on different threads) can be
434: * processed through the same Pipeline without interfering with each
435: * other's control flow.
436: *
437: * @param request The servlet request we are processing
438: * @param response The servlet response we are creating
439: *
440: * @exception IOException if an input/output error occurs
441: * @exception ServletException if a servlet exception is thrown
442: */
443: public void invoke(Request request, Response response)
444: throws IOException, ServletException {
445:
446: // Invoke the first Valve in this pipeline for this request
447: (new StandardPipelineValveContext()).invokeNext(request,
448: response);
449:
450: }
451:
452: /**
453: * Remove the specified Valve from the pipeline associated with this
454: * Container, if it is found; otherwise, do nothing. If the Valve is
455: * found and removed, the Valve's <code>setContainer(null)</code> method
456: * will be called if it implements <code>Contained</code>.
457: *
458: * @param valve Valve to be removed
459: */
460: public void removeValve(Valve valve) {
461:
462: synchronized (valves) {
463:
464: // Locate this Valve in our list
465: int j = -1;
466: for (int i = 0; i < valves.length; i++) {
467: if (valve == valves[i]) {
468: j = i;
469: break;
470: }
471: }
472: if (j < 0)
473: return;
474:
475: // Remove this valve from our list
476: Valve results[] = new Valve[valves.length - 1];
477: int n = 0;
478: for (int i = 0; i < valves.length; i++) {
479: if (i == j)
480: continue;
481: results[n++] = valves[i];
482: }
483: valves = results;
484: try {
485: if (valve instanceof Contained)
486: ((Contained) valve).setContainer(null);
487: } catch (Throwable t) {
488: ;
489: }
490:
491: }
492:
493: // Stop this valve if necessary
494: if (started && (valve instanceof Lifecycle)) {
495: try {
496: ((Lifecycle) valve).stop();
497: } catch (LifecycleException e) {
498: log("StandardPipeline.removeValve: stop: ", e);
499: }
500: }
501:
502: }
503:
504: // ------------------------------------------------------ Protected Methods
505:
506: /**
507: * Log a message on the Logger associated with our Container (if any).
508: *
509: * @param message Message to be logged
510: */
511: protected void log(String message) {
512:
513: Logger logger = null;
514: if (container != null)
515: logger = container.getLogger();
516: if (logger != null)
517: logger.log("StandardPipeline[" + container.getName()
518: + "]: " + message);
519: else
520: System.out.println("StandardPipeline["
521: + container.getName() + "]: " + message);
522:
523: }
524:
525: /**
526: * Log a message on the Logger associated with our Container (if any).
527: *
528: * @param message Message to be logged
529: * @param throwable Associated exception
530: */
531: protected void log(String message, Throwable throwable) {
532:
533: Logger logger = null;
534: if (container != null)
535: logger = container.getLogger();
536: if (logger != null)
537: logger.log("StandardPipeline[" + container.getName()
538: + "]: " + message, throwable);
539: else {
540: System.out.println("StandardPipeline["
541: + container.getName() + "]: " + message);
542: throwable.printStackTrace(System.out);
543: }
544:
545: }
546:
547: // ------------------------------- StandardPipelineValveContext Inner Class
548:
549: protected class StandardPipelineValveContext implements
550: ValveContext {
551:
552: // ------------------------------------------------- Instance Variables
553:
554: protected int stage = 0;
555:
556: // --------------------------------------------------------- Properties
557:
558: /**
559: * Return descriptive information about this ValveContext
560: * implementation.
561: */
562: public String getInfo() {
563: return info;
564: }
565:
566: // ----------------------------------------------------- Public Methods
567:
568: /**
569: * Cause the <code>invoke()</code> method of the next Valve that is
570: * part of the Pipeline currently being processed (if any) to be
571: * executed, passing on the specified request and response objects
572: * plus this <code>ValveContext</code> instance. Exceptions thrown by
573: * a subsequently executed Valve (or a Filter or Servlet at the
574: * application level) will be passed on to our caller.
575: *
576: * If there are no more Valves to be executed, an appropriate
577: * ServletException will be thrown by this ValveContext.
578: *
579: * @param request The request currently being processed
580: * @param response The response currently being created
581: *
582: * @exception IOException if thrown by a subsequent Valve, Filter, or
583: * Servlet
584: * @exception ServletException if thrown by a subsequent Valve, Filter,
585: * or Servlet
586: * @exception ServletException if there are no further Valves
587: * configured in the Pipeline currently being processed
588: */
589: public void invokeNext(Request request, Response response)
590: throws IOException, ServletException {
591:
592: int subscript = stage;
593: stage = stage + 1;
594:
595: // Invoke the requested Valve for the current request thread
596: if (subscript < valves.length) {
597: valves[subscript].invoke(request, response, this );
598: } else if ((subscript == valves.length) && (basic != null)) {
599: basic.invoke(request, response, this );
600: } else {
601: throw new ServletException(sm
602: .getString("standardPipeline.noValve"));
603: }
604:
605: }
606:
607: }
608:
609: }
|