1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.core;
20
21
22 import java.util.ArrayList;
23
24 import javax.management.ObjectName;
25
26 import org.apache.catalina.Contained;
27 import org.apache.catalina.Container;
28 import org.apache.catalina.Lifecycle;
29 import org.apache.catalina.LifecycleException;
30 import org.apache.catalina.LifecycleListener;
31 import org.apache.catalina.Pipeline;
32 import org.apache.catalina.Valve;
33 import org.apache.catalina.util.LifecycleSupport;
34 import org.apache.catalina.util.StringManager;
35 import org.apache.catalina.valves.ValveBase;
36 import org.apache.juli.logging.Log;
37 import org.apache.juli.logging.LogFactory;
38 import org.apache.tomcat.util.modeler.Registry;
39
40
41 /**
42 * Standard implementation of a processing <b>Pipeline</b> that will invoke
43 * a series of Valves that have been configured to be called in order. This
44 * implementation can be used for any type of Container.
45 *
46 * <b>IMPLEMENTATION WARNING</b> - This implementation assumes that no
47 * calls to <code>addValve()</code> or <code>removeValve</code> are allowed
48 * while a request is currently being processed. Otherwise, the mechanism
49 * by which per-thread state is maintained will need to be modified.
50 *
51 * @author Craig R. McClanahan
52 */
53
54 public class StandardPipeline
55 implements Pipeline, Contained, Lifecycle
56 {
57
58 private static Log log = LogFactory.getLog(StandardPipeline.class);
59
60 // ----------------------------------------------------------- Constructors
61
62
63 /**
64 * Construct a new StandardPipeline instance with no associated Container.
65 */
66 public StandardPipeline() {
67
68 this(null);
69
70 }
71
72
73 /**
74 * Construct a new StandardPipeline instance that is associated with the
75 * specified Container.
76 *
77 * @param container The container we should be associated with
78 */
79 public StandardPipeline(Container container) {
80
81 super();
82 setContainer(container);
83
84 }
85
86
87 // ----------------------------------------------------- Instance Variables
88
89
90 /**
91 * The basic Valve (if any) associated with this Pipeline.
92 */
93 protected Valve basic = null;
94
95
96 /**
97 * The Container with which this Pipeline is associated.
98 */
99 protected Container container = null;
100
101
102 /**
103 * Descriptive information about this implementation.
104 */
105 protected String info = "org.apache.catalina.core.StandardPipeline/1.0";
106
107
108 /**
109 * The lifecycle event support for this component.
110 */
111 protected LifecycleSupport lifecycle = new LifecycleSupport(this);
112
113
114 /**
115 * The string manager for this package.
116 */
117 protected static StringManager sm =
118 StringManager.getManager(Constants.Package);
119
120
121 /**
122 * Has this component been started yet?
123 */
124 protected boolean started = false;
125
126
127 /**
128 * The first valve associated with this Pipeline.
129 */
130 protected Valve first = null;
131
132
133 // --------------------------------------------------------- Public Methods
134
135
136 /**
137 * Return descriptive information about this implementation class.
138 */
139 public String getInfo() {
140
141 return (this.info);
142
143 }
144
145
146 // ------------------------------------------------------ Contained Methods
147
148
149 /**
150 * Return the Container with which this Pipeline is associated.
151 */
152 public Container getContainer() {
153
154 return (this.container);
155
156 }
157
158
159 /**
160 * Set the Container with which this Pipeline is associated.
161 *
162 * @param container The new associated container
163 */
164 public void setContainer(Container container) {
165
166 this.container = container;
167
168 }
169
170
171 // ------------------------------------------------------ Lifecycle Methods
172
173
174 /**
175 * Add a lifecycle event listener to this component.
176 *
177 * @param listener The listener to add
178 */
179 public void addLifecycleListener(LifecycleListener listener) {
180
181 lifecycle.addLifecycleListener(listener);
182
183 }
184
185
186 /**
187 * Get the lifecycle listeners associated with this lifecycle. If this
188 * Lifecycle has no listeners registered, a zero-length array is returned.
189 */
190 public LifecycleListener[] findLifecycleListeners() {
191
192 return lifecycle.findLifecycleListeners();
193
194 }
195
196
197 /**
198 * Remove a lifecycle event listener from this component.
199 *
200 * @param listener The listener to remove
201 */
202 public void removeLifecycleListener(LifecycleListener listener) {
203
204 lifecycle.removeLifecycleListener(listener);
205
206 }
207
208 /**
209 * Prepare for active use of the public methods of this Component.
210 *
211 * @exception LifecycleException if this component detects a fatal error
212 * that prevents it from being started
213 */
214 public synchronized void start() throws LifecycleException {
215
216 // Validate and update our current component state
217 if (started)
218 throw new LifecycleException
219 (sm.getString("standardPipeline.alreadyStarted"));
220
221 // Notify our interested LifecycleListeners
222 lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
223
224 started = true;
225
226 // Start the Valves in our pipeline (including the basic), if any
227 Valve current = first;
228 if (current == null) {
229 current = basic;
230 }
231 while (current != null) {
232 if (current instanceof Lifecycle)
233 ((Lifecycle) current).start();
234 registerValve(current);
235 current = current.getNext();
236 }
237
238 // Notify our interested LifecycleListeners
239 lifecycle.fireLifecycleEvent(START_EVENT, null);
240
241 // Notify our interested LifecycleListeners
242 lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
243
244 }
245
246
247 /**
248 * Gracefully shut down active use of the public methods of this Component.
249 *
250 * @exception LifecycleException if this component detects a fatal error
251 * that needs to be reported
252 */
253 public synchronized void stop() throws LifecycleException {
254
255 // Validate and update our current component state
256 if (!started)
257 throw new LifecycleException
258 (sm.getString("standardPipeline.notStarted"));
259
260 // Notify our interested LifecycleListeners
261 lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
262
263 // Notify our interested LifecycleListeners
264 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
265 started = false;
266
267 // Stop the Valves in our pipeline (including the basic), if any
268 Valve current = first;
269 if (current == null) {
270 current = basic;
271 }
272 while (current != null) {
273 if (current instanceof Lifecycle)
274 ((Lifecycle) current).stop();
275 unregisterValve(current);
276 current = current.getNext();
277 }
278
279 // Notify our interested LifecycleListeners
280 lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
281 }
282
283 private void registerValve(Valve valve) {
284
285 if( valve instanceof ValveBase &&
286 ((ValveBase)valve).getObjectName()==null ) {
287 try {
288
289 String domain=((ContainerBase)container).getDomain();
290 if( container instanceof StandardContext ) {
291 domain=((StandardContext)container).getEngineName();
292 }
293 if( container instanceof StandardWrapper) {
294 Container ctx=((StandardWrapper)container).getParent();
295 domain=((StandardContext)ctx).getEngineName();
296 }
297 ObjectName vname=((ValveBase)valve).createObjectName(
298 domain,
299 ((ContainerBase)container).getJmxName());
300 if( vname != null ) {
301 ((ValveBase)valve).setObjectName(vname);
302 Registry.getRegistry(null, null).registerComponent
303 (valve, vname, valve.getClass().getName());
304 ((ValveBase)valve).setController
305 (((ContainerBase)container).getJmxName());
306 }
307 } catch( Throwable t ) {
308 log.info( "Can't register valve " + valve , t );
309 }
310 }
311 }
312
313 private void unregisterValve(Valve valve) {
314 if( valve instanceof ValveBase ) {
315 try {
316 ValveBase vb=(ValveBase)valve;
317 if( vb.getController()!=null &&
318 vb.getController() ==
319 ((ContainerBase)container).getJmxName() ) {
320
321 ObjectName vname=vb.getObjectName();
322 Registry.getRegistry(null, null).getMBeanServer()
323 .unregisterMBean(vname);
324 ((ValveBase)valve).setObjectName(null);
325 }
326 } catch( Throwable t ) {
327 log.info( "Can't unregister valve " + valve , t );
328 }
329 }
330 }
331
332 // ------------------------------------------------------- Pipeline Methods
333
334
335 /**
336 * <p>Return the Valve instance that has been distinguished as the basic
337 * Valve for this Pipeline (if any).
338 */
339 public Valve getBasic() {
340
341 return (this.basic);
342
343 }
344
345
346 /**
347 * <p>Set the Valve instance that has been distinguished as the basic
348 * Valve for this Pipeline (if any). Prioer to setting the basic Valve,
349 * the Valve's <code>setContainer()</code> will be called, if it
350 * implements <code>Contained</code>, with the owning Container as an
351 * argument. The method may throw an <code>IllegalArgumentException</code>
352 * if this Valve chooses not to be associated with this Container, or
353 * <code>IllegalStateException</code> if it is already associated with
354 * a different Container.</p>
355 *
356 * @param valve Valve to be distinguished as the basic Valve
357 */
358 public void setBasic(Valve valve) {
359
360 // Change components if necessary
361 Valve oldBasic = this.basic;
362 if (oldBasic == valve)
363 return;
364
365 // Stop the old component if necessary
366 if (oldBasic != null) {
367 if (started && (oldBasic instanceof Lifecycle)) {
368 try {
369 ((Lifecycle) oldBasic).stop();
370 } catch (LifecycleException e) {
371 log.error("StandardPipeline.setBasic: stop", e);
372 }
373 }
374 if (oldBasic instanceof Contained) {
375 try {
376 ((Contained) oldBasic).setContainer(null);
377 } catch (Throwable t) {
378 ;
379 }
380 }
381 }
382
383 // Start the new component if necessary
384 if (valve == null)
385 return;
386 if (valve instanceof Contained) {
387 ((Contained) valve).setContainer(this.container);
388 }
389 if (valve instanceof Lifecycle) {
390 try {
391 ((Lifecycle) valve).start();
392 } catch (LifecycleException e) {
393 log.error("StandardPipeline.setBasic: start", e);
394 return;
395 }
396 }
397
398 // Update the pipeline
399 Valve current = first;
400 while (current != null) {
401 if (current.getNext() == oldBasic) {
402 current.setNext(valve);
403 break;
404 }
405 current = current.getNext();
406 }
407
408 this.basic = valve;
409
410 }
411
412
413 /**
414 * <p>Add a new Valve to the end of the pipeline associated with this
415 * Container. Prior to adding the Valve, the Valve's
416 * <code>setContainer()</code> method will be called, if it implements
417 * <code>Contained</code>, with the owning Container as an argument.
418 * The method may throw an
419 * <code>IllegalArgumentException</code> if this Valve chooses not to
420 * be associated with this Container, or <code>IllegalStateException</code>
421 * if it is already associated with a different Container.</p>
422 *
423 * @param valve Valve to be added
424 *
425 * @exception IllegalArgumentException if this Container refused to
426 * accept the specified Valve
427 * @exception IllegalArgumentException if the specifie Valve refuses to be
428 * associated with this Container
429 * @exception IllegalStateException if the specified Valve is already
430 * associated with a different Container
431 */
432 public void addValve(Valve valve) {
433
434 // Validate that we can add this Valve
435 if (valve instanceof Contained)
436 ((Contained) valve).setContainer(this.container);
437
438 // Start the new component if necessary
439 if (started) {
440 if (valve instanceof Lifecycle) {
441 try {
442 ((Lifecycle) valve).start();
443 } catch (LifecycleException e) {
444 log.error("StandardPipeline.addValve: start: ", e);
445 }
446 }
447 // Register the newly added valve
448 registerValve(valve);
449 }
450
451 // Add this Valve to the set associated with this Pipeline
452 if (first == null) {
453 first = valve;
454 valve.setNext(basic);
455 } else {
456 Valve current = first;
457 while (current != null) {
458 if (current.getNext() == basic) {
459 current.setNext(valve);
460 valve.setNext(basic);
461 break;
462 }
463 current = current.getNext();
464 }
465 }
466
467 }
468
469
470 /**
471 * Return the set of Valves in the pipeline associated with this
472 * Container, including the basic Valve (if any). If there are no
473 * such Valves, a zero-length array is returned.
474 */
475 public Valve[] getValves() {
476
477 ArrayList valveList = new ArrayList();
478 Valve current = first;
479 if (current == null) {
480 current = basic;
481 }
482 while (current != null) {
483 valveList.add(current);
484 current = current.getNext();
485 }
486
487 return ((Valve[]) valveList.toArray(new Valve[0]));
488
489 }
490
491 public ObjectName[] getValveObjectNames() {
492
493 ArrayList valveList = new ArrayList();
494 Valve current = first;
495 if (current == null) {
496 current = basic;
497 }
498 while (current != null) {
499 if (current instanceof ValveBase) {
500 valveList.add(((ValveBase) current).getObjectName());
501 }
502 current = current.getNext();
503 }
504
505 return ((ObjectName[]) valveList.toArray(new ObjectName[0]));
506
507 }
508
509 /**
510 * Remove the specified Valve from the pipeline associated with this
511 * Container, if it is found; otherwise, do nothing. If the Valve is
512 * found and removed, the Valve's <code>setContainer(null)</code> method
513 * will be called if it implements <code>Contained</code>.
514 *
515 * @param valve Valve to be removed
516 */
517 public void removeValve(Valve valve) {
518
519 Valve current;
520 if(first == valve) {
521 first = first.getNext();
522 current = null;
523 } else {
524 current = first;
525 }
526 while (current != null) {
527 if (current.getNext() == valve) {
528 current.setNext(valve.getNext());
529 break;
530 }
531 current = current.getNext();
532 }
533
534 if (first == basic) first = null;
535
536 if (valve instanceof Contained)
537 ((Contained) valve).setContainer(null);
538
539 // Stop this valve if necessary
540 if (started) {
541 if (valve instanceof Lifecycle) {
542 try {
543 ((Lifecycle) valve).stop();
544 } catch (LifecycleException e) {
545 log.error("StandardPipeline.removeValve: stop: ", e);
546 }
547 }
548 // Unregister the removed valave
549 unregisterValve(valve);
550 }
551
552 }
553
554
555 public Valve getFirst() {
556 if (first != null) {
557 return first;
558 } else {
559 return basic;
560 }
561 }
562
563
564 }