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