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 package org.apache.catalina.core;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Set;
22
23 import javax.management.ObjectName;
24
25 import org.apache.catalina.Contained;
26 import org.apache.catalina.Container;
27 import org.apache.catalina.JmxEnabled;
28 import org.apache.catalina.Lifecycle;
29 import org.apache.catalina.LifecycleException;
30 import org.apache.catalina.LifecycleState;
31 import org.apache.catalina.Pipeline;
32 import org.apache.catalina.Valve;
33 import org.apache.catalina.util.LifecycleBase;
34 import org.apache.catalina.util.ToStringUtil;
35 import org.apache.juli.logging.Log;
36 import org.apache.juli.logging.LogFactory;
37 import org.apache.tomcat.util.ExceptionUtils;
38 import org.apache.tomcat.util.res.StringManager;
39
40 /**
41  * Standard implementation of a processing <b>Pipeline</b> that will invoke
42  * a series of Valves that have been configured to be called in order.  This
43  * implementation can be used for any type of Container.
44  *
45  * <b>IMPLEMENTATION WARNING</b> - This implementation assumes that no
46  * calls to <code>addValve()</code> or <code>removeValve</code> are allowed
47  * while a request is currently being processed.  Otherwise, the mechanism
48  * by which per-thread state is maintained will need to be modified.
49  *
50  * @author Craig R. McClanahan
51  */

