KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > core > ContainerBase


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 JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.Serializable JavaDoc;
26 import java.security.AccessController JavaDoc;
27 import java.security.PrivilegedAction JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Hashtable JavaDoc;
31 import java.util.Iterator JavaDoc;
32
33 import javax.management.MBeanRegistration JavaDoc;
34 import javax.management.MBeanServer JavaDoc;
35 import javax.management.MalformedObjectNameException JavaDoc;
36 import javax.management.ObjectName JavaDoc;
37 import javax.naming.directory.DirContext JavaDoc;
38 import javax.servlet.ServletException JavaDoc;
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.Lifecycle;
45 import org.apache.catalina.LifecycleException;
46 import org.apache.catalina.LifecycleListener;
47 import org.apache.catalina.Loader;
48 import org.apache.catalina.Manager;
49 import org.apache.catalina.Pipeline;
50 import org.apache.catalina.Realm;
51 import org.apache.catalina.Valve;
52 import org.apache.catalina.connector.Request;
53 import org.apache.catalina.connector.Response;
54 import org.apache.catalina.util.LifecycleSupport;
55 import org.apache.catalina.util.StringManager;
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58 import org.apache.naming.resources.ProxyDirContext;
59 import org.apache.tomcat.util.modeler.Registry;
60
61
62 /**
63  * Abstract implementation of the <b>Container</b> interface, providing common
64  * functionality required by nearly every implementation. Classes extending
65  * this base class must implement <code>getInfo()</code>, and may implement
66  * a replacement for <code>invoke()</code>.
67  * <p>
68  * All subclasses of this abstract base class will include support for a
69  * Pipeline object that defines the processing to be performed for each request
70  * received by the <code>invoke()</code> method of this class, utilizing the
71  * "Chain of Responsibility" design pattern. A subclass should encapsulate its
72  * own processing functionality as a <code>Valve</code>, and configure this
73  * Valve into the pipeline by calling <code>setBasic()</code>.
74  * <p>
75  * This implementation fires property change events, per the JavaBeans design
76  * pattern, for changes in singleton properties. In addition, it fires the
77  * following <code>ContainerEvent</code> events to listeners who register
78  * themselves with <code>addContainerListener()</code>:
79  * <table border=1>
80  * <tr>
81  * <th>Type</th>
82  * <th>Data</th>
83  * <th>Description</th>
84  * </tr>
85  * <tr>
86  * <td align=center><code>addChild</code></td>
87  * <td align=center><code>Container</code></td>
88  * <td>Child container added to this Container.</td>
89  * </tr>
90  * <tr>
91  * <td align=center><code>addValve</code></td>
92  * <td align=center><code>Valve</code></td>
93  * <td>Valve added to this Container.</td>
94  * </tr>
95  * <tr>
96  * <td align=center><code>removeChild</code></td>
97  * <td align=center><code>Container</code></td>
98  * <td>Child container removed from this Container.</td>
99  * </tr>
100  * <tr>
101  * <td align=center><code>removeValve</code></td>
102  * <td align=center><code>Valve</code></td>
103  * <td>Valve removed from this Container.</td>
104  * </tr>
105  * <tr>
106  * <td align=center><code>start</code></td>
107  * <td align=center><code>null</code></td>
108  * <td>Container was started.</td>
109  * </tr>
110  * <tr>
111  * <td align=center><code>stop</code></td>
112  * <td align=center><code>null</code></td>
113  * <td>Container was stopped.</td>
114  * </tr>
115  * </table>
116  * Subclasses that fire additional events should document them in the
117  * class comments of the implementation class.
118  *
119  * @author Craig R. McClanahan
120  */

