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