52 public class StandardPipeline extends LifecycleBase implements Pipeline {
53
54     private static final Log log = LogFactory.getLog(StandardPipeline.class);
55     private static final StringManager sm = StringManager.getManager(Constants.Package);
56
57     // ----------------------------------------------------------- Constructors
58
59
60     /**
61      * Construct a new StandardPipeline instance with no associated Container.
62      */

63     public StandardPipeline() {
64
65         this(null);
66
67     }
68
69
70     /**
71      * Construct a new StandardPipeline instance that is associated with the
72      * specified Container.
73      *
74      * @param container The container we should be associated with
75      */

76     public StandardPipeline(Container container) {
77
78         super();
79         setContainer(container);
80
81     }
82
83
84     // ----------------------------------------------------- Instance Variables
85
86
87     /**
88      * The basic Valve (if any) associated with this Pipeline.
89      */

90     protected Valve basic = null;
91
92
93     /**
94      * The Container with which this Pipeline is associated.
95      */

96     protected Container container = null;
97
98
99     /**
100      * The first valve associated with this Pipeline.
101      */

102     protected Valve first = null;
103
104
105     // --------------------------------------------------------- Public Methods
106
107     @Override
108     public boolean isAsyncSupported() {
109         Valve valve = (first!=null)?first:basic;
110         boolean supported = true;
111         while (supported && valve!=null) {
112             supported = supported & valve.isAsyncSupported();
113             valve = valve.getNext();
114         }
115         return supported;
116     }
117
118
119     @Override
120     public void findNonAsyncValves(Set<String> result) {
121         Valve valve = (first!=null) ? first : basic;
122         while (valve != null) {
123             if (!valve.isAsyncSupported()) {
124                 result.add(valve.getClass().getName());
125             }
126             valve = valve.getNext();
127         }
128     }
129
130
131     // ------------------------------------------------------ Contained Methods
132
133     /**
134      * Return the Container with which this Pipeline is associated.
135      */

136     @Override
137     public Container getContainer() {
138         return this.container;
139     }
140
141
142     /**
143      * Set the Container with which this Pipeline is associated.
144      *
145      * @param container The new associated container
146      */

147     @Override
148     public void setContainer(Container container) {
149         this.container = container;
150     }
151
152
153     @Override
154     protected void initInternal() {
155         // NOOP
156     }
157
158
159     /**
160      * Start {@link Valve}s) in this pipeline and implement the requirements
161      * of {@link LifecycleBase#startInternal()}.
162      *
163      * @exception LifecycleException if this component detects a fatal error
164      *  that prevents this component from being used
165      */

166     @Override
167     protected synchronized void startInternal() throws LifecycleException {
168
169         // Start the Valves in our pipeline (including the basic), if any
170         Valve current = first;
171         if (current == null) {
172             current = basic;
173         }
174         while (current != null) {
175             if (current instanceof Lifecycle)
176                 ((Lifecycle) current).start();
177             current = current.getNext();
178         }
179
180         setState(LifecycleState.STARTING);
181     }
182
183
184     /**
185      * Stop {@link Valve}s) in this pipeline and implement the requirements
186      * of {@link LifecycleBase#stopInternal()}.
187      *
188      * @exception LifecycleException if this component detects a fatal error
189      *  that prevents this component from being used
190      */

191     @Override
192     protected synchronized void stopInternal() throws LifecycleException {
193
194         setState(LifecycleState.STOPPING);
195
196         // Stop the Valves in our pipeline (including the basic), if any
197         Valve current = first;
198         if (current == null) {
199             current = basic;
200         }
201         while (current != null) {
202             if (current instanceof Lifecycle)
203                 ((Lifecycle) current).stop();
204             current = current.getNext();
205         }
206     }
207
208
209     @Override
210     protected void destroyInternal() {
211         Valve[] valves = getValves();
212         for (Valve valve : valves) {
213             removeValve(valve);
214         }
215     }
216
217
218     /**
219      * Return a String representation of this component.
220      */

221     @Override
222     public String toString() {
223         return ToStringUtil.toString(this);
224     }
225
226
227     // ------------------------------------------------------- Pipeline Methods
228
229
230     /**
231      * <p>Return the Valve instance that has been distinguished as the basic
232      * Valve for this Pipeline (if any).
233      */

234     @Override
235     public Valve getBasic() {
236         return this.basic;
237     }
238
239
240     /**
241      * <p>Set the Valve instance that has been distinguished as the basic
242      * Valve for this Pipeline (if any).  Prior to setting the basic Valve,
243      * the Valve's <code>setContainer()</code> will be called, if it
244      * implements <code>Contained</code>, with the owning Container as an
245      * argument.  The method may throw an <code>IllegalArgumentException</code>
246      * if this Valve chooses not to be associated with this Container, or
247      * <code>IllegalStateException</code> if it is already associated with
248      * a different Container.</p>
249      *
250      * @param valve Valve to be distinguished as the basic Valve
251      */

252     @Override
253     public void setBasic(Valve valve) {
254
255         // Change components if necessary
256         Valve oldBasic = this.basic;
257         if (oldBasic == valve)
258             return;
259
260         // Stop the old component if necessary
261         if (oldBasic != null) {
262             if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
263                 try {
264                     ((Lifecycle) oldBasic).stop();
265                 } catch (LifecycleException e) {
266                     log.error(sm.getString("standardPipeline.basic.stop"), e);
267                 }
268             }
269             if (oldBasic instanceof Contained) {
270                 try {
271                     ((Contained) oldBasic).setContainer(null);
272                 } catch (Throwable t) {
273                     ExceptionUtils.handleThrowable(t);
274                 }
275             }
276         }
277
278         // Start the new component if necessary
279         if (valve == null)
280             return;
281         if (valve instanceof Contained) {
282             ((Contained) valve).setContainer(this.container);
283         }
284         if (getState().isAvailable() && valve instanceof Lifecycle) {
285             try {
286                 ((Lifecycle) valve).start();
287             } catch (LifecycleException e) {
288                 log.error(sm.getString("standardPipeline.basic.start"), e);
289                 return;
290             }
291         }
292
293         // Update the pipeline
294         Valve current = first;
295         while (current != null) {
296             if (current.getNext() == oldBasic) {
297                 current.setNext(valve);
298                 break;
299             }
300             current = current.getNext();
301         }
302
303         this.basic = valve;
304
305     }
306
307
308     /**
309      * <p>Add a new Valve to the end of the pipeline associated with this
310      * Container.  Prior to adding the Valve, the Valve's
311      * <code>setContainer()</code> method will be called, if it implements
312      * <code>Contained</code>, with the owning Container as an argument.
313      * The method may throw an
314      * <code>IllegalArgumentException</code> if this Valve chooses not to
315      * be associated with this Container, or <code>IllegalStateException</code>
316      * if it is already associated with a different Container.</p>
317      *
318      * @param valve Valve to be added
319      *
320      * @exception IllegalArgumentException if this Container refused to
321      *  accept the specified Valve
322      * @exception IllegalArgumentException if the specified Valve refuses to be
323      *  associated with this Container
324      * @exception IllegalStateException if the specified Valve is already
325      *  associated with a different Container
326      */

