1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.core;
20
21
22 import java.beans.PropertyChangeListener;
23 import java.beans.PropertyChangeSupport;
24 import java.io.IOException;
25 import java.io.Serializable;
26 import java.security.AccessController;
27 import java.security.PrivilegedAction;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Hashtable;
31 import java.util.Iterator;
32
33 import javax.management.MBeanRegistration;
34 import javax.management.MBeanServer;
35 import javax.management.MalformedObjectNameException;
36 import javax.management.ObjectName;
37 import javax.naming.directory.DirContext;
38 import javax.servlet.ServletException;
39
40 import org.apache.catalina.Cluster;
41 import org.apache.catalina.Container;
42 import org.apache.catalina.ContainerEvent;
43 import org.apache.catalina.ContainerListener;
44 import org.apache.catalina.Globals;
45 import org.apache.catalina.Lifecycle;
46 import org.apache.catalina.LifecycleException;
47 import org.apache.catalina.LifecycleListener;
48 import org.apache.catalina.Loader;
49 import org.apache.catalina.Manager;
50 import org.apache.catalina.Pipeline;
51 import org.apache.catalina.Realm;
52 import org.apache.catalina.Valve;
53 import org.apache.catalina.connector.Request;
54 import org.apache.catalina.connector.Response;
55 import org.apache.catalina.util.LifecycleSupport;
56 import org.apache.catalina.util.StringManager;
57 import org.apache.juli.logging.Log;
58 import org.apache.juli.logging.LogFactory;
59 import org.apache.naming.resources.ProxyDirContext;
60 import org.apache.tomcat.util.modeler.Registry;
61
62
63 /**
64 * Abstract implementation of the <b>Container</b> interface, providing common
65 * functionality required by nearly every implementation. Classes extending
66 * this base class must implement <code>getInfo()</code>, and may implement
67 * a replacement for <code>invoke()</code>.
68 * <p>
69 * All subclasses of this abstract base class will include support for a
70 * Pipeline object that defines the processing to be performed for each request
71 * received by the <code>invoke()</code> method of this class, utilizing the
72 * "Chain of Responsibility" design pattern. A subclass should encapsulate its
73 * own processing functionality as a <code>Valve</code>, and configure this
74 * Valve into the pipeline by calling <code>setBasic()</code>.
75 * <p>
76 * This implementation fires property change events, per the JavaBeans design
77 * pattern, for changes in singleton properties. In addition, it fires the
78 * following <code>ContainerEvent</code> events to listeners who register
79 * themselves with <code>addContainerListener()</code>:
80 * <table border=1>
81 * <tr>
82 * <th>Type</th>
83 * <th>Data</th>
84 * <th>Description</th>
85 * </tr>
86 * <tr>
87 * <td align=center><code>addChild</code></td>
88 * <td align=center><code>Container</code></td>
89 * <td>Child container added to this Container.</td>
90 * </tr>
91 * <tr>
92 * <td align=center><code>addValve</code></td>
93 * <td align=center><code>Valve</code></td>
94 * <td>Valve added to this Container.</td>
95 * </tr>
96 * <tr>
97 * <td align=center><code>removeChild</code></td>
98 * <td align=center><code>Container</code></td>
99 * <td>Child container removed from this Container.</td>
100 * </tr>
101 * <tr>
102 * <td align=center><code>removeValve</code></td>
103 * <td align=center><code>Valve</code></td>
104 * <td>Valve removed from this Container.</td>
105 * </tr>
106 * <tr>
107 * <td align=center><code>start</code></td>
108 * <td align=center><code>null</code></td>
109 * <td>Container was started.</td>
110 * </tr>
111 * <tr>
112 * <td align=center><code>stop</code></td>
113 * <td align=center><code>null</code></td>
114 * <td>Container was stopped.</td>
115 * </tr>
116 * </table>
117 * Subclasses that fire additional events should document them in the
118 * class comments of the implementation class.
119 *
120 * @author Craig R. McClanahan
121 */
122
123 public abstract class ContainerBase
124 implements Container, Lifecycle, Pipeline, MBeanRegistration, Serializable {
125
126 private static org.apache.juli.logging.Log log=
127 org.apache.juli.logging.LogFactory.getLog( ContainerBase.class );
128
129 /**
130 * Perform addChild with the permissions of this class.
131 * addChild can be called with the XML parser on the stack,
132 * this allows the XML parser to have fewer privileges than
133 * Tomcat.
134 */
135 protected class PrivilegedAddChild
136 implements PrivilegedAction {
137
138 private Container child;
139
140 PrivilegedAddChild(Container child) {
141 this.child = child;
142 }
143
144 public Object run() {
145 addChildInternal(child);
146 return null;
147 }
148
149 }
150
151
152 // ----------------------------------------------------- Instance Variables
153
154
155 /**
156 * The child Containers belonging to this Container, keyed by name.
157 */
158 protected HashMap children = new HashMap();
159
160
161 /**
162 * The processor delay for this component.
163 */
164 protected int backgroundProcessorDelay = -1;
165
166
167 /**
168 * The lifecycle event support for this component.
169 */
170 protected LifecycleSupport lifecycle = new LifecycleSupport(this);
171
172
173 /**
174 * The container event listeners for this Container.
175 */
176 protected ArrayList listeners = new ArrayList();
177
178
179 /**
180 * The Loader implementation with which this Container is associated.
181 */
182 protected Loader loader = null;
183
184
185 /**
186 * The Logger implementation with which this Container is associated.
187 */
188 protected Log logger = null;
189
190
191 /**
192 * Associated logger name.
193 */
194 protected String logName = null;
195
196
197 /**
198 * The Manager implementation with which this Container is associated.
199 */
200 protected Manager manager = null;
201
202
203 /**
204 * The cluster with which this Container is associated.
205 */
206 protected Cluster cluster = null;
207
208
209 /**
210 * The human-readable name of this Container.
211 */
212 protected String name = null;
213
214
215 /**
216 * The parent Container to which this Container is a child.
217 */
218 protected Container parent = null;
219
220
221 /**
222 * The parent class loader to be configured when we install a Loader.
223 */
224 protected ClassLoader parentClassLoader = null;
225
226
227 /**
228 * The Pipeline object with which this Container is associated.
229 */
230 protected Pipeline pipeline = new StandardPipeline(this);
231
232
233 /**
234 * The Realm with which this Container is associated.
235 */
236 protected Realm realm = null;
237
238
239 /**
240 * The resources DirContext object with which this Container is associated.
241 */
242 protected DirContext resources = null;
243
244
245 /**
246 * The string manager for this package.
247 */
248 protected static StringManager sm =
249 StringManager.getManager(Constants.Package);
250
251
252 /**
253 * Has this component been started?
254 */
255 protected boolean started = false;
256
257 protected boolean initialized=false;
258
259 /**
260 * Will children be started automatically when they are added.
261 */
262 protected boolean startChildren = true;
263
264 /**
265 * The property change support for this component.
266 */
267 protected PropertyChangeSupport support = new PropertyChangeSupport(this);
268
269
270 /**
271 * The background thread.
272 */
273 private Thread thread = null;
274
275
276 /**
277 * The background thread completion semaphore.
278 */
279 private boolean threadDone = false;
280
281
282 // ------------------------------------------------------------- Properties
283
284
285 /**
286 * Get the delay between the invocation of the backgroundProcess method on
287 * this container and its children. Child containers will not be invoked
288 * if their delay value is not negative (which would mean they are using
289 * their own thread). Setting this to a positive value will cause
290 * a thread to be spawn. After waiting the specified amount of time,
291 * the thread will invoke the executePeriodic method on this container
292 * and all its children.
293 */
294 public int getBackgroundProcessorDelay() {
295 return backgroundProcessorDelay;
296 }
297
298
299 /**
300 * Set the delay between the invocation of the execute method on this
301 * container and its children.
302 *
303 * @param delay The delay in seconds between the invocation of
304 * backgroundProcess methods
305 */
306 public void setBackgroundProcessorDelay(int delay) {
307 backgroundProcessorDelay = delay;
308 }
309
310
311 /**
312 * Return descriptive information about this Container implementation and
313 * the corresponding version number, in the format
314 * <code><description>/<version></code>.
315 */
316 public String getInfo() {
317 return this.getClass().getName();
318 }
319
320
321 /**
322 * Return the Loader with which this Container is associated. If there is
323 * no associated Loader, return the Loader associated with our parent
324 * Container (if any); otherwise, return <code>null</code>.
325 */
326 public Loader getLoader() {
327
328 if (loader != null)
329 return (loader);
330 if (parent != null)
331 return (parent.getLoader());
332 return (null);
333
334 }
335
336
337 /**
338 * Set the Loader with which this Container is associated.
339 *
340 * @param loader The newly associated loader
341 */
342 public synchronized void setLoader(Loader loader) {
343
344 // Change components if necessary
345 Loader oldLoader = this.loader;
346 if (oldLoader == loader)
347 return;
348 this.loader = loader;
349
350 // Stop the old component if necessary
351 if (started && (oldLoader != null) &&
352 (oldLoader instanceof Lifecycle)) {
353 try {
354 ((Lifecycle) oldLoader).stop();
355 } catch (LifecycleException e) {
356 log.error("ContainerBase.setLoader: stop: ", e);
357 }
358 }
359
360 // Start the new component if necessary
361 if (loader != null)
362 loader.setContainer(this);
363 if (started && (loader != null) &&
364 (loader instanceof Lifecycle)) {
365 try {
366 ((Lifecycle) loader).start();
367 } catch (LifecycleException e) {
368 log.error("ContainerBase.setLoader: start: ", e);
369 }
370 }
371
372 // Report this property change to interested listeners
373 support.firePropertyChange("loader", oldLoader, this.loader);
374
375 }
376
377
378 /**
379 * Return the Logger with which this Container is associated. If there is
380 * no associated Logger, return the Logger associated with our parent
381 * Container (if any); otherwise return <code>null</code>.
382 */
383 public Log getLogger() {
384
385 if (logger != null)
386 return (logger);
387 logger = LogFactory.getLog(logName());
388 return (logger);
389
390 }
391
392
393 /**
394 * Return the Manager with which this Container is associated. If there is
395 * no associated Manager, return the Manager associated with our parent
396 * Container (if any); otherwise return <code>null</code>.
397 */
398 public Manager getManager() {
399
400 if (manager != null)
401 return (manager);
402 if (parent != null)
403 return (parent.getManager());
404 return (null);
405
406 }
407
408
409 /**
410 * Set the Manager with which this Container is associated.
411 *
412 * @param manager The newly associated Manager
413 */
414 public synchronized void setManager(Manager manager) {
415
416 // Change components if necessary
417 Manager oldManager = this.manager;
418 if (oldManager == manager)
419 return;
420 this.manager = manager;
421
422 // Stop the old component if necessary
423 if (started && (oldManager != null) &&
424 (oldManager instanceof Lifecycle)) {
425 try {
426 ((Lifecycle) oldManager).stop();
427 } catch (LifecycleException e) {
428 log.error("ContainerBase.setManager: stop: ", e);
429 }
430 }
431
432 // Start the new component if necessary
433 if (manager != null)
434 manager.setContainer(this);
435 if (started && (manager != null) &&
436 (manager instanceof Lifecycle)) {
437 try {
438 ((Lifecycle) manager).start();
439 } catch (LifecycleException e) {
440 log.error("ContainerBase.setManager: start: ", e);
441 }
442 }
443
444 // Report this property change to interested listeners
445 support.firePropertyChange("manager", oldManager, this.manager);
446
447 }
448
449
450 /**
451 * Return an object which may be utilized for mapping to this component.
452 */
453 public Object getMappingObject() {
454 return this;
455 }
456
457
458 /**
459 * Return the Cluster with which this Container is associated. If there is
460 * no associated Cluster, return the Cluster associated with our parent
461 * Container (if any); otherwise return <code>null</code>.
462 */
463 public Cluster getCluster() {
464 if (cluster != null)
465 return (cluster);
466
467 if (parent != null)
468 return (parent.getCluster());
469
470 return (null);
471 }
472
473
474 /**
475 * Set the Cluster with which this Container is associated.
476 *
477 * @param cluster The newly associated Cluster
478 */
479 public synchronized void setCluster(Cluster cluster) {
480 // Change components if necessary
481 Cluster oldCluster = this.cluster;
482 if (oldCluster == cluster)
483 return;
484 this.cluster = cluster;
485
486 // Stop the old component if necessary
487 if (started && (oldCluster != null) &&
488 (oldCluster instanceof Lifecycle)) {
489 try {
490 ((Lifecycle) oldCluster).stop();
491 } catch (LifecycleException e) {
492 log.error("ContainerBase.setCluster: stop: ", e);
493 }
494 }
495
496 // Start the new component if necessary
497 if (cluster != null)
498 cluster.setContainer(this);
499
500 if (started && (cluster != null) &&
501 (cluster instanceof Lifecycle)) {
502 try {
503 ((Lifecycle) cluster).start();
504 } catch (LifecycleException e) {
505 log.error("ContainerBase.setCluster: start: ", e);
506 }
507 }
508
509 // Report this property change to interested listeners
510 support.firePropertyChange("cluster", oldCluster, this.cluster);
511 }
512
513
514 /**
515 * Return a name string (suitable for use by humans) that describes this
516 * Container. Within the set of child containers belonging to a particular
517 * parent, Container names must be unique.
518 */
519 public String getName() {
520
521 return (name);
522
523 }
524
525
526 /**
527 * Set a name string (suitable for use by humans) that describes this
528 * Container. Within the set of child containers belonging to a particular
529 * parent, Container names must be unique.
530 *
531 * @param name New name of this container
532 *
533 * @exception IllegalStateException if this Container has already been
534 * added to the children of a parent Container (after which the name
535 * may not be changed)
536 */
537 public void setName(String name) {
538
539 String oldName = this.name;
540 this.name = name;
541 support.firePropertyChange("name", oldName, this.name);
542 }
543
544
545 /**
546 * Return if children of this container will be started automatically when
547 * they are added to this container.
548 */
549 public boolean getStartChildren() {
550
551 return (startChildren);
552
553 }
554
555
556 /**
557 * Set if children of this container will be started automatically when
558 * they are added to this container.
559 *
560 * @param startChildren New value of the startChildren flag
561 */
562 public void setStartChildren(boolean startChildren) {
563
564 boolean oldStartChildren = this.startChildren;
565 this.startChildren = startChildren;
566 support.firePropertyChange("startChildren", oldStartChildren, this.startChildren);
567 }
568
569
570 /**
571 * Return the Container for which this Container is a child, if there is
572 * one. If there is no defined parent, return <code>null</code>.
573 */
574 public Container getParent() {
575
576 return (parent);
577
578 }
579
580
581 /**
582 * Set the parent Container to which this Container is being added as a
583 * child. This Container may refuse to become attached to the specified
584 * Container by throwing an exception.
585 *
586 * @param container Container to which this Container is being added
587 * as a child
588 *
589 * @exception IllegalArgumentException if this Container refuses to become
590 * attached to the specified Container
591 */
592 public void setParent(Container container) {
593
594 Container oldParent = this.parent;
595 this.parent = container;
596 support.firePropertyChange("parent", oldParent, this.parent);
597
598 }
599
600
601 /**
602 * Return the parent class loader (if any) for this web application.
603 * This call is meaningful only <strong>after</strong> a Loader has
604 * been configured.
605 */
606 public ClassLoader getParentClassLoader() {
607 if (parentClassLoader != null)
608 return (parentClassLoader);
609 if (parent != null) {
610 return (parent.getParentClassLoader());
611 }
612 return (ClassLoader.getSystemClassLoader());
613
614 }
615
616
617 /**
618 * Set the parent class loader (if any) for this web application.
619 * This call is meaningful only <strong>before</strong> a Loader has
620 * been configured, and the specified value (if non-null) should be
621 * passed as an argument to the class loader constructor.
622 *
623 *
624 * @param parent The new parent class loader
625 */
626 public void setParentClassLoader(ClassLoader parent) {
627 ClassLoader oldParentClassLoader = this.parentClassLoader;
628 this.parentClassLoader = parent;
629 support.firePropertyChange("parentClassLoader", oldParentClassLoader,
630 this.parentClassLoader);
631
632 }
633
634
635 /**
636 * Return the Pipeline object that manages the Valves associated with
637 * this Container.
638 */
639 public Pipeline getPipeline() {
640
641 return (this.pipeline);
642
643 }
644
645
646 /**
647 * Return the Realm with which this Container is associated. If there is
648 * no associated Realm, return the Realm associated with our parent
649 * Container (if any); otherwise return <code>null</code>.
650 */
651 public Realm getRealm() {
652
653 if (realm != null)
654 return (realm);
655 if (parent != null)
656 return (parent.getRealm());
657 return (null);
658
659 }
660
661
662 /**
663 * Set the Realm with which this Container is associated.
664 *
665 * @param realm The newly associated Realm
666 */
667 public synchronized void setRealm(Realm realm) {
668
669 // Change components if necessary
670 Realm oldRealm = this.realm;
671 if (oldRealm == realm)
672 return;
673 this.realm = realm;
674
675 // Stop the old component if necessary
676 if (started && (oldRealm != null) &&
677 (oldRealm instanceof Lifecycle)) {
678 try {
679 ((Lifecycle) oldRealm).stop();
680 } catch (LifecycleException e) {
681 log.error("ContainerBase.setRealm: stop: ", e);
682 }
683 }
684
685 // Start the new component if necessary
686 if (realm != null)
687 realm.setContainer(this);
688 if (started && (realm != null) &&
689 (realm instanceof Lifecycle)) {
690 try {
691 ((Lifecycle) realm).start();
692 } catch (LifecycleException e) {
693 log.error("ContainerBase.setRealm: start: ", e);
694 }
695 }
696
697 // Report this property change to interested listeners
698 support.firePropertyChange("realm", oldRealm, this.realm);
699
700 }
701
702
703 /**
704 * Return the resources DirContext object with which this Container is
705 * associated. If there is no associated resources object, return the
706 * resources associated with our parent Container (if any); otherwise
707 * return <code>null</code>.
708 */
709 public DirContext getResources() {
710 if (resources != null)
711 return (resources);
712 if (parent != null)
713 return (parent.getResources());
714 return (null);
715
716 }
717
718
719 /**
720 * Set the resources DirContext object with which this Container is
721 * associated.
722 *
723 * @param resources The newly associated DirContext
724 */
725 public synchronized void setResources(DirContext resources) {
726 // Called from StandardContext.setResources()
727 // <- StandardContext.start()
728 // <- ContainerBase.addChildInternal()
729
730 // Change components if necessary
731 DirContext oldResources = this.resources;
732 if (oldResources == resources)
733 return;
734 Hashtable env = new Hashtable();
735 if (getParent() != null)
736 env.put(ProxyDirContext.HOST, getParent().getName());
737 env.put(ProxyDirContext.CONTEXT, getName());
738 this.resources = new ProxyDirContext(env, resources);
739 // Report this property change to interested listeners
740 support.firePropertyChange("resources", oldResources, this.resources);
741
742 }
743
744
745 // ------------------------------------------------------ Container Methods
746
747
748 /**
749 * Add a new child Container to those associated with this Container,
750 * if supported. Prior to adding this Container to the set of children,
751 * the child's <code>setParent()</code> method must be called, with this
752 * Container as an argument. This method may thrown an
753 * <code>IllegalArgumentException</code> if this Container chooses not
754 * to be attached to the specified Container, in which case it is not added
755 *
756 * @param child New child Container to be added
757 *
758 * @exception IllegalArgumentException if this exception is thrown by
759 * the <code>setParent()</code> method of the child Container
760 * @exception IllegalArgumentException if the new child does not have
761 * a name unique from that of existing children of this Container
762 * @exception IllegalStateException if this Container does not support
763 * child Containers
764 */
765 public void addChild(Container child) {
766 if (Globals.IS_SECURITY_ENABLED) {
767 PrivilegedAction dp =
768 new PrivilegedAddChild(child);
769 AccessController.doPrivileged(dp);
770 } else {
771 addChildInternal(child);
772 }
773 }
774
775 private void addChildInternal(Container child) {
776
777 if( log.isDebugEnabled() )
778 log.debug("Add child " + child + " " + this);
779 synchronized(children) {
780 if (children.get(child.getName()) != null)
781 throw new IllegalArgumentException("addChild: Child name '" +
782 child.getName() +
783 "' is not unique");
784 child.setParent(this); // May throw IAE
785 children.put(child.getName(), child);
786
787 // Start child
788 if (started && startChildren && (child instanceof Lifecycle)) {
789 boolean success = false;
790 try {
791 ((Lifecycle) child).start();
792 success = true;
793 } catch (LifecycleException e) {
794 log.error("ContainerBase.addChild: start: ", e);
795 throw new IllegalStateException
796 ("ContainerBase.addChild: start: " + e);
797 } finally {
798 if (!success) {
799 children.remove(child.getName());
800 }
801 }
802 }
803
804 fireContainerEvent(ADD_CHILD_EVENT, child);
805 }
806
807 }
808
809
810 /**
811 * Add a container event listener to this component.
812 *
813 * @param listener The listener to add
814 */
815 public void addContainerListener(ContainerListener listener) {
816
817 synchronized (listeners) {
818 listeners.add(listener);
819 }
820
821 }
822
823
824 /**
825 * Add a property change listener to this component.
826 *
827 * @param listener The listener to add
828 */
829 public void addPropertyChangeListener(PropertyChangeListener listener) {
830
831 support.addPropertyChangeListener(listener);
832
833 }
834
835
836 /**
837 * Return the child Container, associated with this Container, with
838 * the specified name (if any); otherwise, return <code>null</code>
839 *
840 * @param name Name of the child Container to be retrieved
841 */
842 public Container findChild(String name) {
843
844 if (name == null)
845 return (null);
846 synchronized (children) { // Required by post-start changes
847 return ((Container) children.get(name));
848 }
849
850 }
851
852
853 /**
854 * Return the set of children Containers associated with this Container.
855 * If this Container has no children, a zero-length array is returned.
856 */
857 public Container[] findChildren() {
858
859 synchronized (children) {
860 Container results[] = new Container[children.size()];
861 return ((Container[]) children.values().toArray(results));
862 }
863
864 }
865
866
867 /**
868 * Return the set of container listeners associated with this Container.
869 * If this Container has no registered container listeners, a zero-length
870 * array is returned.
871 */
872 public ContainerListener[] findContainerListeners() {
873
874 synchronized (listeners) {
875 ContainerListener[] results =
876 new ContainerListener[listeners.size()];
877 return ((ContainerListener[]) listeners.toArray(results));
878 }
879
880 }
881
882
883 /**
884 * Process the specified Request, to produce the corresponding Response,
885 * by invoking the first Valve in our pipeline (if any), or the basic
886 * Valve otherwise.
887 *
888 * @param request Request to be processed
889 * @param response Response to be produced
890 *
891 * @exception IllegalStateException if neither a pipeline or a basic
892 * Valve have been configured for this Container
893 * @exception IOException if an input/output error occurred while
894 * processing
895 * @exception ServletException if a ServletException was thrown
896 * while processing this request
897 */
898 public void invoke(Request request, Response response)
899 throws IOException, ServletException {
900
901 pipeline.getFirst().invoke(request, response);
902
903 }
904
905
906 /**
907 * Remove an existing child Container from association with this parent
908 * Container.
909 *
910 * @param child Existing child Container to be removed
911 */
912 public void removeChild(Container child) {
913
914 synchronized(children) {
915 if (children.get(child.getName()) == null)
916 return;
917 children.remove(child.getName());
918 }
919
920 if (started && (child instanceof Lifecycle)) {
921 try {
922 if( child instanceof ContainerBase ) {
923 if( ((ContainerBase)child).started ) {
924 ((Lifecycle) child).stop();
925 }
926 } else {
927 ((Lifecycle) child).stop();
928 }
929 } catch (LifecycleException e) {
930 log.error("ContainerBase.removeChild: stop: ", e);
931 }
932 }
933
934 fireContainerEvent(REMOVE_CHILD_EVENT, child);
935
936 // child.setParent(null);
937
938 }
939
940
941 /**
942 * Remove a container event listener from this component.
943 *
944 * @param listener The listener to remove
945 */
946 public void removeContainerListener(ContainerListener listener) {
947
948 synchronized (listeners) {
949 listeners.remove(listener);
950 }
951
952 }
953
954
955 /**
956 * Remove a property change listener from this component.
957 *
958 * @param listener The listener to remove
959 */
960 public void removePropertyChangeListener(PropertyChangeListener listener) {
961
962 support.removePropertyChangeListener(listener);
963
964 }
965
966
967 // ------------------------------------------------------ Lifecycle Methods
968
969
970 /**
971 * Add a lifecycle event listener to this component.
972 *
973 * @param listener The listener to add
974 */
975 public void addLifecycleListener(LifecycleListener listener) {
976
977 lifecycle.addLifecycleListener(listener);
978
979 }
980
981
982 /**
983 * Get the lifecycle listeners associated with this lifecycle. If this
984 * Lifecycle has no listeners registered, a zero-length array is returned.
985 */
986 public LifecycleListener[] findLifecycleListeners() {
987
988 return lifecycle.findLifecycleListeners();
989
990 }
991
992
993 /**
994 * Remove a lifecycle event listener from this component.
995 *
996 * @param listener The listener to remove
997 */
998 public void removeLifecycleListener(LifecycleListener listener) {
999
1000 lifecycle.removeLifecycleListener(listener);
1001
1002 }
1003
1004
1005 /**
1006 * Prepare for active use of the public methods of this Component.
1007 *
1008 * @exception LifecycleException if this component detects a fatal error
1009 * that prevents it from being started
1010 */
1011 public synchronized void start() throws LifecycleException {
1012
1013 // Validate and update our current component state
1014 if (started) {
1015 if(log.isInfoEnabled())
1016 log.info(sm.getString("containerBase.alreadyStarted", logName()));
1017 return;
1018 }
1019
1020 // Notify our interested LifecycleListeners
1021 lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
1022
1023 started = true;
1024
1025 // Start our subordinate components, if any
1026 if ((loader != null) && (loader instanceof Lifecycle))
1027 ((Lifecycle) loader).start();
1028 logger = null;
1029 getLogger();
1030 if ((logger != null) && (logger instanceof Lifecycle))
1031 ((Lifecycle) logger).start();
1032 if ((manager != null) && (manager instanceof Lifecycle))
1033 ((Lifecycle) manager).start();
1034 if ((cluster != null) && (cluster instanceof Lifecycle))
1035 ((Lifecycle) cluster).start();
1036 if ((realm != null) && (realm instanceof Lifecycle))
1037 ((Lifecycle) realm).start();
1038 if ((resources != null) && (resources instanceof Lifecycle))
1039 ((Lifecycle) resources).start();
1040
1041 // Start our child containers, if any
1042 Container children[] = findChildren();
1043 for (int i = 0; i < children.length; i++) {
1044 if (children[i] instanceof Lifecycle)
1045 ((Lifecycle) children[i]).start();
1046 }
1047
1048 // Start the Valves in our pipeline (including the basic), if any
1049 if (pipeline instanceof Lifecycle)
1050 ((Lifecycle) pipeline).start();
1051
1052 // Notify our interested LifecycleListeners
1053 lifecycle.fireLifecycleEvent(START_EVENT, null);
1054
1055 // Start our thread
1056 threadStart();
1057
1058 // Notify our interested LifecycleListeners
1059 lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
1060
1061 }
1062
1063
1064 /**
1065 * Gracefully shut down active use of the public methods of this Component.
1066 *
1067 * @exception LifecycleException if this component detects a fatal error
1068 * that needs to be reported
1069 */
1070 public synchronized void stop() throws LifecycleException {
1071
1072 // Validate and update our current component state
1073 if (!started) {
1074 if(log.isInfoEnabled())
1075 log.info(sm.getString("containerBase.notStarted", logName()));
1076 return;
1077 }
1078
1079 // Notify our interested LifecycleListeners
1080 lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
1081
1082 // Stop our thread
1083 threadStop();
1084
1085 // Notify our interested LifecycleListeners
1086 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
1087 started = false;
1088
1089 // Stop the Valves in our pipeline (including the basic), if any
1090 if (pipeline instanceof Lifecycle) {
1091 ((Lifecycle) pipeline).stop();
1092 }
1093
1094 // Stop our child containers, if any
1095 Container children[] = findChildren();
1096 for (int i = 0; i < children.length; i++) {
1097 if (children[i] instanceof Lifecycle)
1098 ((Lifecycle) children[i]).stop();
1099 }
1100 // Remove children - so next start can work
1101 children = findChildren();
1102 for (int i = 0; i < children.length; i++) {
1103 removeChild(children[i]);
1104 }
1105
1106 // Stop our subordinate components, if any
1107 if ((resources != null) && (resources instanceof Lifecycle)) {
1108 ((Lifecycle) resources).stop();
1109 }
1110 if ((realm != null) && (realm instanceof Lifecycle)) {
1111 ((Lifecycle) realm).stop();
1112 }
1113 if ((cluster != null) && (cluster instanceof Lifecycle)) {
1114 ((Lifecycle) cluster).stop();
1115 }
1116 if ((manager != null) && (manager instanceof Lifecycle)) {
1117 ((Lifecycle) manager).stop();
1118 }
1119 if ((logger != null) && (logger instanceof Lifecycle)) {
1120 ((Lifecycle) logger).stop();
1121 }
1122 if ((loader != null) && (loader instanceof Lifecycle)) {
1123 ((Lifecycle) loader).stop();
1124 }
1125
1126 // Notify our interested LifecycleListeners
1127 lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
1128
1129 }
1130
1131 /** Init method, part of the MBean lifecycle.
1132 * If the container was added via JMX, it'll register itself with the
1133 * parent, using the ObjectName conventions to locate the parent.
1134 *
1135 * If the container was added directly and it doesn't have an ObjectName,
1136 * it'll create a name and register itself with the JMX console. On destroy(),
1137 * the object will unregister.
1138 *
1139 * @throws Exception
1140 */
1141 public void init() throws Exception {
1142
1143 if( this.getParent() == null ) {
1144 // "Life" update
1145 ObjectName parentName=getParentName();
1146
1147 //log.info("Register " + parentName );
1148 if( parentName != null &&
1149 mserver.isRegistered(parentName))
1150 {
1151 mserver.invoke(parentName, "addChild", new Object[] { this },
1152 new String[] {"org.apache.catalina.Container"});
1153 }
1154 }
1155 initialized=true;
1156 }
1157
1158 public ObjectName getParentName() throws MalformedObjectNameException {
1159 return null;
1160 }
1161
1162 public void destroy() throws Exception {
1163 if( started ) {
1164 stop();
1165 }
1166 initialized=false;
1167
1168 // unregister this component
1169 if ( oname != null ) {
1170 try {
1171 if( controller == oname ) {
1172 Registry.getRegistry(null, null)
1173 .unregisterComponent(oname);
1174 if(log.isDebugEnabled())
1175 log.debug("unregistering " + oname);
1176 }
1177 } catch( Throwable t ) {
1178 log.error("Error unregistering ", t );
1179 }
1180 }
1181
1182 if (parent != null) {
1183 parent.removeChild(this);
1184 }
1185
1186 // Stop our child containers, if any
1187 Container children[] = findChildren();
1188 for (int i = 0; i < children.length; i++) {
1189 removeChild(children[i]);
1190 }
1191
1192 }
1193
1194 // ------------------------------------------------------- Pipeline Methods
1195
1196
1197 /**
1198 * Add a new Valve to the end of the pipeline associated with this
1199 * Container. Prior to adding the Valve, the Valve's
1200 * <code>setContainer</code> method must be called, with this Container
1201 * as an argument. The method may throw an
1202 * <code>IllegalArgumentException</code> if this Valve chooses not to
1203 * be associated with this Container, or <code>IllegalStateException</code>
1204 * if it is already associated with a different Container.
1205 *
1206 * @param valve Valve to be added
1207 *
1208 * @exception IllegalArgumentException if this Container refused to
1209 * accept the specified Valve
1210 * @exception IllegalArgumentException if the specifie Valve refuses to be
1211 * associated with this Container
1212 * @exception IllegalStateException if the specified Valve is already
1213 * associated with a different Container
1214 */
1215 public synchronized void addValve(Valve valve) {
1216
1217 pipeline.addValve(valve);
1218 fireContainerEvent(ADD_VALVE_EVENT, valve);
1219 }
1220
1221 public ObjectName[] getValveObjectNames() {
1222 return ((StandardPipeline)pipeline).getValveObjectNames();
1223 }
1224
1225 /**
1226 * <p>Return the Valve instance that has been distinguished as the basic
1227 * Valve for this Pipeline (if any).
1228 */
1229 public Valve getBasic() {
1230
1231 return (pipeline.getBasic());
1232
1233 }
1234
1235
1236 /**
1237 * Return the first valve in the pipeline.
1238 */
1239 public Valve getFirst() {
1240
1241 return (pipeline.getFirst());
1242
1243 }
1244
1245
1246 /**
1247 * Return the set of Valves in the pipeline associated with this
1248 * Container, including the basic Valve (if any). If there are no
1249 * such Valves, a zero-length array is returned.
1250 */
1251 public Valve[] getValves() {
1252
1253 return (pipeline.getValves());
1254
1255 }
1256
1257
1258 /**
1259 * Remove the specified Valve from the pipeline associated with this
1260 * Container, if it is found; otherwise, do nothing.
1261 *
1262 * @param valve Valve to be removed
1263 */
1264 public synchronized void removeValve(Valve valve) {
1265
1266 pipeline.removeValve(valve);
1267 fireContainerEvent(REMOVE_VALVE_EVENT, valve);
1268 }
1269
1270
1271 /**
1272 * <p>Set the Valve instance that has been distinguished as the basic
1273 * Valve for this Pipeline (if any). Prioer to setting the basic Valve,
1274 * the Valve's <code>setContainer()</code> will be called, if it
1275 * implements <code>Contained</code>, with the owning Container as an
1276 * argument. The method may throw an <code>IllegalArgumentException</code>
1277 * if this Valve chooses not to be associated with this Container, or
1278 * <code>IllegalStateException</code> if it is already associated with
1279 * a different Container.</p>
1280 *
1281 * @param valve Valve to be distinguished as the basic Valve
1282 */
1283 public void setBasic(Valve valve) {
1284
1285 pipeline.setBasic(valve);
1286
1287 }
1288
1289
1290 /**
1291 * Execute a periodic task, such as reloading, etc. This method will be
1292 * invoked inside the classloading context of this container. Unexpected
1293 * throwables will be caught and logged.
1294 */
1295 public void backgroundProcess() {
1296
1297 if (!started)
1298 return;
1299
1300 if (cluster != null) {
1301 try {
1302 cluster.backgroundProcess();
1303 } catch (Exception e) {
1304 log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);
1305 }
1306 }
1307 if (loader != null) {
1308 try {
1309 loader.backgroundProcess();
1310 } catch (Exception e) {
1311 log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);
1312 }
1313 }
1314 if (manager != null) {
1315 try {
1316 manager.backgroundProcess();
1317 } catch (Exception e) {
1318 log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e);
1319 }
1320 }
1321 if (realm != null) {
1322 try {
1323 realm.backgroundProcess();
1324 } catch (Exception e) {
1325 log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);
1326 }
1327 }
1328 Valve current = pipeline.getFirst();
1329 while (current != null) {
1330 try {
1331 current.backgroundProcess();
1332 } catch (Exception e) {
1333 log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);
1334 }
1335 current = current.getNext();
1336 }
1337 lifecycle.fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
1338 }
1339
1340
1341 // ------------------------------------------------------ Protected Methods
1342
1343
1344 /**
1345 * Notify all container event listeners that a particular event has
1346 * occurred for this Container. The default implementation performs
1347 * this notification synchronously using the calling thread.
1348 *
1349 * @param type Event type
1350 * @param data Event data
1351 */
1352 public void fireContainerEvent(String type, Object data) {
1353
1354 if (listeners.size() < 1)
1355 return;
1356 ContainerEvent event = new ContainerEvent(this, type, data);
1357 ContainerListener list[] = new ContainerListener[0];
1358 synchronized (listeners) {
1359 list = (ContainerListener[]) listeners.toArray(list);
1360 }
1361 for (int i = 0; i < list.length; i++)
1362 ((ContainerListener) list[i]).containerEvent(event);
1363
1364 }
1365
1366
1367 /**
1368 * Return the abbreviated name of this container for logging messsages
1369 */
1370 protected String logName() {
1371
1372 if (logName != null) {
1373 return logName;
1374 }
1375 String loggerName = null;
1376 Container current = this;
1377 while (current != null) {
1378 String name = current.getName();
1379 if ((name == null) || (name.equals(""))) {
1380 name = "/";
1381 }
1382 loggerName = "[" + name + "]"
1383 + ((loggerName != null) ? ("." + loggerName) : "");
1384 current = current.getParent();
1385 }
1386 logName = ContainerBase.class.getName() + "." + loggerName;
1387 return logName;
1388
1389 }
1390
1391
1392 // -------------------- JMX and Registration --------------------
1393 protected String type;
1394 protected String domain;
1395 protected String suffix;
1396 protected ObjectName oname;
1397 protected ObjectName controller;
1398 protected transient MBeanServer mserver;
1399
1400 public ObjectName getJmxName() {
1401 return oname;
1402 }
1403
1404 public String getObjectName() {
1405 if (oname != null) {
1406 return oname.toString();
1407 } else return null;
1408 }
1409
1410 public String getDomain() {
1411 if( domain==null ) {
1412 Container parent=this;
1413 while( parent != null &&
1414 !( parent instanceof StandardEngine) ) {
1415 parent=parent.getParent();
1416 }
1417 if( parent instanceof StandardEngine ) {
1418 domain=((StandardEngine)parent).getDomain();
1419 }
1420 }
1421 return domain;
1422 }
1423
1424 public void setDomain(String domain) {
1425 this.domain=domain;
1426 }
1427
1428 public String getType() {
1429 return type;
1430 }
1431
1432 protected String getJSR77Suffix() {
1433 return suffix;
1434 }
1435
1436 public ObjectName preRegister(MBeanServer server,
1437 ObjectName name) throws Exception {
1438 oname=name;
1439 mserver=server;
1440 if (name == null ){
1441 return null;
1442 }
1443
1444 domain=name.getDomain();
1445
1446 type=name.getKeyProperty("type");
1447 if( type==null ) {
1448 type=name.getKeyProperty("j2eeType");
1449 }
1450
1451 String j2eeApp=name.getKeyProperty("J2EEApplication");
1452 String j2eeServer=name.getKeyProperty("J2EEServer");
1453 if( j2eeApp==null ) {
1454 j2eeApp="none";
1455 }
1456 if( j2eeServer==null ) {
1457 j2eeServer="none";
1458 }
1459 suffix=",J2EEApplication=" + j2eeApp + ",J2EEServer=" + j2eeServer;
1460 return name;
1461 }
1462
1463 public void postRegister(Boolean registrationDone) {
1464 }
1465
1466 public void preDeregister() throws Exception {
1467 }
1468
1469 public void postDeregister() {
1470 }
1471
1472 public ObjectName[] getChildren() {
1473 ObjectName result[]=new ObjectName[children.size()];
1474 Iterator it=children.values().iterator();
1475 int i=0;
1476 while( it.hasNext() ) {
1477 Object next=it.next();
1478 if( next instanceof ContainerBase ) {
1479 result[i++]=((ContainerBase)next).getJmxName();
1480 }
1481 }
1482 return result;
1483 }
1484
1485 public ObjectName createObjectName(String domain, ObjectName parent)
1486 throws Exception
1487 {
1488 if( log.isDebugEnabled())
1489 log.debug("Create ObjectName " + domain + " " + parent );
1490 return null;
1491 }
1492
1493 public String getContainerSuffix() {
1494 Container container=this;
1495 Container context=null;
1496 Container host=null;
1497 Container servlet=null;
1498
1499 StringBuffer suffix=new StringBuffer();
1500
1501 if( container instanceof StandardHost ) {
1502 host=container;
1503 } else if( container instanceof StandardContext ) {
1504 host=container.getParent();
1505 context=container;
1506 } else if( container instanceof StandardWrapper ) {
1507 context=container.getParent();
1508 host=context.getParent();
1509 servlet=container;
1510 }
1511 if( context!=null ) {
1512 String path=((StandardContext)context).getPath();
1513 suffix.append(",path=").append((path.equals("")) ? "/" : path);
1514 }
1515 if( host!=null ) suffix.append(",host=").append( host.getName() );
1516 if( servlet != null ) {
1517 String name=container.getName();
1518 suffix.append(",servlet=");
1519 suffix.append((name=="") ? "/" : name);
1520 }
1521 return suffix.toString();
1522 }
1523
1524
1525 /**
1526 * Start the background thread that will periodically check for
1527 * session timeouts.
1528 */
1529 protected void threadStart() {
1530
1531 if (thread != null)
1532 return;
1533 if (backgroundProcessorDelay <= 0)
1534 return;
1535
1536 threadDone = false;
1537 String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
1538 thread = new Thread(new ContainerBackgroundProcessor(), threadName);
1539 thread.setDaemon(true);
1540 thread.start();
1541
1542 }
1543
1544
1545 /**
1546 * Stop the background thread that is periodically checking for
1547 * session timeouts.
1548 */
1549 protected void threadStop() {
1550
1551 if (thread == null)
1552 return;
1553
1554 threadDone = true;
1555 thread.interrupt();
1556 try {
1557 thread.join();
1558 } catch (InterruptedException e) {
1559 ;
1560 }
1561
1562 thread = null;
1563
1564 }
1565
1566
1567 // -------------------------------------- ContainerExecuteDelay Inner Class
1568
1569
1570 /**
1571 * Private thread class to invoke the backgroundProcess method
1572 * of this container and its children after a fixed delay.
1573 */
1574 protected class ContainerBackgroundProcessor implements Runnable {
1575
1576 public void run() {
1577 while (!threadDone) {
1578 try {
1579 Thread.sleep(backgroundProcessorDelay * 1000L);
1580 } catch (InterruptedException e) {
1581 ;
1582 }
1583 if (!threadDone) {
1584 Container parent = (Container) getMappingObject();
1585 ClassLoader cl =
1586 Thread.currentThread().getContextClassLoader();
1587 if (parent.getLoader() != null) {
1588 cl = parent.getLoader().getClassLoader();
1589 }
1590 processChildren(parent, cl);
1591 }
1592 }
1593 }
1594
1595 protected void processChildren(Container container, ClassLoader cl) {
1596 try {
1597 if (container.getLoader() != null) {
1598 Thread.currentThread().setContextClassLoader
1599 (container.getLoader().getClassLoader());
1600 }
1601 container.backgroundProcess();
1602 } catch (Throwable t) {
1603 log.error("Exception invoking periodic operation: ", t);
1604 } finally {
1605 Thread.currentThread().setContextClassLoader(cl);
1606 }
1607 Container[] children = container.findChildren();
1608 for (int i = 0; i < children.length; i++) {
1609 if (children[i].getBackgroundProcessorDelay() <= 0) {
1610 processChildren(children[i], cl);
1611 }
1612 }
1613 }
1614
1615 }
1616
1617
1618 }