121
122 public abstract class ContainerBase
123     implements Container, Lifecycle, Pipeline, MBeanRegistration JavaDoc, Serializable JavaDoc {
124
125     private static org.apache.commons.logging.Log log=
126         org.apache.commons.logging.LogFactory.getLog( ContainerBase.class );
127
128     /**
129      * Perform addChild with the permissions of this class.
130      * addChild can be called with the XML parser on the stack,
131      * this allows the XML parser to have fewer privileges than
132      * Tomcat.
133      */

134     protected class PrivilegedAddChild
135         implements PrivilegedAction JavaDoc {
136
137         private Container child;
138
139         PrivilegedAddChild(Container child) {
140             this.child = child;
141         }
142
143         public Object JavaDoc run() {
144             addChildInternal(child);
145             return null;
146         }
147
148     }
149
150
151     // ----------------------------------------------------- Instance Variables
152

153
154     /**
155      * The child Containers belonging to this Container, keyed by name.
156      */

157     protected HashMap JavaDoc children = new HashMap JavaDoc();
158
159
160     /**
161      * The processor delay for this component.
162      */

163     protected int backgroundProcessorDelay = -1;
164
165
166     /**
167      * The lifecycle event support for this component.
168      */

169     protected LifecycleSupport lifecycle = new LifecycleSupport(this);
170
171
172     /**
173      * The container event listeners for this Container.
174      */

175     protected ArrayList JavaDoc listeners = new ArrayList JavaDoc();
176
177
178     /**
179      * The Loader implementation with which this Container is associated.
180      */

181     protected Loader loader = null;
182
183
184     /**
185      * The Logger implementation with which this Container is associated.
186      */

187     protected Log logger = null;
188
189
190     /**
191      * Associated logger name.
192      */

193     protected String JavaDoc logName = null;
194     
195
196     /**
197      * The Manager implementation with which this Container is associated.
198      */

199     protected Manager manager = null;
200
201
202     /**
203      * The cluster with which this Container is associated.
204      */

205     protected Cluster cluster = null;
206
207     
208     /**
209      * The human-readable name of this Container.
210      */

211     protected String JavaDoc name = null;
212
213
214     /**
215      * The parent Container to which this Container is a child.
216      */

217     protected Container parent = null;
218
219
220     /**
221      * The parent class loader to be configured when we install a Loader.
222      */

223     protected ClassLoader JavaDoc parentClassLoader = null;
224
225
226     /**
227      * The Pipeline object with which this Container is associated.
228      */

229     protected Pipeline pipeline = new StandardPipeline(this);
230
231
232     /**
233      * The Realm with which this Container is associated.
234      */

235     protected Realm realm = null;
236
237
238     /**
239      * The resources DirContext object with which this Container is associated.
240      */

241     protected DirContext JavaDoc resources = null;
242
243
244     /**
245      * The string manager for this package.
246      */

247     protected static StringManager sm =
248         StringManager.getManager(Constants.Package);
249
250
251     /**
252      * Has this component been started?
253      */

254     protected boolean started = false;
255
256     protected boolean initialized=false;
257
258     /**
259      * The property change support for this component.
260      */

261     protected PropertyChangeSupport JavaDoc support = new PropertyChangeSupport JavaDoc(this);
262
263
264     /**
265      * The background thread.
266      */

267     private Thread JavaDoc thread = null;
268
269
270     /**
271      * The background thread completion semaphore.
272      */

273     private boolean threadDone = false;
274
275
276     // ------------------------------------------------------------- Properties
277

278
279     /**
280      * Get the delay between the invocation of the backgroundProcess method on
281      * this container and its children. Child containers will not be invoked
282      * if their delay value is not negative (which would mean they are using
283      * their own thread). Setting this to a positive value will cause
284      * a thread to be spawn. After waiting the specified amount of time,
285      * the thread will invoke the executePeriodic method on this container
286      * and all its children.
287      */

288     public int getBackgroundProcessorDelay() {
289         return backgroundProcessorDelay;
290     }
291
292
293     /**
294      * Set the delay between the invocation of the execute method on this
295      * container and its children.
296      *
297      * @param delay The delay in seconds between the invocation of
298      * backgroundProcess methods
299      */

300     public void setBackgroundProcessorDelay(int delay) {
301         backgroundProcessorDelay = delay;
302     }
303
304
305     /**
306      * Return descriptive information about this Container implementation and
307      * the corresponding version number, in the format
308      * <code>&lt;description&gt;/&lt;version&gt;</code>.
309      */

310     public String JavaDoc getInfo() {
311         return this.getClass().getName();
312     }
313
314
315     /**
316      * Return the Loader with which this Container is associated. If there is
317      * no associated Loader, return the Loader associated with our parent
318      * Container (if any); otherwise, return <code>null</code>.
319      */

320     public Loader getLoader() {
321
322         if (loader != null)
323             return (loader);
324         if (parent != null)
325             return (parent.getLoader());
326         return (null);
327
328     }
329
330
331     /**
332      * Set the Loader with which this Container is associated.
333      *
334      * @param loader The newly associated loader
335      */

336     public synchronized void setLoader(Loader loader) {
337
338         // Change components if necessary
339
Loader oldLoader = this.loader;
340         if (oldLoader == loader)
341             return;
342         this.loader = loader;
343
344         // Stop the old component if necessary
345
if (started && (oldLoader != null) &&
346             (oldLoader instanceof Lifecycle)) {
347             try {
348                 ((Lifecycle) oldLoader).stop();
349             } catch (LifecycleException e) {
350                 log.error("ContainerBase.setLoader: stop: ", e);
351             }
352         }
353
354         // Start the new component if necessary
355
if (loader != null)
356             loader.setContainer(this);
357         if (started && (loader != null) &&
358             (loader instanceof Lifecycle)) {
359             try {
360                 ((Lifecycle) loader).start();
361             } catch (LifecycleException e) {
362                 log.error("ContainerBase.setLoader: start: ", e);
363             }
364         }
365
366         // Report this property change to interested listeners
367
support.firePropertyChange("loader", oldLoader, this.loader);
368
369     }
370
371
372     /**
373      * Return the Logger with which this Container is associated. If there is
374      * no associated Logger, return the Logger associated with our parent
375      * Container (if any); otherwise return <code>null</code>.
376      */

377     public Log getLogger() {
378
379         if (logger != null)
380             return (logger);
381         logger = LogFactory.getLog(logName());
382         return (logger);
383
384     }
385
386
387     /**
388      * Return the Manager with which this Container is associated. If there is
389      * no associated Manager, return the Manager associated with our parent
390      * Container (if any); otherwise return <code>null</code>.
391      */

392     public Manager getManager() {
393
394         if (manager != null)
395             return (manager);
396         if (parent != null)
397             return (parent.getManager());
398         return (null);
399
400     }
401
402
403     /**
404      * Set the Manager with which this Container is associated.
405      *
406      * @param manager The newly associated Manager
407      */

408     public synchronized void setManager(Manager manager) {
409
410         // Change components if necessary
411
Manager oldManager = this.manager;
412         if (oldManager == manager)
413             return;
414         this.manager = manager;
415
416         // Stop the old component if necessary
417
if (started && (oldManager != null) &&
418             (oldManager instanceof Lifecycle)) {
419             try {
420                 ((Lifecycle) oldManager).stop();
421             } catch (LifecycleException e) {
422                 log.error("ContainerBase.setManager: stop: ", e);
423             }
424         }
425
426         // Start the new component if necessary
427
if (manager != null)
428             manager.setContainer(this);
429         if (started && (manager != null) &&
430             (manager instanceof Lifecycle)) {
431             try {
432                 ((Lifecycle) manager).start();
433             } catch (LifecycleException e) {
434                 log.error("ContainerBase.setManager: start: ", e);
435             }
436         }
437
438         // Report this property change to interested listeners
439
support.firePropertyChange("manager", oldManager, this.manager);
440
441     }
442
443
444     /**
445      * Return an object which may be utilized for mapping to this component.
446      */

447     public Object JavaDoc getMappingObject() {
448         return this;
449     }
450
451
452     /**
453      * Return the Cluster with which this Container is associated. If there is
454      * no associated Cluster, return the Cluster associated with our parent
455      * Container (if any); otherwise return <code>null</code>.
456      */

457     public Cluster getCluster() {
458         if (cluster != null)
459             return (cluster);
460
461         if (parent != null)
462             return (parent.getCluster());
463
464         return (null);
465     }
466
467
468     /**
469      * Set the Cluster with which this Container is associated.
470      *
471      * @param cluster The newly associated Cluster
472      */

473     public synchronized void setCluster(Cluster cluster) {
474         // Change components if necessary
475
Cluster oldCluster = this.cluster;
476         if (oldCluster == cluster)
477             return;
478         this.cluster = cluster;
479
480         // Stop the old component if necessary
481
if (started && (oldCluster != null) &&
482             (oldCluster instanceof Lifecycle)) {
483             try {
484                 ((Lifecycle) oldCluster).stop();
485             } catch (LifecycleException e) {
486                 log.error("ContainerBase.setCluster: stop: ", e);
487             }
488         }
489
490         // Start the new component if necessary
491
if (cluster != null)
492             cluster.setContainer(this);
493
494         if (started && (cluster != null) &&
495             (cluster instanceof Lifecycle)) {
496             try {
497                 ((Lifecycle) cluster).start();
498             } catch (LifecycleException e) {
499                 log.error("ContainerBase.setCluster: start: ", e);
500             }
501         }
502
503         // Report this property change to interested listeners
504
support.firePropertyChange("cluster", oldCluster, this.cluster);
505     }
506
507
508     /**
509      * Return a name string (suitable for use by humans) that describes this
510      * Container. Within the set of child containers belonging to a particular
511      * parent, Container names must be unique.
512      */

513     public String JavaDoc getName() {
514
515         return (name);
516
517     }
518
519
520     /**
521      * Set a name string (suitable for use by humans) that describes this
522      * Container. Within the set of child containers belonging to a particular
523      * parent, Container names must be unique.
524      *
525      * @param name New name of this container
526      *
527      * @exception IllegalStateException if this Container has already been
528      * added to the children of a parent Container (after which the name
529      * may not be changed)
530      */

531     public void setName(String JavaDoc name) {
532
533         String JavaDoc oldName = this.name;
534         this.name = name;
535         support.firePropertyChange("name", oldName, this.name);
536     }
537
538
539     /**
540      * Return the Container for which this Container is a child, if there is
541      * one. If there is no defined parent, return <code>null</code>.
542      */

543     public Container getParent() {
544
545         return (parent);
546
547     }
548
549
550     /**
551      * Set the parent Container to which this Container is being added as a
552      * child. This Container may refuse to become attached to the specified
553      * Container by throwing an exception.
554      *
555      * @param container Container to which this Container is being added
556      * as a child
557      *
558      * @exception IllegalArgumentException if this Container refuses to become
559      * attached to the specified Container
560      */

561     public void setParent(Container container) {
562
563         Container oldParent = this.parent;
564         this.parent = container;
565         support.firePropertyChange("parent", oldParent, this.parent);
566
567     }
568
569
570     /**
571      * Return the parent class loader (if any) for this web application.
572      * This call is meaningful only <strong>after</strong> a Loader has
573      * been configured.
574      */

575     public ClassLoader JavaDoc getParentClassLoader() {
576         if (parentClassLoader != null)
577             return (parentClassLoader);
578         if (parent != null) {
579             return (parent.getParentClassLoader());
580         }
581         return (ClassLoader.getSystemClassLoader());
582
583     }
584
585
586     /**
587      * Set the parent class loader (if any) for this web application.
588      * This call is meaningful only <strong>before</strong> a Loader has
589      * been configured, and the specified value (if non-null) should be
590      * passed as an argument to the class loader constructor.
591      *
592      *
593      * @param parent The new parent class loader
594      */

595     public void setParentClassLoader(ClassLoader JavaDoc parent) {
596         ClassLoader JavaDoc oldParentClassLoader = this.parentClassLoader;
597         this.parentClassLoader = parent;
598         support.firePropertyChange("parentClassLoader", oldParentClassLoader,
599                                    this.parentClassLoader);
600
601     }
602
603
604     /**
605      * Return the Pipeline object that manages the Valves associated with
606      * this Container.
607      */

608     public Pipeline getPipeline() {
609
610         return (this.pipeline);
611
612     }
613
614
615     /**
616      * Return the Realm with which this Container is associated. If there is
617      * no associated Realm, return the Realm associated with our parent
618      * Container (if any); otherwise return <code>null</code>.
619      */

620     public Realm getRealm() {
621
622         if (realm != null)
623             return (realm);
624         if (parent != null)
625             return (parent.getRealm());
626         return (null);
627
628     }
629
630
631     /**
632      * Set the Realm with which this Container is associated.
633      *
634      * @param realm The newly associated Realm
635      */

636     public synchronized void setRealm(Realm realm) {
637
638         // Change components if necessary
639
Realm oldRealm = this.realm;
640         if (oldRealm == realm)
641             return;
642         this.realm = realm;
643
644         // Stop the old component if necessary
645
if (started && (oldRealm != null) &&
646             (oldRealm instanceof Lifecycle)) {
647             try {
648                 ((Lifecycle) oldRealm).stop();
649             } catch (LifecycleException e) {
650                 log.error("ContainerBase.setRealm: stop: ", e);
651             }
652         }
653
654         // Start the new component if necessary
655
if (realm != null)
656             realm.setContainer(this);
657         if (started && (realm != null) &&
658             (realm instanceof Lifecycle)) {
659             try {
660                 ((Lifecycle) realm).start();
661             } catch (LifecycleException e) {
662                 log.error("ContainerBase.setRealm: start: ", e);
663             }
664         }
665
666         // Report this property change to interested listeners
667
support.firePropertyChange("realm", oldRealm, this.realm);
668
669     }
670
671
672     /**
673       * Return the resources DirContext object with which this Container is
674       * associated. If there is no associated resources object, return the
675       * resources associated with our parent Container (if any); otherwise
676       * return <code>null</code>.
677      */

678     public DirContext JavaDoc getResources() {
679         if (resources != null)
680             return (resources);
681         if (parent != null)
682             return (parent.getResources());
683         return (null);
684
685     }
686
687
688     /**
689      * Set the resources DirContext object with which this Container is
690      * associated.
691      *
692      * @param resources The newly associated DirContext
693      */

694     public synchronized void setResources(DirContext JavaDoc resources) {
695         // Called from StandardContext.setResources()
696
// <- StandardContext.start()
697
// <- ContainerBase.addChildInternal()
698

699         // Change components if necessary
700
DirContext JavaDoc oldResources = this.resources;
701         if (oldResources == resources)
702             return;
703         Hashtable JavaDoc env = new Hashtable JavaDoc();
704         if (getParent() != null)
705             env.put(ProxyDirContext.HOST, getParent().getName());
706         env.put(ProxyDirContext.CONTEXT, getName());
707         this.resources = new ProxyDirContext(env, resources);
708         // Report this property change to interested listeners
709
support.firePropertyChange("resources", oldResources, this.resources);
710
711     }
712
713
714     // ------------------------------------------------------ Container Methods
715

716
717     /**
718      * Add a new child Container to those associated with this Container,
719      * if supported. Prior to adding this Container to the set of children,
720      * the child's <code>setParent()</code> method must be called, with this
721      * Container as an argument. This method may thrown an
722      * <code>IllegalArgumentException</code> if this Container chooses not
723      * to be attached to the specified Container, in which case it is not added
724      *
725      * @param child New child Container to be added
726      *
727      * @exception IllegalArgumentException if this exception is thrown by
728      * the <code>setParent()</code> method of the child Container
729      * @exception IllegalArgumentException if the new child does not have
730      * a name unique from that of existing children of this Container
731      * @exception IllegalStateException if this Container does not support
732      * child Containers
733      */

734     public void addChild(Container child) {
735         if (System.getSecurityManager() != null) {
736             PrivilegedAction JavaDoc dp =
737                 new PrivilegedAddChild(child);
738             AccessController.doPrivileged(dp);
739         } else {
740             addChildInternal(child);
741         }
742     }
743
744     private void addChildInternal(Container child) {
745
746         if( log.isDebugEnabled() )
747             log.debug("Add child " + child + " " + this);
748         synchronized(children) {
749             if (children.get(child.getName()) != null)
750                 throw new IllegalArgumentException JavaDoc("addChild: Child name '" +
751                                                    child.getName() +
752                                                    "' is not unique");
753             child.setParent(this); // May throw IAE
754
children.put(child.getName(), child);
755
756             // Start child
757
if (started && (child instanceof Lifecycle)) {
758                 boolean success = false;
759                 try {
760                     ((Lifecycle) child).start();
761                     success = true;
762                 } catch (LifecycleException e) {
763                     log.error("ContainerBase.addChild: start: ", e);
764                     throw new IllegalStateException JavaDoc
765                         ("ContainerBase.addChild: start: " + e);
766                 } finally {
767                     if (!success) {
768                         children.remove(child.getName());
769                     }
770                 }
771             }
772
773             fireContainerEvent(ADD_CHILD_EVENT, child);
774         }
775
776     }
777
778
779     /**
780      * Add a container event listener to this component.
781      *
782      * @param listener The listener to add
783      */

784     public void addContainerListener(ContainerListener listener) {
785
786         synchronized (listeners) {
787             listeners.add(listener);
788         }
789
790     }
791
792
793     /**
794      * Add a property change listener to this component.
795      *
796      * @param listener The listener to add
797      */

798     public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
799
800         support.addPropertyChangeListener(listener);
801
802     }
803
804
805     /**
806      * Return the child Container, associated with this Container, with
807      * the specified name (if any); otherwise, return <code>null</code>
808      *
809      * @param name Name of the child Container to be retrieved
810      */