327     @Override
328     public void addValve(Valve valve) {
329
330         // Validate that we can add this Valve
331         if (valve instanceof Contained)
332             ((Contained) valve).setContainer(this.container);
333
334         // Start the new component if necessary
335         if (getState().isAvailable()) {
336             if (valve instanceof Lifecycle) {
337                 try {
338                     ((Lifecycle) valve).start();
339                 } catch (LifecycleException e) {
340                     log.error(sm.getString("standardPipeline.valve.start"), e);
341                 }
342             }
343         }
344
345         // Add this Valve to the set associated with this Pipeline
346         if (first == null) {
347             first = valve;
348             valve.setNext(basic);
349         } else {
350             Valve current = first;
351             while (current != null) {
352                 if (current.getNext() == basic) {
353                     current.setNext(valve);
354                     valve.setNext(basic);
355                     break;
356                 }
357                 current = current.getNext();
358             }
359         }
360
361         container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
362     }
363
364
365     /**
366      * Return the set of Valves in the pipeline associated with this
367      * Container, including the basic Valve (if any).  If there are no
368      * such Valves, a zero-length array is returned.
369      */

370     @Override
371     public Valve[] getValves() {
372
373         List<Valve> valveList = new ArrayList<>();
374         Valve current = first;
375         if (current == null) {
376             current = basic;
377         }
378         while (current != null) {
379             valveList.add(current);
380             current = current.getNext();
381         }
382
383         return valveList.toArray(new Valve[0]);
384
385     }
386
387     public ObjectName[] getValveObjectNames() {
388
389         List<ObjectName> valveList = new ArrayList<>();
390         Valve current = first;
391         if (current == null) {
392             current = basic;
393         }
394         while (current != null) {
395             if (current instanceof JmxEnabled) {
396                 valveList.add(((JmxEnabled) current).getObjectName());
397             }
398             current = current.getNext();
399         }
400
401         return valveList.toArray(new ObjectName[0]);
402
403     }
404
405     /**
406      * Remove the specified Valve from the pipeline associated with this
407      * Container, if it is found; otherwise, do nothing.  If the Valve is
408      * found and removed, the Valve's <code>setContainer(null)</code> method
409      * will be called if it implements <code>Contained</code>.
410      *
411      * @param valve Valve to be removed
412      */

413     @Override
414     public void removeValve(Valve valve) {
415
416         Valve current;
417         if(first == valve) {
418             first = first.getNext();
419             current = null;
420         } else {
421             current = first;
422         }
423         while (current != null) {
424             if (current.getNext() == valve) {
425                 current.setNext(valve.getNext());
426                 break;
427             }
428             current = current.getNext();
429         }
430
431         if (first == basic) first = null;
432
433         if (valve instanceof Contained)
434             ((Contained) valve).setContainer(null);
435
436         if (valve instanceof Lifecycle) {
437             // Stop this valve if necessary
438             if (getState().isAvailable()) {
439                 try {
440                     ((Lifecycle) valve).stop();
441                 } catch (LifecycleException e) {
442                     log.error(sm.getString("standardPipeline.valve.stop"), e);
443                 }
444             }
445             try {
446                 ((Lifecycle) valve).destroy();
447             } catch (LifecycleException e) {
448                 log.error(sm.getString("standardPipeline.valve.destroy"), e);
449             }
450         }
451
452         container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
453     }
454
455
456     @Override
457     public Valve getFirst() {
458         if (first != null) {
459             return first;
460         }
461
462         return basic;
463     }
464 }
465