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.ObjectName;
022: import javax.servlet.ServletException;
023:
024: import org.apache.catalina.Contained;
025: import org.apache.catalina.Container;
026: import org.apache.catalina.Lifecycle;
027: import org.apache.catalina.LifecycleException;
028: import org.apache.catalina.LifecycleListener;
029: import org.apache.catalina.Logger;
030: import org.apache.catalina.Pipeline;
031: import org.apache.catalina.Request;
032: import org.apache.catalina.Response;
033: import org.apache.catalina.Valve;
034: import org.apache.catalina.util.LifecycleSupport;
035: import org.apache.catalina.util.StringManager;
036: import org.apache.catalina.valves.ValveBase;
037: import org.apache.commons.logging.Log;
038: import org.apache.commons.logging.LogFactory;
039: import org.apache.commons.modeler.Registry;
040:
041: /**
042: * Standard implementation of a processing <b>Pipeline</b> that will invoke
043: * a series of Valves that have been configured to be called in order. This
044: * implementation can be used for any type of Container.
045: *
046: * <b>IMPLEMENTATION WARNING</b> - This implementation assumes that no
047: * calls to <code>addValve()</code> or <code>removeValve</code> are allowed
048: * while a request is currently being processed. Otherwise, the mechanism
049: * by which per-thread state is maintained will need to be modified.
050: *
051: * @author Craig R. McClanahan
052: */
053:
054: public class StandardPipeline implements Pipeline, Contained, Lifecycle {
055: private static Log log = LogFactory.getLog(StandardPipeline.class);
056:
057: // ----------------------------------------------------------- Constructors
058:
059: /**
060: * Construct a new StandardPipeline instance with no associated Container.
061: */
062: public StandardPipeline() {
063:
064: this (null);
065:
066: }
067:
068: /**
069: * Construct a new StandardPipeline instance that is associated with the
070: * specified Container.
071: *
072: * @param container The container we should be associated with
073: */
074: public StandardPipeline(Container container) {
075:
076: super ();
077: setContainer(container);
078:
079: }
080:
081: // ----------------------------------------------------- Instance Variables
082:
083: /**
084: * The basic Valve (if any) associated with this Pipeline.
085: */
086: protected Valve basic = null;
087:
088: /**
089: * The Container with which this Pipeline is associated.
090: */
091: protected Container container = null;
092:
093: /**
094: * The debugging detail level for this component.
095: */
096: protected int debug = 0;
097:
098: /**
099: * Descriptive information about this implementation.
100: */
101: protected String info = "org.apache.catalina.core.StandardPipeline/1.0";
102:
103: /**
104: * The lifecycle event support for this component.
105: */
106: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
107:
108: /**
109: * The string manager for this package.
110: */
111: protected static StringManager sm = StringManager
112: .getManager(Constants.Package);
113:
114: /**
115: * Has this component been started yet?
116: */
117: protected boolean started = false;
118:
119: /**
120: * The set of Valves (not including the Basic one, if any) associated with
121: * this Pipeline.
122: */
123: protected Valve valves[] = new Valve[0];
124:
125: // --------------------------------------------------------- Public Methods
126:
127: /**
128: * Return descriptive information about this implementation class.
129: */
130: public String getInfo() {
131:
132: return (this .info);
133:
134: }
135:
136: // ------------------------------------------------------ Contained Methods
137:
138: /**
139: * Return the Container with which this Pipeline is associated.
140: */
141: public Container getContainer() {
142:
143: return (this .container);
144:
145: }
146:
147: /**
148: * Set the Container with which this Pipeline is associated.
149: *
150: * @param container The new associated container
151: */
152: public void setContainer(Container container) {
153:
154: this .container = container;
155:
156: }
157:
158: // ------------------------------------------------------ Lifecycle Methods
159:
160: /**
161: * Add a lifecycle event listener to this component.
162: *
163: * @param listener The listener to add
164: */
165: public void addLifecycleListener(LifecycleListener listener) {
166:
167: lifecycle.addLifecycleListener(listener);
168:
169: }
170:
171: /**
172: * Get the lifecycle listeners associated with this lifecycle. If this
173: * Lifecycle has no listeners registered, a zero-length array is returned.
174: */
175: public LifecycleListener[] findLifecycleListeners() {
176:
177: return lifecycle.findLifecycleListeners();
178:
179: }
180:
181: /**
182: * Remove a lifecycle event listener from this component.
183: *
184: * @param listener The listener to remove
185: */
186: public void removeLifecycleListener(LifecycleListener listener) {
187:
188: lifecycle.removeLifecycleListener(listener);
189:
190: }
191:
192: /**
193: * Prepare for active use of the public methods of this Component.
194: *
195: * @exception LifecycleException if this component detects a fatal error
196: * that prevents it from being started
197: */
198: public synchronized void start() throws LifecycleException {
199:
200: // Validate and update our current component state
201: if (started)
202: throw new LifecycleException(sm
203: .getString("standardPipeline.alreadyStarted"));
204:
205: // Notify our interested LifecycleListeners
206: lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
207:
208: started = true;
209:
210: // Start the Valves in our pipeline (including the basic), if any
211: for (int i = 0; i < valves.length; i++) {
212: if (valves[i] instanceof Lifecycle)
213: ((Lifecycle) valves[i]).start();
214: registerValve(valves[i]);
215: }
216: if ((basic != null) && (basic instanceof Lifecycle))
217: ((Lifecycle) basic).start();
218:
219: if (basic != null)
220: registerValve(basic);
221:
222: // Notify our interested LifecycleListeners
223: lifecycle.fireLifecycleEvent(START_EVENT, null);
224:
225: // Notify our interested LifecycleListeners
226: lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
227:
228: }
229:
230: /**
231: * Gracefully shut down active use of the public methods of this Component.
232: *
233: * @exception LifecycleException if this component detects a fatal error
234: * that needs to be reported
235: */
236: public synchronized void stop() throws LifecycleException {
237:
238: // Validate and update our current component state
239: if (!started)
240: throw new LifecycleException(sm
241: .getString("standardPipeline.notStarted"));
242:
243: // Notify our interested LifecycleListeners
244: lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
245:
246: // Notify our interested LifecycleListeners
247: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
248: started = false;
249:
250: // Stop the Valves in our pipeline (including the basic), if any
251: if ((basic != null) && (basic instanceof Lifecycle))
252: ((Lifecycle) basic).stop();
253: if (basic != null) {
254: unregisterValve(basic);
255: }
256: for (int i = 0; i < valves.length; i++) {
257: if (valves[i] instanceof Lifecycle)
258: ((Lifecycle) valves[i]).stop();
259: unregisterValve(valves[i]);
260:
261: }
262:
263: // Notify our interested LifecycleListeners
264: lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
265: }
266:
267: private void registerValve(Valve valve) {
268:
269: if (valve instanceof ValveBase
270: && ((ValveBase) valve).getObjectName() == null) {
271: try {
272:
273: String domain = ((ContainerBase) container).getDomain();
274: if (container instanceof StandardContext) {
275: domain = ((StandardContext) container)
276: .getEngineName();
277: }
278: if (container instanceof StandardWrapper) {
279: Container ctx = ((StandardWrapper) container)
280: .getParent();
281: domain = ((StandardContext) ctx).getEngineName();
282: }
283: ObjectName vname = ((ValveBase) valve)
284: .createObjectName(domain,
285: ((ContainerBase) container)
286: .getJmxName());
287: if (vname != null) {
288: ((ValveBase) valve).setObjectName(vname);
289: Registry.getRegistry(null, null).registerComponent(
290: valve, vname, valve.getClass().getName());
291: ((ValveBase) valve)
292: .setController(((ContainerBase) container)
293: .getJmxName());
294: }
295: } catch (Throwable t) {
296: log.info("Can't register valve " + valve, t);
297: }
298: }
299: }
300:
301: private void unregisterValve(Valve valve) {
302: if (valve instanceof ValveBase) {
303: try {
304: ValveBase vb = (ValveBase) valve;
305: if (vb.getController() != null
306: && vb.getController() == ((ContainerBase) container)
307: .getJmxName()) {
308:
309: ObjectName vname = vb.getObjectName();
310: Registry.getRegistry(null, null).getMBeanServer()
311: .unregisterMBean(vname);
312: ((ValveBase) valve).setObjectName(null);
313: }
314: } catch (Throwable t) {
315: log.info("Can't unregister valve " + valve, t);
316: }
317: }
318: }
319:
320: // ------------------------------------------------------- Pipeline Methods
321:
322: /**
323: * <p>Return the Valve instance that has been distinguished as the basic
324: * Valve for this Pipeline (if any).
325: */
326: public Valve getBasic() {
327:
328: return (this .basic);
329:
330: }
331:
332: /**
333: * <p>Set the Valve instance that has been distinguished as the basic
334: * Valve for this Pipeline (if any). Prioer to setting the basic Valve,
335: * the Valve's <code>setContainer()</code> will be called, if it
336: * implements <code>Contained</code>, with the owning Container as an
337: * argument. The method may throw an <code>IllegalArgumentException</code>
338: * if this Valve chooses not to be associated with this Container, or
339: * <code>IllegalStateException</code> if it is already associated with
340: * a different Container.</p>
341: *
342: * @param valve Valve to be distinguished as the basic Valve
343: */
344: public void setBasic(Valve valve) {
345:
346: // Change components if necessary
347: Valve oldBasic = this .basic;
348: if (oldBasic == valve)
349: return;
350:
351: // Stop the old component if necessary
352: if (oldBasic != null) {
353: if (started && (oldBasic instanceof Lifecycle)) {
354: try {
355: ((Lifecycle) oldBasic).stop();
356: } catch (LifecycleException e) {
357: log.error("StandardPipeline.setBasic: stop", e);
358: }
359: }
360: if (oldBasic instanceof Contained) {
361: try {
362: ((Contained) oldBasic).setContainer(null);
363: } catch (Throwable t) {
364: ;
365: }
366: }
367: }
368:
369: // Start the new component if necessary
370: if (valve == null)
371: return;
372: if (valve instanceof Contained) {
373: ((Contained) valve).setContainer(this .container);
374: }
375: if (valve instanceof Lifecycle) {
376: try {
377: ((Lifecycle) valve).start();
378: } catch (LifecycleException e) {
379: log.error("StandardPipeline.setBasic: start", e);
380: return;
381: }
382: }
383: this .basic = valve;
384:
385: }
386:
387: /**
388: * <p>Add a new Valve to the end of the pipeline associated with this
389: * Container. Prior to adding the Valve, the Valve's
390: * <code>setContainer()</code> method will be called, if it implements
391: * <code>Contained</code>, with the owning Container as an argument.
392: * The method may throw an
393: * <code>IllegalArgumentException</code> if this Valve chooses not to
394: * be associated with this Container, or <code>IllegalStateException</code>
395: * if it is already associated with a different Container.</p>
396: *
397: * @param valve Valve to be added
398: *
399: * @exception IllegalArgumentException if this Container refused to
400: * accept the specified Valve
401: * @exception IllegalArgumentException if the specifie Valve refuses to be
402: * associated with this Container
403: * @exception IllegalStateException if the specified Valve is already
404: * associated with a different Container
405: */
406: public void addValve(Valve valve) {
407:
408: // Validate that we can add this Valve
409: if (valve instanceof Contained)
410: ((Contained) valve).setContainer(this .container);
411:
412: // Start the new component if necessary
413: if (started) {
414: if (valve instanceof Lifecycle) {
415: try {
416: ((Lifecycle) valve).start();
417: } catch (LifecycleException e) {
418: log.error("StandardPipeline.addValve: start: ", e);
419: }
420: }
421: // Register the newly added valve
422: registerValve(valve);
423: }
424:
425: // Add this Valve to the set associated with this Pipeline
426: synchronized (valves) {
427: Valve results[] = new Valve[valves.length + 1];
428: System.arraycopy(valves, 0, results, 0, valves.length);
429: results[valves.length] = valve;
430: valves = results;
431: }
432:
433: }
434:
435: /**
436: * Return the set of Valves in the pipeline associated with this
437: * Container, including the basic Valve (if any). If there are no
438: * such Valves, a zero-length array is returned.
439: */
440: public Valve[] getValves() {
441:
442: if (basic == null)
443: return (valves);
444: synchronized (valves) {
445: Valve results[] = new Valve[valves.length + 1];
446: System.arraycopy(valves, 0, results, 0, valves.length);
447: results[valves.length] = basic;
448: return (results);
449: }
450:
451: }
452:
453: public ObjectName[] getValveObjectNames() {
454: ObjectName oname[] = new ObjectName[valves.length + 1];
455: for (int i = 0; i < valves.length; i++) {
456: if (valves[i] instanceof ValveBase)
457: oname[i] = ((ValveBase) valves[i]).getObjectName();
458: }
459: if (basic instanceof ValveBase)
460: oname[valves.length] = ((ValveBase) basic).getObjectName();
461: return oname;
462: }
463:
464: /**
465: * Cause the specified request and response to be processed by the Valves
466: * associated with this pipeline, until one of these valves causes the
467: * response to be created and returned. The implementation must ensure
468: * that multiple simultaneous requests (on different threads) can be
469: * processed through the same Pipeline without interfering with each
470: * other's control flow.
471: *
472: * @param request The servlet request we are processing
473: * @param response The servlet response we are creating
474: *
475: * @exception IOException if an input/output error occurs
476: * @exception ServletException if a servlet exception is thrown
477: */
478: public void invoke(Request request, Response response)
479: throws IOException, ServletException {
480:
481: // Invoke the first Valve in this pipeline for this request
482: //(new StandardPipelineValveContext()).invokeNext(request, response);
483:
484: StandardValveContext valveContext = (StandardValveContext) request
485: .getValveContext();
486: if (valveContext == null) {
487: valveContext = new StandardValveContext();
488: request.setValveContext(valveContext);
489: }
490:
491: valveContext.set(basic, valves);
492: valveContext.invokeNext(request, response);
493: valveContext.set(null, null);
494:
495: }
496:
497: /**
498: * Remove the specified Valve from the pipeline associated with this
499: * Container, if it is found; otherwise, do nothing. If the Valve is
500: * found and removed, the Valve's <code>setContainer(null)</code> method
501: * will be called if it implements <code>Contained</code>.
502: *
503: * @param valve Valve to be removed
504: */
505: public void removeValve(Valve valve) {
506:
507: synchronized (valves) {
508:
509: // Locate this Valve in our list
510: int j = -1;
511: for (int i = 0; i < valves.length; i++) {
512: if (valve == valves[i]) {
513: j = i;
514: break;
515: }
516: }
517: if (j < 0)
518: return;
519:
520: // Remove this valve from our list
521: Valve results[] = new Valve[valves.length - 1];
522: int n = 0;
523: for (int i = 0; i < valves.length; i++) {
524: if (i == j)
525: continue;
526: results[n++] = valves[i];
527: }
528: valves = results;
529: try {
530: if (valve instanceof Contained)
531: ((Contained) valve).setContainer(null);
532: } catch (Throwable t) {
533: ;
534: }
535:
536: }
537:
538: // Stop this valve if necessary
539: if (started) {
540: if (valve instanceof Lifecycle) {
541: try {
542: ((Lifecycle) valve).stop();
543: } catch (LifecycleException e) {
544: log
545: .error(
546: "StandardPipeline.removeValve: stop: ",
547: e);
548: }
549: }
550: // Unregister the removed valave
551: unregisterValve(valve);
552: }
553:
554: }
555:
556: // ------------------------------------------------------ Protected Methods
557:
558: /**
559: * Log a message on the Logger associated with our Container (if any).
560: *
561: * @param message Message to be logged
562: */
563: protected void log(String message) {
564:
565: Logger logger = null;
566: if (container != null)
567: logger = container.getLogger();
568: if (logger != null)
569: logger.log("StandardPipeline[" + container.getName()
570: + "]: " + message);
571: else
572: System.out.println("StandardPipeline["
573: + container.getName() + "]: " + message);
574:
575: }
576:
577: /**
578: * Log a message on the Logger associated with our Container (if any).
579: *
580: * @param message Message to be logged
581: * @param throwable Associated exception
582: */
583: protected void log(String message, Throwable throwable) {
584:
585: Logger logger = null;
586: if (container != null)
587: logger = container.getLogger();
588: if (logger != null)
589: logger.log("StandardPipeline[" + container.getName()
590: + "]: " + message, throwable);
591: else {
592: System.out.println("StandardPipeline["
593: + container.getName() + "]: " + message);
594: throwable.printStackTrace(System.out);
595: }
596:
597: }
598:
599: }
|