811     public Container findChild(String JavaDoc name) {
812
813         if (name == null)
814             return (null);
815         synchronized (children) { // Required by post-start changes
816
return ((Container) children.get(name));
817         }
818
819     }
820
821
822     /**
823      * Return the set of children Containers associated with this Container.
824      * If this Container has no children, a zero-length array is returned.
825      */

826     public Container[] findChildren() {
827
828         synchronized (children) {
829             Container results[] = new Container[children.size()];
830             return ((Container[]) children.values().toArray(results));
831         }
832
833     }
834
835
836     /**
837      * Return the set of container listeners associated with this Container.
838      * If this Container has no registered container listeners, a zero-length
839      * array is returned.
840      */

841     public ContainerListener[] findContainerListeners() {
842
843         synchronized (listeners) {
844             ContainerListener[] results =
845                 new ContainerListener[listeners.size()];
846             return ((ContainerListener[]) listeners.toArray(results));
847         }
848
849     }
850
851
852     /**
853      * Process the specified Request, to produce the corresponding Response,
854      * by invoking the first Valve in our pipeline (if any), or the basic
855      * Valve otherwise.
856      *
857      * @param request Request to be processed
858      * @param response Response to be produced
859      *
860      * @exception IllegalStateException if neither a pipeline or a basic
861      * Valve have been configured for this Container
862      * @exception IOException if an input/output error occurred while
863      * processing
864      * @exception ServletException if a ServletException was thrown
865      * while processing this request
866      */

