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.beans.PropertyChangeListener;
20 import java.beans.PropertyChangeSupport;
21 import java.io.File;
22 import java.security.AccessController;
23 import java.security.PrivilegedAction;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.CopyOnWriteArrayList;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ScheduledFuture;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.locks.Lock;
35 import java.util.concurrent.locks.ReadWriteLock;
36 import java.util.concurrent.locks.ReentrantReadWriteLock;
37
38 import javax.management.ObjectName;
39
40 import org.apache.catalina.AccessLog;
41 import org.apache.catalina.Cluster;
42 import org.apache.catalina.Container;
43 import org.apache.catalina.ContainerEvent;
44 import org.apache.catalina.ContainerListener;
45 import org.apache.catalina.Context;
46 import org.apache.catalina.Engine;
47 import org.apache.catalina.Globals;
48 import org.apache.catalina.Host;
49 import org.apache.catalina.Lifecycle;
50 import org.apache.catalina.LifecycleException;
51 import org.apache.catalina.LifecycleState;
52 import org.apache.catalina.Loader;
53 import org.apache.catalina.Pipeline;
54 import org.apache.catalina.Realm;
55 import org.apache.catalina.Server;
56 import org.apache.catalina.Valve;
57 import org.apache.catalina.Wrapper;
58 import org.apache.catalina.connector.Request;
59 import org.apache.catalina.connector.Response;
60 import org.apache.catalina.util.ContextName;
61 import org.apache.catalina.util.LifecycleMBeanBase;
62 import org.apache.juli.logging.Log;
63 import org.apache.juli.logging.LogFactory;
64 import org.apache.tomcat.util.ExceptionUtils;
65 import org.apache.tomcat.util.MultiThrowable;
66 import org.apache.tomcat.util.res.StringManager;
67 import org.apache.tomcat.util.threads.InlineExecutorService;
68
69
70 /**
71 * Abstract implementation of the <b>Container</b> interface, providing common
72 * functionality required by nearly every implementation. Classes extending
73 * this base class must may implement a replacement for <code>invoke()</code>.
74 * <p>
75 * All subclasses of this abstract base class will include support for a
76 * Pipeline object that defines the processing to be performed for each request
77 * received by the <code>invoke()</code> method of this class, utilizing the
78 * "Chain of Responsibility" design pattern. A subclass should encapsulate its
79 * own processing functionality as a <code>Valve</code>, and configure this
80 * Valve into the pipeline by calling <code>setBasic()</code>.
81 * <p>
82 * This implementation fires property change events, per the JavaBeans design
83 * pattern, for changes in singleton properties. In addition, it fires the
84 * following <code>ContainerEvent</code> events to listeners who register
85 * themselves with <code>addContainerListener()</code>:
86 * <table border=1>
87 * <caption>ContainerEvents fired by this implementation</caption>
88 * <tr>
89 * <th>Type</th>
90 * <th>Data</th>
91 * <th>Description</th>
92 * </tr>
93 * <tr>
94 * <td><code>addChild</code></td>
95 * <td><code>Container</code></td>
96 * <td>Child container added to this Container.</td>
97 * </tr>
98 * <tr>
99 * <td><code>{@link #getPipeline() pipeline}.addValve</code></td>
100 * <td><code>Valve</code></td>
101 * <td>Valve added to this Container.</td>
102 * </tr>
103 * <tr>
104 * <td><code>removeChild</code></td>
105 * <td><code>Container</code></td>
106 * <td>Child container removed from this Container.</td>
107 * </tr>
108 * <tr>
109 * <td><code>{@link #getPipeline() pipeline}.removeValve</code></td>
110 * <td><code>Valve</code></td>
111 * <td>Valve removed from this Container.</td>
112 * </tr>
113 * <tr>
114 * <td><code>start</code></td>
115 * <td><code>null</code></td>
116 * <td>Container was started.</td>
117 * </tr>
118 * <tr>
119 * <td><code>stop</code></td>
120 * <td><code>null</code></td>
121 * <td>Container was stopped.</td>
122 * </tr>
123 * </table>
124 * Subclasses that fire additional events should document them in the
125 * class comments of the implementation class.
126 *
127 * @author Craig R. McClanahan
128 */
129 public abstract class ContainerBase extends LifecycleMBeanBase
130 implements Container {
131
132 private static final Log log = LogFactory.getLog(ContainerBase.class);
133
134 /**
135 * Perform addChild with the permissions of this class.
136 * addChild can be called with the XML parser on the stack,
137 * this allows the XML parser to have fewer privileges than
138 * Tomcat.
139 */
140 protected class PrivilegedAddChild implements PrivilegedAction<Void> {
141
142 private final Container child;
143
144 PrivilegedAddChild(Container child) {
145 this.child = child;
146 }
147
148 @Override
149 public Void run() {
150 addChildInternal(child);
151 return null;
152 }
153
154 }
155
156
157 // ----------------------------------------------------- Instance Variables
158
159
160 /**
161 * The child Containers belonging to this Container, keyed by name.
162 */
163 protected final HashMap<String, Container> children = new HashMap<>();
164
165
166 /**
167 * The processor delay for this component.
168 */
169 protected int backgroundProcessorDelay = -1;
170
171
172 /**
173 * The future allowing control of the background processor.
174 */
175 protected ScheduledFuture<?> backgroundProcessorFuture;
176 protected ScheduledFuture<?> monitorFuture;
177
178 /**
179 * The container event listeners for this Container. Implemented as a
180 * CopyOnWriteArrayList since listeners may invoke methods to add/remove
181 * themselves or other listeners and with a ReadWriteLock that would trigger
182 * a deadlock.
183 */
184 protected final List<ContainerListener> listeners = new CopyOnWriteArrayList<>();
185
186 /**
187 * The Logger implementation with which this Container is associated.
188 */
189 protected Log logger = null;
190
191
192 /**
193 * Associated logger name.
194 */
195 protected String logName = null;
196
197
198 /**
199 * The cluster with which this Container is associated.
200 */
201 protected Cluster cluster = null;
202 private final ReadWriteLock clusterLock = new ReentrantReadWriteLock();
203
204
205 /**
206 * The human-readable name of this Container.
207 */
208 protected String name = null;
209
210
211 /**
212 * The parent Container to which this Container is a child.
213 */
214 protected Container parent = null;
215
216
217 /**
218 * The parent class loader to be configured when we install a Loader.
219 */
220 protected ClassLoader parentClassLoader = null;
221
222
223 /**
224 * The Pipeline object with which this Container is associated.
225 */
226 protected final Pipeline pipeline = new StandardPipeline(this);
227
228
229 /**
230 * The Realm with which this Container is associated.
231 */
232 private volatile Realm realm = null;
233
234
235 /**
236 * Lock used to control access to the Realm.
237 */
238 private final ReadWriteLock realmLock = new ReentrantReadWriteLock();
239
240
241 /**
242 * The string manager for this package.
243 */
244 protected static final StringManager sm =
245 StringManager.getManager(Constants.Package);
246
247
248 /**
249 * Will children be started automatically when they are added.
250 */
251 protected boolean startChildren = true;
252
253 /**
254 * The property change support for this component.
255 */
256 protected final PropertyChangeSupport support =
257 new PropertyChangeSupport(this);
258
259
260 /**
261 * The access log to use for requests normally handled by this container
262 * that have been handled earlier in the processing chain.
263 */
264 protected volatile AccessLog accessLog = null;
265 private volatile boolean accessLogScanComplete = false;
266
267
268 /**
269 * The number of threads available to process start and stop events for any
270 * children associated with this container.
271 */
272 private int startStopThreads = 1;
273 protected ExecutorService startStopExecutor;
274
275
276 // ------------------------------------------------------------- Properties
277
278 @Override
279 public int getStartStopThreads() {
280 return startStopThreads;
281 }
282
283 @Override
284 public void setStartStopThreads(int startStopThreads) {
285 int oldStartStopThreads = this.startStopThreads;
286 this.startStopThreads = startStopThreads;
287
288 // Use local copies to ensure thread safety
289 if (oldStartStopThreads != startStopThreads && startStopExecutor != null) {
290 reconfigureStartStopExecutor(getStartStopThreads());
291 }
292 }
293
294
295 /**
296 * Get the delay between the invocation of the backgroundProcess method on
297 * this container and its children. Child containers will not be invoked
298 * if their delay value is not negative (which would mean they are using
299 * their own thread). Setting this to a positive value will cause
300 * a thread to be spawn. After waiting the specified amount of time,
301 * the thread will invoke the executePeriodic method on this container
302 * and all its children.
303 */
304 @Override
305 public int getBackgroundProcessorDelay() {
306 return backgroundProcessorDelay;
307 }
308
309
310 /**
311 * Set the delay between the invocation of the execute method on this
312 * container and its children.
313 *
314 * @param delay The delay in seconds between the invocation of
315 * backgroundProcess methods
316 */
317 @Override
318 public void setBackgroundProcessorDelay(int delay) {
319 backgroundProcessorDelay = delay;
320 }
321
322
323 /**
324 * Return the Logger for this Container.
325 */
326 @Override
327 public Log getLogger() {
328 if (logger != null)
329 return logger;
330 logger = LogFactory.getLog(getLogName());
331 return logger;
332 }
333
334
335 /**
336 * @return the abbreviated name of this container for logging messages
337 */
338 @Override
339 public String getLogName() {
340
341 if (logName != null) {
342 return logName;
343 }
344 String loggerName = null;
345 Container current = this;
346 while (current != null) {
347 String name = current.getName();
348 if ((name == null) || (name.equals(""))) {
349 name = "/";
350 } else if (name.startsWith("##")) {
351 name = "/" + name;
352 }
353 loggerName = "[" + name + "]"
354 + ((loggerName != null) ? ("." + loggerName) : "");
355 current = current.getParent();
356 }
357 logName = ContainerBase.class.getName() + "." + loggerName;
358 return logName;
359
360 }
361
362
363 /**
364 * Return the Cluster with which this Container is associated. If there is
365 * no associated Cluster, return the Cluster associated with our parent
366 * Container (if any); otherwise return <code>null</code>.
367 */
368 @Override
369 public Cluster getCluster() {
370 Lock readLock = clusterLock.readLock();
371 readLock.lock();
372 try {
373 if (cluster != null)
374 return cluster;
375
376 if (parent != null)
377 return parent.getCluster();
378
379 return null;
380 } finally {
381 readLock.unlock();
382 }
383 }
384
385
386 /*
387 * Provide access to just the cluster component attached to this container.
388 */
389 protected Cluster getClusterInternal() {
390 Lock readLock = clusterLock.readLock();
391 readLock.lock();
392 try {
393 return cluster;
394 } finally {
395 readLock.unlock();
396 }
397 }
398
399
400 /**
401 * Set the Cluster with which this Container is associated.
402 *
403 * @param cluster The newly associated Cluster
404 */
405 @Override
406 public void setCluster(Cluster cluster) {
407
408 Cluster oldCluster = null;
409 Lock writeLock = clusterLock.writeLock();
410 writeLock.lock();
411 try {
412 // Change components if necessary
413 oldCluster = this.cluster;
414 if (oldCluster == cluster)
415 return;
416 this.cluster = cluster;
417
418 // Stop the old component if necessary
419 if (getState().isAvailable() && (oldCluster != null) &&
420 (oldCluster instanceof Lifecycle)) {
421 try {
422 ((Lifecycle) oldCluster).stop();
423 } catch (LifecycleException e) {
424 log.error(sm.getString("containerBase.cluster.stop"), e);
425 }
426 }
427
428 // Start the new component if necessary
429 if (cluster != null)
430 cluster.setContainer(this);
431
432 if (getState().isAvailable() && (cluster != null) &&
433 (cluster instanceof Lifecycle)) {
434 try {
435 ((Lifecycle) cluster).start();
436 } catch (LifecycleException e) {
437 log.error(sm.getString("containerBase.cluster.start"), e);
438 }
439 }
440 } finally {
441 writeLock.unlock();
442 }
443
444 // Report this property change to interested listeners
445 support.firePropertyChange("cluster", oldCluster, cluster);
446 }
447
448
449 /**
450 * Return a name string (suitable for use by humans) that describes this
451 * Container. Within the set of child containers belonging to a particular
452 * parent, Container names must be unique.
453 */
454 @Override
455 public String getName() {
456 return name;
457 }
458
459
460 /**
461 * Set a name string (suitable for use by humans) that describes this
462 * Container. Within the set of child containers belonging to a particular
463 * parent, Container names must be unique.
464 *
465 * @param name New name of this container
466 *
467 * @exception IllegalStateException if this Container has already been
468 * added to the children of a parent Container (after which the name
469 * may not be changed)
470 */
471 @Override
472 public void setName(String name) {
473 if (name == null) {
474 throw new IllegalArgumentException(sm.getString("containerBase.nullName"));
475 }
476 String oldName = this.name;
477 this.name = name;
478 support.firePropertyChange("name", oldName, this.name);
479 }
480
481
482 /**
483 * Return if children of this container will be started automatically when
484 * they are added to this container.
485 *
486 * @return <code>true</code> if the children will be started
487 */
488 public boolean getStartChildren() {
489 return startChildren;
490 }
491
492
493 /**
494 * Set if children of this container will be started automatically when
495 * they are added to this container.
496 *
497 * @param startChildren New value of the startChildren flag
498 */
499 public void setStartChildren(boolean startChildren) {
500
501 boolean oldStartChildren = this.startChildren;
502 this.startChildren = startChildren;
503 support.firePropertyChange("startChildren", oldStartChildren, this.startChildren);
504 }
505
506
507 /**
508 * Return the Container for which this Container is a child, if there is
509 * one. If there is no defined parent, return <code>null</code>.
510 */
511 @Override
512 public Container getParent() {
513 return parent;
514 }
515
516
517 /**
518 * Set the parent Container to which this Container is being added as a
519 * child. This Container may refuse to become attached to the specified
520 * Container by throwing an exception.
521 *
522 * @param container Container to which this Container is being added
523 * as a child
524 *
525 * @exception IllegalArgumentException if this Container refuses to become
526 * attached to the specified Container
527 */
528 @Override
529 public void setParent(Container container) {
530
531 Container oldParent = this.parent;
532 this.parent = container;
533 support.firePropertyChange("parent", oldParent, this.parent);
534
535 }
536
537
538 /**
539 * Return the parent class loader (if any) for this web application.
540 * This call is meaningful only <strong>after</strong> a Loader has
541 * been configured.
542 */
543 @Override
544 public ClassLoader getParentClassLoader() {
545 if (parentClassLoader != null)
546 return parentClassLoader;
547 if (parent != null) {
548 return parent.getParentClassLoader();
549 }
550 return ClassLoader.getSystemClassLoader();
551 }
552
553
554 /**
555 * Set the parent class loader (if any) for this web application.
556 * This call is meaningful only <strong>before</strong> a Loader has
557 * been configured, and the specified value (if non-null) should be
558 * passed as an argument to the class loader constructor.
559 *
560 *
561 * @param parent The new parent class loader
562 */
563 @Override
564 public void setParentClassLoader(ClassLoader parent) {
565 ClassLoader oldParentClassLoader = this.parentClassLoader;
566 this.parentClassLoader = parent;
567 support.firePropertyChange("parentClassLoader", oldParentClassLoader,
568 this.parentClassLoader);
569
570 }
571
572
573 /**
574 * Return the Pipeline object that manages the Valves associated with
575 * this Container.
576 */
577 @Override
578 public Pipeline getPipeline() {
579 return this.pipeline;
580 }
581
582
583 /**
584 * Return the Realm with which this Container is associated. If there is
585 * no associated Realm, return the Realm associated with our parent
586 * Container (if any); otherwise return <code>null</code>.
587 */
588 @Override
589 public Realm getRealm() {
590
591 Lock l = realmLock.readLock();
592 l.lock();
593 try {
594 if (realm != null)
595 return realm;
596 if (parent != null)
597 return parent.getRealm();
598 return null;
599 } finally {
600 l.unlock();
601 }
602 }
603
604
605 protected Realm getRealmInternal() {
606 Lock l = realmLock.readLock();
607 l.lock();
608 try {
609 return realm;
610 } finally {
611 l.unlock();
612 }
613 }
614
615 /**
616 * Set the Realm with which this Container is associated.
617 *
618 * @param realm The newly associated Realm
619 */
620 @Override
621 public void setRealm(Realm realm) {
622
623 Lock l = realmLock.writeLock();
624 l.lock();
625 try {
626 // Change components if necessary
627 Realm oldRealm = this.realm;
628 if (oldRealm == realm)
629 return;
630 this.realm = realm;
631
632 // Stop the old component if necessary
633 if (getState().isAvailable() && (oldRealm != null) &&
634 (oldRealm instanceof Lifecycle)) {
635 try {
636 ((Lifecycle) oldRealm).stop();
637 } catch (LifecycleException e) {
638 log.error(sm.getString("containerBase.realm.stop"), e);
639 }
640 }
641
642 // Start the new component if necessary
643 if (realm != null)
644 realm.setContainer(this);
645 if (getState().isAvailable() && (realm != null) &&
646 (realm instanceof Lifecycle)) {
647 try {
648 ((Lifecycle) realm).start();
649 } catch (LifecycleException e) {
650 log.error(sm.getString("containerBase.realm.start"), e);
651 }
652 }
653
654 // Report this property change to interested listeners
655 support.firePropertyChange("realm", oldRealm, this.realm);
656 } finally {
657 l.unlock();
658 }
659
660 }
661
662
663 // ------------------------------------------------------ Container Methods
664
665
666 /**
667 * Add a new child Container to those associated with this Container,
668 * if supported. Prior to adding this Container to the set of children,
669 * the child's <code>setParent()</code> method must be called, with this
670 * Container as an argument. This method may thrown an
671 * <code>IllegalArgumentException</code> if this Container chooses not
672 * to be attached to the specified Container, in which case it is not added
673 *
674 * @param child New child Container to be added
675 *
676 * @exception IllegalArgumentException if this exception is thrown by
677 * the <code>setParent()</code> method of the child Container
678 * @exception IllegalArgumentException if the new child does not have
679 * a name unique from that of existing children of this Container
680 * @exception IllegalStateException if this Container does not support
681 * child Containers
682 */
683 @Override
684 public void addChild(Container child) {
685 if (Globals.IS_SECURITY_ENABLED) {
686 PrivilegedAction<Void> dp =
687 new PrivilegedAddChild(child);
688 AccessController.doPrivileged(dp);
689 } else {
690 addChildInternal(child);
691 }
692 }
693
694 private void addChildInternal(Container child) {
695
696 if (log.isDebugEnabled()) {
697 log.debug("Add child " + child + " " + this);
698 }
699
700 synchronized(children) {
701 if (children.get(child.getName()) != null)
702 throw new IllegalArgumentException(
703 sm.getString("containerBase.child.notUnique", child.getName()));
704 child.setParent(this); // May throw IAE
705 children.put(child.getName(), child);
706 }
707
708 fireContainerEvent(ADD_CHILD_EVENT, child);
709
710 // Start child
711 // Don't do this inside sync block - start can be a slow process and
712 // locking the children object can cause problems elsewhere
713 try {
714 if ((getState().isAvailable() ||
715 LifecycleState.STARTING_PREP.equals(getState())) &&
716 startChildren) {
717 child.start();
718 }
719 } catch (LifecycleException e) {
720 throw new IllegalStateException(sm.getString("containerBase.child.start"), e);
721 }
722 }
723
724
725 /**
726 * Add a container event listener to this component.
727 *
728 * @param listener The listener to add
729 */
730 @Override
731 public void addContainerListener(ContainerListener listener) {
732 listeners.add(listener);
733 }
734
735
736 /**
737 * Add a property change listener to this component.
738 *
739 * @param listener The listener to add
740 */
741 @Override
742 public void addPropertyChangeListener(PropertyChangeListener listener) {
743 support.addPropertyChangeListener(listener);
744 }
745
746
747 /**
748 * Return the child Container, associated with this Container, with
749 * the specified name (if any); otherwise, return <code>null</code>
750 *
751 * @param name Name of the child Container to be retrieved
752 */
753 @Override
754 public Container findChild(String name) {
755 if (name == null) {
756 return null;
757 }
758 synchronized (children) {
759 return children.get(name);
760 }
761 }
762
763
764 /**
765 * Return the set of children Containers associated with this Container.
766 * If this Container has no children, a zero-length array is returned.
767 */
768 @Override
769 public Container[] findChildren() {
770 synchronized (children) {
771 Container results[] = new Container[children.size()];
772 return children.values().toArray(results);
773 }
774 }
775
776
777 /**
778 * Return the set of container listeners associated with this Container.
779 * If this Container has no registered container listeners, a zero-length
780 * array is returned.
781 */
782 @Override
783 public ContainerListener[] findContainerListeners() {
784 ContainerListener[] results =
785 new ContainerListener[0];
786 return listeners.toArray(results);
787 }
788
789
790 /**
791 * Remove an existing child Container from association with this parent
792 * Container.
793 *
794 * @param child Existing child Container to be removed
795 */
796 @Override
797 public void removeChild(Container child) {
798
799 if (child == null) {
800 return;
801 }
802
803 try {
804 if (child.getState().isAvailable()) {
805 child.stop();
806 }
807 } catch (LifecycleException e) {
808 log.error(sm.getString("containerBase.child.stop"), e);
809 }
810
811 boolean destroy = false;
812 try {
813 // child.destroy() may have already been called which would have
814 // triggered this call. If that is the case, no need to destroy the
815 // child again.
816 if (!LifecycleState.DESTROYING.equals(child.getState())) {
817 child.destroy();
818 destroy = true;
819 }
820 } catch (LifecycleException e) {
821 log.error(sm.getString("containerBase.child.destroy"), e);
822 }
823
824 if (!destroy) {
825 fireContainerEvent(REMOVE_CHILD_EVENT, child);
826 }
827
828 synchronized(children) {
829 if (children.get(child.getName()) == null)
830 return;
831 children.remove(child.getName());
832 }
833
834 }
835
836
837 /**
838 * Remove a container event listener from this component.
839 *
840 * @param listener The listener to remove
841 */
842 @Override
843 public void removeContainerListener(ContainerListener listener) {
844 listeners.remove(listener);
845 }
846
847
848 /**
849 * Remove a property change listener from this component.
850 *
851 * @param listener The listener to remove
852 */
853 @Override
854 public void removePropertyChangeListener(PropertyChangeListener listener) {
855
856 support.removePropertyChangeListener(listener);
857
858 }
859
860
861 @Override
862 protected void initInternal() throws LifecycleException {
863 reconfigureStartStopExecutor(getStartStopThreads());
864 super.initInternal();
865 }
866
867
868 private void reconfigureStartStopExecutor(int threads) {
869 if (threads == 1) {
870 // Use a fake executor
871 if (!(startStopExecutor instanceof InlineExecutorService)) {
872 startStopExecutor = new InlineExecutorService();
873 }
874 } else {
875 // Delegate utility execution to the Service
876 Server server = Container.getService(this).getServer();
877 server.setUtilityThreads(threads);
878 startStopExecutor = server.getUtilityExecutor();
879 }
880 }
881
882
883 /**
884 * Start this component and implement the requirements
885 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
886 *
887 * @exception LifecycleException if this component detects a fatal error
888 * that prevents this component from being used
889 */
890 @Override
891 protected synchronized void startInternal() throws LifecycleException {
892
893 // Start our subordinate components, if any
894 logger = null;
895 getLogger();
896 Cluster cluster = getClusterInternal();
897 if (cluster instanceof Lifecycle) {
898 ((Lifecycle) cluster).start();
899 }
900 Realm realm = getRealmInternal();
901 if (realm instanceof Lifecycle) {
902 ((Lifecycle) realm).start();
903 }
904
905 // Start our child containers, if any
906 Container children[] = findChildren();
907 List<Future<Void>> results = new ArrayList<>();
908 for (int i = 0; i < children.length; i++) {
909 results.add(startStopExecutor.submit(new StartChild(children[i])));
910 }
911
912 MultiThrowable multiThrowable = null;
913
914 for (Future<Void> result : results) {
915 try {
916 result.get();
917 } catch (Throwable e) {
918 log.error(sm.getString("containerBase.threadedStartFailed"), e);
919 if (multiThrowable == null) {
920 multiThrowable = new MultiThrowable();
921 }
922 multiThrowable.add(e);
923 }
924
925 }
926 if (multiThrowable != null) {
927 throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
928 multiThrowable.getThrowable());
929 }
930
931 // Start the Valves in our pipeline (including the basic), if any
932 if (pipeline instanceof Lifecycle) {
933 ((Lifecycle) pipeline).start();
934 }
935
936 setState(LifecycleState.STARTING);
937
938 // Start our thread
939 if (backgroundProcessorDelay > 0) {
940 monitorFuture = Container.getService(ContainerBase.this).getServer()
941 .getUtilityExecutor().scheduleWithFixedDelay(
942 new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
943 }
944 }
945
946
947 /**
948 * Stop this component and implement the requirements
949 * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
950 *
951 * @exception LifecycleException if this component detects a fatal error
952 * that prevents this component from being used
953 */
954 @Override
955 protected synchronized void stopInternal() throws LifecycleException {
956
957 // Stop our thread
958 if (monitorFuture != null) {
959 monitorFuture.cancel(true);
960 monitorFuture = null;
961 }
962 threadStop();
963
964 setState(LifecycleState.STOPPING);
965
966 // Stop the Valves in our pipeline (including the basic), if any
967 if (pipeline instanceof Lifecycle &&
968 ((Lifecycle) pipeline).getState().isAvailable()) {
969 ((Lifecycle) pipeline).stop();
970 }
971
972 // Stop our child containers, if any
973 Container children[] = findChildren();
974 List<Future<Void>> results = new ArrayList<>();
975 for (int i = 0; i < children.length; i++) {
976 results.add(startStopExecutor.submit(new StopChild(children[i])));
977 }
978
979 boolean fail = false;
980 for (Future<Void> result : results) {
981 try {
982 result.get();
983 } catch (Exception e) {
984 log.error(sm.getString("containerBase.threadedStopFailed"), e);
985 fail = true;
986 }
987 }
988 if (fail) {
989 throw new LifecycleException(
990 sm.getString("containerBase.threadedStopFailed"));
991 }
992
993 // Stop our subordinate components, if any
994 Realm realm = getRealmInternal();
995 if (realm instanceof Lifecycle) {
996 ((Lifecycle) realm).stop();
997 }
998 Cluster cluster = getClusterInternal();
999 if (cluster instanceof Lifecycle) {
1000 ((Lifecycle) cluster).stop();
1001 }
1002 }
1003
1004 @Override
1005 protected void destroyInternal() throws LifecycleException {
1006
1007 Realm realm = getRealmInternal();
1008 if (realm instanceof Lifecycle) {
1009 ((Lifecycle) realm).destroy();
1010 }
1011 Cluster cluster = getClusterInternal();
1012 if (cluster instanceof Lifecycle) {
1013 ((Lifecycle) cluster).destroy();
1014 }
1015
1016 // Stop the Valves in our pipeline (including the basic), if any
1017 if (pipeline instanceof Lifecycle) {
1018 ((Lifecycle) pipeline).destroy();
1019 }
1020
1021 // Remove children now this container is being destroyed
1022 for (Container child : findChildren()) {
1023 removeChild(child);
1024 }
1025
1026 // Required if the child is destroyed directly.
1027 if (parent != null) {
1028 parent.removeChild(this);
1029 }
1030
1031 // If init fails, this may be null
1032 if (startStopExecutor != null) {
1033 startStopExecutor.shutdownNow();
1034 }
1035
1036 super.destroyInternal();
1037 }
1038
1039
1040 /**
1041 * Check this container for an access log and if none is found, look to the
1042 * parent. If there is no parent and still none is found, use the NoOp
1043 * access log.
1044 */
1045 @Override
1046 public void logAccess(Request request, Response response, long time,
1047 boolean useDefault) {
1048
1049 boolean logged = false;
1050
1051 if (getAccessLog() != null) {
1052 getAccessLog().log(request, response, time);
1053 logged = true;
1054 }
1055
1056 if (getParent() != null) {
1057 // No need to use default logger once request/response has been logged
1058 // once
1059 getParent().logAccess(request, response, time, (useDefault && !logged));
1060 }
1061 }
1062
1063 @Override
1064 public AccessLog getAccessLog() {
1065
1066 if (accessLogScanComplete) {
1067 return accessLog;
1068 }
1069
1070 AccessLogAdapter adapter = null;
1071 Valve valves[] = getPipeline().getValves();
1072 for (Valve valve : valves) {
1073 if (valve instanceof AccessLog) {
1074 if (adapter == null) {
1075 adapter = new AccessLogAdapter((AccessLog) valve);
1076 } else {
1077 adapter.add((AccessLog) valve);
1078 }
1079 }
1080 }
1081 if (adapter != null) {
1082 accessLog = adapter;
1083 }
1084 accessLogScanComplete = true;
1085 return accessLog;
1086 }
1087
1088 // ------------------------------------------------------- Pipeline Methods
1089
1090
1091 /**
1092 * Convenience method, intended for use by the digester to simplify the
1093 * process of adding Valves to containers. See
1094 * {@link Pipeline#addValve(Valve)} for full details. Components other than
1095 * the digester should use {@link #getPipeline()}.{@link #addValve(Valve)} in case a
1096 * future implementation provides an alternative method for the digester to
1097 * use.
1098 *
1099 * @param valve Valve to be added
1100 *
1101 * @exception IllegalArgumentException if this Container refused to
1102 * accept the specified Valve
1103 * @exception IllegalArgumentException if the specified Valve refuses to be
1104 * associated with this Container
1105 * @exception IllegalStateException if the specified Valve is already
1106 * associated with a different Container
1107 */
1108 public synchronized void addValve(Valve valve) {
1109
1110 pipeline.addValve(valve);
1111 }
1112
1113
1114 /**
1115 * Execute a periodic task, such as reloading, etc. This method will be
1116 * invoked inside the classloading context of this container. Unexpected
1117 * throwables will be caught and logged.
1118 */
1119 @Override
1120 public void backgroundProcess() {
1121
1122 if (!getState().isAvailable())
1123 return;
1124
1125 Cluster cluster = getClusterInternal();
1126 if (cluster != null) {
1127 try {
1128 cluster.backgroundProcess();
1129 } catch (Exception e) {
1130 log.warn(sm.getString("containerBase.backgroundProcess.cluster",
1131 cluster), e);
1132 }
1133 }
1134 Realm realm = getRealmInternal();
1135 if (realm != null) {
1136 try {
1137 realm.backgroundProcess();
1138 } catch (Exception e) {
1139 log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);
1140 }
1141 }
1142 Valve current = pipeline.getFirst();
1143 while (current != null) {
1144 try {
1145 current.backgroundProcess();
1146 } catch (Exception e) {
1147 log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);
1148 }
1149 current = current.getNext();
1150 }
1151 fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
1152 }
1153
1154
1155 @Override
1156 public File getCatalinaBase() {
1157
1158 if (parent == null) {
1159 return null;
1160 }
1161
1162 return parent.getCatalinaBase();
1163 }
1164
1165
1166 @Override
1167 public File getCatalinaHome() {
1168
1169 if (parent == null) {
1170 return null;
1171 }
1172
1173 return parent.getCatalinaHome();
1174 }
1175
1176
1177 // ------------------------------------------------------ Protected Methods
1178
1179 /**
1180 * Notify all container event listeners that a particular event has
1181 * occurred for this Container. The default implementation performs
1182 * this notification synchronously using the calling thread.
1183 *
1184 * @param type Event type
1185 * @param data Event data
1186 */
1187 @Override
1188 public void fireContainerEvent(String type, Object data) {
1189
1190 if (listeners.size() < 1)
1191 return;
1192
1193 ContainerEvent event = new ContainerEvent(this, type, data);
1194 // Note for each uses an iterator internally so this is safe
1195 for (ContainerListener listener : listeners) {
1196 listener.containerEvent(event);
1197 }
1198 }
1199
1200
1201 // -------------------- JMX and Registration --------------------
1202
1203 @Override
1204 protected String getDomainInternal() {
1205
1206 Container p = this.getParent();
1207 if (p == null) {
1208 return null;
1209 } else {
1210 return p.getDomain();
1211 }
1212 }
1213
1214
1215 @Override
1216 public String getMBeanKeyProperties() {
1217 Container c = this;
1218 StringBuilder keyProperties = new StringBuilder();
1219 int containerCount = 0;
1220
1221 // Work up container hierarchy, add a component to the name for
1222 // each container
1223 while (!(c instanceof Engine)) {
1224 if (c instanceof Wrapper) {
1225 keyProperties.insert(0, ",servlet=");
1226 keyProperties.insert(9, c.getName());
1227 } else if (c instanceof Context) {
1228 keyProperties.insert(0, ",context=");
1229 ContextName cn = new ContextName(c.getName(), false);
1230 keyProperties.insert(9,cn.getDisplayName());
1231 } else if (c instanceof Host) {
1232 keyProperties.insert(0, ",host=");
1233 keyProperties.insert(6, c.getName());
1234 } else if (c == null) {
1235 // May happen in unit testing and/or some embedding scenarios
1236 keyProperties.append(",container");
1237 keyProperties.append(containerCount++);
1238 keyProperties.append("=null");
1239 break;
1240 } else {
1241 // Should never happen...
1242 keyProperties.append(",container");
1243 keyProperties.append(containerCount++);
1244 keyProperties.append('=');
1245 keyProperties.append(c.getName());
1246 }
1247 c = c.getParent();
1248 }
1249 return keyProperties.toString();
1250 }
1251
1252 public ObjectName[] getChildren() {
1253 List<ObjectName> names = new ArrayList<>(children.size());
1254 for (Container next : children.values()) {
1255 if (next instanceof ContainerBase) {
1256 names.add(next.getObjectName());
1257 }
1258 }
1259 return names.toArray(new ObjectName[names.size()]);
1260 }
1261
1262
1263 // -------------------- Background Thread --------------------
1264
1265 /**
1266 * Start the background thread that will periodically check for
1267 * session timeouts.
1268 */
1269 protected void threadStart() {
1270 if (backgroundProcessorDelay > 0
1271 && (getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState()))
1272 && (backgroundProcessorFuture == null || backgroundProcessorFuture.isDone())) {
1273 if (backgroundProcessorFuture != null && backgroundProcessorFuture.isDone()) {
1274 // There was an error executing the scheduled task, get it and log it
1275 try {
1276 backgroundProcessorFuture.get();
1277 } catch (InterruptedException | ExecutionException e) {
1278 log.error(sm.getString("containerBase.backgroundProcess.error"), e);
1279 }
1280 }
1281 backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor()
1282 .scheduleWithFixedDelay(new ContainerBackgroundProcessor(),
1283 backgroundProcessorDelay, backgroundProcessorDelay,
1284 TimeUnit.SECONDS);
1285 }
1286 }
1287
1288
1289 /**
1290 * Stop the background thread that is periodically checking for
1291 * session timeouts.
1292 */
1293 protected void threadStop() {
1294 if (backgroundProcessorFuture != null) {
1295 backgroundProcessorFuture.cancel(true);
1296 backgroundProcessorFuture = null;
1297 }
1298 }
1299
1300
1301 @Override
1302 public final String toString() {
1303 StringBuilder sb = new StringBuilder();
1304 Container parent = getParent();
1305 if (parent != null) {
1306 sb.append(parent.toString());
1307 sb.append('.');
1308 }
1309 sb.append(this.getClass().getSimpleName());
1310 sb.append('[');
1311 sb.append(getName());
1312 sb.append(']');
1313 return sb.toString();
1314 }
1315
1316 // ------------------------------- ContainerBackgroundProcessor Inner Class
1317
1318 protected class ContainerBackgroundProcessorMonitor implements Runnable {
1319 @Override
1320 public void run() {
1321 if (getState().isAvailable()) {
1322 threadStart();
1323 }
1324 }
1325 }
1326
1327 /**
1328 * Private runnable class to invoke the backgroundProcess method
1329 * of this container and its children after a fixed delay.
1330 */
1331 protected class ContainerBackgroundProcessor implements Runnable {
1332
1333 @Override
1334 public void run() {
1335 processChildren(ContainerBase.this);
1336 }
1337
1338 protected void processChildren(Container container) {
1339 ClassLoader originalClassLoader = null;
1340
1341 try {
1342 if (container instanceof Context) {
1343 Loader loader = ((Context) container).getLoader();
1344 // Loader will be null for FailedContext instances
1345 if (loader == null) {
1346 return;
1347 }
1348
1349 // Ensure background processing for Contexts and Wrappers
1350 // is performed under the web app's class loader
1351 originalClassLoader = ((Context) container).bind(false, null);
1352 }
1353 container.backgroundProcess();
1354 Container[] children = container.findChildren();
1355 for (int i = 0; i < children.length; i++) {
1356 if (children[i].getBackgroundProcessorDelay() <= 0) {
1357 processChildren(children[i]);
1358 }
1359 }
1360 } catch (Throwable t) {
1361 ExceptionUtils.handleThrowable(t);
1362 log.error(sm.getString("containerBase.backgroundProcess.error"), t);
1363 } finally {
1364 if (container instanceof Context) {
1365 ((Context) container).unbind(false, originalClassLoader);
1366 }
1367 }
1368 }
1369 }
1370
1371
1372 // ---------------------------- Inner classes used with start/stop Executor
1373
1374 private static class StartChild implements Callable<Void> {
1375
1376 private Container child;
1377
1378 public StartChild(Container child) {
1379 this.child = child;
1380 }
1381
1382 @Override
1383 public Void call() throws LifecycleException {
1384 child.start();
1385 return null;
1386 }
1387 }
1388
1389 private static class StopChild implements Callable<Void> {
1390
1391 private Container child;
1392
1393 public StopChild(Container child) {
1394 this.child = child;
1395 }
1396
1397 @Override
1398 public Void call() throws LifecycleException {
1399 if (child.getState().isAvailable()) {
1400 child.stop();
1401 }
1402 return null;
1403 }
1404 }
1405
1406 }
1407