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(falsenull);
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