867     public void invoke(Request request, Response response)
868         throws IOException JavaDoc, ServletException JavaDoc {
869
870         pipeline.getFirst().invoke(request, response);
871
872     }
873
874
875     /**
876      * Remove an existing child Container from association with this parent
877      * Container.
878      *
879      * @param child Existing child Container to be removed
880      */

881     public void removeChild(Container child) {
882
883         synchronized(children) {
884             if (children.get(child.getName()) == null)
885                 return;
886             children.remove(child.getName());
887         }
888         
889         if (started && (child instanceof Lifecycle)) {
890             try {
891                 if( child instanceof ContainerBase ) {
892                     if( ((ContainerBase)child).started ) {
893                         ((Lifecycle) child).stop();
894                     }
895                 } else {
896                     ((Lifecycle) child).stop();
897                 }
898             } catch (LifecycleException e) {
899                 log.error("ContainerBase.removeChild: stop: ", e);
900             }
901         }
902         
903         fireContainerEvent(REMOVE_CHILD_EVENT, child);
904         
905         // child.setParent(null);
906

907     }
908
909
910     /**
911      * Remove a container event listener from this component.
912      *
913      * @param listener The listener to remove
914      */

915     public void removeContainerListener(ContainerListener listener) {
916
917         synchronized (listeners) {
918             listeners.remove(listener);
919         }
920
921     }
922
923
924     /**
925      * Remove a property change listener from this component.
926      *
927      * @param listener The listener to remove
928      */

929     public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
930
931         support.removePropertyChangeListener(listener);
932
933     }
934
935
936     // ------------------------------------------------------ Lifecycle Methods
937

938
939     /**
940      * Add a lifecycle event listener to this component.
941      *
942      * @param listener The listener to add
943      */

944     public void addLifecycleListener(LifecycleListener listener) {
945
946         lifecycle.addLifecycleListener(listener);
947
948     }
949
950
951     /**
952      * Get the lifecycle listeners associated with this lifecycle. If this
953      * Lifecycle has no listeners registered, a zero-length array is returned.
954      */

955     public LifecycleListener[] findLifecycleListeners() {
956
957         return lifecycle.findLifecycleListeners();
958
959     }
960
961
962     /**
963      * Remove a lifecycle event listener from this component.
964      *
965      * @param listener The listener to remove
966      */

967     public void removeLifecycleListener(LifecycleListener listener) {
968
969         lifecycle.removeLifecycleListener(listener);
970
971     }
972
973
974     /**
975      * Prepare for active use of the public methods of this Component.
976      *
977      * @exception LifecycleException if this component detects a fatal error
978      * that prevents it from being started
979      */

980     public synchronized void start() throws