]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/component/ContainerLifeCycle.java
Merge "Update notes about password security"
[gigi.git] / lib / jetty / org / eclipse / jetty / util / component / ContainerLifeCycle.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.util.component;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.concurrent.CopyOnWriteArrayList;
27
28 import org.eclipse.jetty.util.annotation.ManagedObject;
29 import org.eclipse.jetty.util.annotation.ManagedOperation;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32
33 /**
34  * An ContainerLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans.
35  * <p>
36  * Beans can be added the ContainerLifeCycle either as managed beans or as unmanaged beans.  A managed bean is started, stopped and destroyed with the aggregate.
37  * An unmanaged bean is associated with the aggregate for the purposes of {@link #dump()}, but it's lifecycle must be managed externally.
38  * <p>
39  * When a {@link LifeCycle} bean is added without a managed state being specified the state is determined heuristically:
40  * <ul>
41  *   <li>If the added bean is running, it will be added as an unmanaged bean.
42  *   <li>If the added bean is !running and the container is !running, it will be added as an AUTO bean (see below).
43  *   <li>If the added bean is !running and the container is starting, it will be added as an managed bean and will be started (this handles the frequent case of 
44  *   new beans added during calls to doStart).
45  *   <li>If the added bean is !running and the container is started, it will be added as an unmanaged bean.
46  * </ul>
47  * When the container is started, then all contained managed beans will also be started.  Any contained Auto beans 
48  * will be check for their status and if already started will be switched unmanaged beans, else they will be 
49  * started and switched to managed beans.  Beans added after a container is started are not started and their state needs to
50  * be explicitly managed.
51  * <p>
52  * When stopping the container, a contained bean will be stopped by this aggregate only if it
53  * is started by this aggregate.
54  * <p>
55  * The methods {@link #addBean(Object, boolean)}, {@link #manage(Object)} and {@link #unmanage(Object)} can be used to
56  * explicitly control the life cycle relationship.
57  * <p>
58  * If adding a bean that is shared between multiple {@link ContainerLifeCycle} instances, then it should be started before being added, so it is unmanaged, or
59  * the API must be used to explicitly set it as unmanaged.
60  * <p>
61  * This class also provides utility methods to dump deep structures of objects.  It the dump, the following symbols are used to indicate the type of contained object:
62  * <pre>
63  * SomeContainerLifeCycleInstance
64  *   +- contained POJO instance
65  *   += contained MANAGED object, started and stopped with this instance
66  *   +~ referenced UNMANAGED object, with separate lifecycle
67  *   +? referenced AUTO object that could become MANAGED or UNMANAGED.
68  * </pre>
69  */
70
71 /* ------------------------------------------------------------ */
72 /**
73  */
74 @ManagedObject("Implementation of Container and LifeCycle")
75 public class ContainerLifeCycle extends AbstractLifeCycle implements Container, Destroyable, Dumpable
76 {
77     private static final Logger LOG = Log.getLogger(ContainerLifeCycle.class);
78     private final List<Bean> _beans = new CopyOnWriteArrayList<>();
79     private final List<Container.Listener> _listeners = new CopyOnWriteArrayList<>();
80     private boolean _doStarted = false;
81
82
83     public ContainerLifeCycle()
84     {
85     }
86
87     /**
88      * Starts the managed lifecycle beans in the order they were added.
89      */
90     @Override
91     protected void doStart() throws Exception
92     {
93         // indicate that we are started, so that addBean will start other beans added.
94         _doStarted = true;
95
96         // start our managed and auto beans
97         for (Bean b : _beans)
98         {
99             if (b._bean instanceof LifeCycle)
100             {
101                 LifeCycle l = (LifeCycle)b._bean;
102                 switch(b._managed)
103                 {
104                     case MANAGED:
105                         if (!l.isRunning())
106                             start(l);
107                         break;
108                     case AUTO:
109                         if (l.isRunning())
110                             unmanage(b);
111                         else
112                         {
113                             manage(b);
114                             start(l);
115                         }
116                         break;
117                 }
118             }
119         }
120
121         super.doStart();
122     }
123
124     /**
125      * Starts the given lifecycle.
126      *
127      * @param l
128      * @throws Exception
129      */
130     protected void start(LifeCycle l) throws Exception
131     {
132         l.start();
133     }
134     
135     /**
136      * Stops the given lifecycle.
137      *
138      * @param l
139      * @throws Exception
140      */
141     protected void stop(LifeCycle l) throws Exception
142     {
143         l.stop();
144     }
145
146     /**
147      * Stops the managed lifecycle beans in the reverse order they were added.
148      */
149     @Override
150     protected void doStop() throws Exception
151     {
152         _doStarted = false;
153         super.doStop();
154         List<Bean> reverse = new ArrayList<>(_beans);
155         Collections.reverse(reverse);
156         for (Bean b : reverse)
157         {
158             if (b._managed==Managed.MANAGED && b._bean instanceof LifeCycle)
159             {
160                 LifeCycle l = (LifeCycle)b._bean;
161                 if (l.isRunning())
162                     stop(l);
163             }
164         }
165     }
166
167     /**
168      * Destroys the managed Destroyable beans in the reverse order they were added.
169      */
170     @Override
171     public void destroy()
172     {
173         List<Bean> reverse = new ArrayList<>(_beans);
174         Collections.reverse(reverse);
175         for (Bean b : reverse)
176         {
177             if (b._bean instanceof Destroyable && (b._managed==Managed.MANAGED || b._managed==Managed.POJO))
178             {
179                 Destroyable d = (Destroyable)b._bean;
180                 d.destroy();
181             }
182         }
183         _beans.clear();
184     }
185
186
187     /**
188      * @param bean the bean to test
189      * @return whether this aggregate contains the bean
190      */
191     public boolean contains(Object bean)
192     {
193         for (Bean b : _beans)
194             if (b._bean == bean)
195                 return true;
196         return false;
197     }
198
199     /**
200      * @param bean the bean to test
201      * @return whether this aggregate contains and manages the bean
202      */
203     public boolean isManaged(Object bean)
204     {
205         for (Bean b : _beans)
206             if (b._bean == bean)
207                 return b.isManaged();
208         return false;
209     }
210
211     /**
212      * Adds the given bean, detecting whether to manage it or not.
213      * If the bean is a {@link LifeCycle}, then it will be managed if it is not
214      * already started and not managed if it is already started.
215      * The {@link #addBean(Object, boolean)}
216      * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)}
217      * methods may be used after an add to change the status.
218      *
219      * @param o the bean object to add
220      * @return true if the bean was added, false if it was already present
221      */
222     @Override
223     public boolean addBean(Object o)
224     {
225         if (o instanceof LifeCycle)
226         {
227             LifeCycle l = (LifeCycle)o;
228             return addBean(o,l.isRunning()?Managed.UNMANAGED:Managed.AUTO);
229         }
230
231         return addBean(o,Managed.POJO);
232     }
233
234     /**
235      * Adds the given bean, explicitly managing it or not.
236      *
237      * @param o       The bean object to add
238      * @param managed whether to managed the lifecycle of the bean
239      * @return true if the bean was added, false if it was already present
240      */
241     public boolean addBean(Object o, boolean managed)
242     {
243         if (o instanceof LifeCycle)
244             return addBean(o,managed?Managed.MANAGED:Managed.UNMANAGED);
245         return addBean(o,managed?Managed.POJO:Managed.UNMANAGED);
246     }
247
248     public boolean addBean(Object o, Managed managed)
249     {
250         if (contains(o))
251             return false;
252
253         Bean new_bean = new Bean(o);
254
255         // if the bean is a Listener
256         if (o instanceof Container.Listener)
257             addEventListener((Container.Listener)o);
258
259         // Add the bean
260         _beans.add(new_bean);
261
262         // Tell existing listeners about the new bean
263         for (Container.Listener l:_listeners)
264             l.beanAdded(this,o);
265
266         try
267         {
268             switch (managed)
269             {
270                 case UNMANAGED:
271                     unmanage(new_bean);
272                     break;
273
274                 case MANAGED:
275                     manage(new_bean);
276
277                     if (isStarting() && _doStarted)
278                     {
279                         LifeCycle l = (LifeCycle)o;
280                         if (!l.isRunning())
281                             start(l);
282                     }
283                     break;
284
285                 case AUTO:
286                     if (o instanceof LifeCycle)
287                     {
288                         LifeCycle l = (LifeCycle)o;
289                         if (isStarting())
290                         {
291                             if (l.isRunning())
292                                 unmanage(new_bean);
293                             else if (_doStarted)
294                             {
295                                 manage(new_bean);
296                                 start(l);
297                             }
298                             else
299                                 new_bean._managed=Managed.AUTO;      
300                         }
301                         else if (isStarted())
302                             unmanage(new_bean);
303                         else
304                             new_bean._managed=Managed.AUTO;
305                     }
306                     else
307                         new_bean._managed=Managed.POJO;
308                     break;
309
310                 case POJO:
311                     new_bean._managed=Managed.POJO;
312             }
313         }
314         catch (RuntimeException | Error e)
315         {
316             throw e;
317         }
318         catch (Exception e)
319         {
320             throw new RuntimeException(e);
321         }
322
323         if (LOG.isDebugEnabled())
324             LOG.debug("{} added {}",this,new_bean);
325
326         return true;
327     }
328
329     
330     /* ------------------------------------------------------------ */
331     /** Add a managed lifecycle.
332      * <p>This is a conveniance method that uses addBean(lifecycle,true)
333      * and then ensures that the added bean is started iff this container
334      * is running.  Exception from nested calls to start are caught and 
335      * wrapped as RuntimeExceptions
336      * @param lifecycle
337      */
338     public void addManaged(LifeCycle lifecycle)
339     {
340         addBean(lifecycle,true);
341         try
342         {
343             if (isRunning() && !lifecycle.isRunning())
344                 start(lifecycle);
345         }
346         catch (RuntimeException | Error e)
347         {
348             throw e;
349         }
350         catch (Exception e)
351         {
352             throw new RuntimeException(e);
353         }
354     }
355
356     @Override
357     public void addEventListener(Container.Listener listener)
358     {
359         if (_listeners.contains(listener))
360             return;
361         
362         _listeners.add(listener);
363
364         // tell it about existing beans
365         for (Bean b:_beans)
366         {
367             listener.beanAdded(this,b._bean);
368
369             // handle inheritance
370             if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
371             {
372                 if (b._bean instanceof ContainerLifeCycle)
373                      ((ContainerLifeCycle)b._bean).addBean(listener, false);
374                  else
375                      ((Container)b._bean).addBean(listener);
376             }
377         }
378     }
379
380     /**
381      * Manages a bean already contained by this aggregate, so that it is started/stopped/destroyed with this
382      * aggregate.
383      *
384      * @param bean The bean to manage (must already have been added).
385      */
386     public void manage(Object bean)
387     {
388         for (Bean b : _beans)
389         {
390             if (b._bean == bean)
391             {
392                 manage(b);
393                 return;
394             }
395         }
396         throw new IllegalArgumentException("Unknown bean " + bean);
397     }
398
399     private void manage(Bean bean)
400     {
401         if (bean._managed!=Managed.MANAGED)
402         {
403             bean._managed=Managed.MANAGED;
404
405             if (bean._bean instanceof Container)
406             {
407                 for (Container.Listener l:_listeners)
408                 {
409                     if (l instanceof InheritedListener)
410                     {
411                         if (bean._bean instanceof ContainerLifeCycle)
412                             ((ContainerLifeCycle)bean._bean).addBean(l,false);
413                         else
414                             ((Container)bean._bean).addBean(l);
415                     }
416                 }
417             }
418
419             if (bean._bean instanceof AbstractLifeCycle)
420             {
421                 ((AbstractLifeCycle)bean._bean).setStopTimeout(getStopTimeout());
422             }
423         }
424     }
425
426     /**
427      * Unmanages a bean already contained by this aggregate, so that it is not started/stopped/destroyed with this
428      * aggregate.
429      *
430      * @param bean The bean to unmanage (must already have been added).
431      */
432     public void unmanage(Object bean)
433     {
434         for (Bean b : _beans)
435         {
436             if (b._bean == bean)
437             {
438                 unmanage(b);
439                 return;
440             }
441         }
442         throw new IllegalArgumentException("Unknown bean " + bean);
443     }
444
445     private void unmanage(Bean bean)
446     {
447         if (bean._managed!=Managed.UNMANAGED)
448         {
449             if (bean._managed==Managed.MANAGED && bean._bean instanceof Container)
450             {
451                 for (Container.Listener l:_listeners)
452                 {
453                     if (l instanceof InheritedListener)
454                         ((Container)bean._bean).removeBean(l);
455                 }
456             }
457             bean._managed=Managed.UNMANAGED;
458         }
459     }
460
461     @Override
462     public Collection<Object> getBeans()
463     {
464         return getBeans(Object.class);
465     }
466
467     public void setBeans(Collection<Object> beans)
468     {
469         for (Object bean : beans)
470             addBean(bean);
471     }
472
473     @Override
474     public <T> Collection<T> getBeans(Class<T> clazz)
475     {
476         ArrayList<T> beans = new ArrayList<>();
477         for (Bean b : _beans)
478         {
479             if (clazz.isInstance(b._bean))
480                 beans.add(clazz.cast(b._bean));
481         }
482         return beans;
483     }
484
485     @Override
486     public <T> T getBean(Class<T> clazz)
487     {
488         for (Bean b : _beans)
489         {
490             if (clazz.isInstance(b._bean))
491                 return clazz.cast(b._bean);
492         }
493         return null;
494     }
495
496     /**
497      * Removes all bean
498      */
499     public void removeBeans()
500     {
501         ArrayList<Bean> beans= new ArrayList<>(_beans);
502         for (Bean b : beans)
503             remove(b);
504     }
505
506     private Bean getBean(Object o)
507     {
508         for (Bean b : _beans)
509         {
510             if (b._bean == o)
511                 return b;
512         }
513         return null;
514     }
515
516     @Override
517     public boolean removeBean(Object o)
518     {
519         Bean b=getBean(o);
520         return b!=null && remove(b);
521     }
522
523     private boolean remove(Bean bean)
524     {
525         if (_beans.remove(bean))
526         {
527             boolean wasManaged = bean.isManaged();
528             
529             unmanage(bean);
530
531             for (Container.Listener l:_listeners)
532                 l.beanRemoved(this,bean._bean);
533
534             if (bean._bean instanceof Container.Listener)
535                 removeEventListener((Container.Listener)bean._bean);
536
537             // stop managed beans
538             if (wasManaged && bean._bean instanceof LifeCycle)
539             {
540                 try
541                 {
542                     stop((LifeCycle)bean._bean);
543                 }
544                 catch(RuntimeException | Error e)
545                 {
546                     throw e;
547                 }
548                 catch (Exception e)
549                 {
550                     throw new RuntimeException(e);
551                 }
552             }
553             return true;
554         }
555         return false;
556     }
557
558     @Override
559     public void removeEventListener(Container.Listener listener)
560     {
561         if (_listeners.remove(listener))
562         {
563             // remove existing beans
564             for (Bean b:_beans)
565             {
566                 listener.beanRemoved(this,b._bean);
567
568                 if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
569                     ((Container)b._bean).removeBean(listener);
570             }
571         }
572     }
573
574     @Override
575     public void setStopTimeout(long stopTimeout)
576     {
577         super.setStopTimeout(stopTimeout);
578         for (Bean bean : _beans)
579         {
580             if (bean.isManaged() && bean._bean instanceof AbstractLifeCycle)
581                 ((AbstractLifeCycle)bean._bean).setStopTimeout(stopTimeout);
582         }
583     }
584
585     /**
586      * Dumps to {@link System#err}.
587      * @see #dump()
588      */
589     @ManagedOperation("Dump the object to stderr")
590     public void dumpStdErr()
591     {
592         try
593         {
594             dump(System.err, "");
595         }
596         catch (IOException e)
597         {
598             LOG.warn(e);
599         }
600     }
601
602     @Override
603     @ManagedOperation("Dump the object to a string")
604     public String dump()
605     {
606         return dump(this);
607     }
608
609     public static String dump(Dumpable dumpable)
610     {
611         StringBuilder b = new StringBuilder();
612         try
613         {
614             dumpable.dump(b, "");
615         }
616         catch (IOException e)
617         {
618             LOG.warn(e);
619         }
620         return b.toString();
621     }
622
623     public void dump(Appendable out) throws IOException
624     {
625         dump(out, "");
626     }
627
628     protected void dumpThis(Appendable out) throws IOException
629     {
630         out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n");
631     }
632
633     public static void dumpObject(Appendable out, Object o) throws IOException
634     {
635         try
636         {
637             if (o instanceof LifeCycle)
638                 out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n");
639             else
640                 out.append(String.valueOf(o)).append("\n");
641         }
642         catch (Throwable th)
643         {
644             out.append(" => ").append(th.toString()).append('\n');
645         }
646     }
647
648     @Override
649     public void dump(Appendable out, String indent) throws IOException
650     {
651         dumpBeans(out,indent);
652     }
653
654     protected void dumpBeans(Appendable out, String indent, Collection<?>... collections) throws IOException
655     {
656         dumpThis(out);
657         int size = _beans.size();
658         for (Collection<?> c : collections)
659             size += c.size();
660         if (size == 0)
661             return;
662         int i = 0;
663         for (Bean b : _beans)
664         {
665             i++;
666
667             switch(b._managed)
668             {
669                 case POJO:
670                     out.append(indent).append(" +- ");
671                     if (b._bean instanceof Dumpable)
672                         ((Dumpable)b._bean).dump(out, indent + (i == size ? "    " : " |  "));
673                     else
674                         dumpObject(out, b._bean);
675                     break;
676
677                 case MANAGED:
678                     out.append(indent).append(" += ");
679                     if (b._bean instanceof Dumpable)
680                         ((Dumpable)b._bean).dump(out, indent + (i == size ? "    " : " |  "));
681                     else
682                         dumpObject(out, b._bean);
683                     break;
684
685                 case UNMANAGED:
686                     out.append(indent).append(" +~ ");
687                     dumpObject(out, b._bean);
688                     break;
689
690                 case AUTO:
691                     out.append(indent).append(" +? ");
692                     if (b._bean instanceof Dumpable)
693                         ((Dumpable)b._bean).dump(out, indent + (i == size ? "    " : " |  "));
694                     else
695                         dumpObject(out, b._bean);
696                     break;
697
698             }
699         }
700
701         if (i<size)
702             out.append(indent).append(" |\n");
703
704         for (Collection<?> c : collections)
705         {
706             for (Object o : c)
707             {
708                 i++;
709                 out.append(indent).append(" +> ");
710
711                 if (o instanceof Dumpable)
712                     ((Dumpable)o).dump(out, indent + (i == size ? "    " : " |  "));
713                 else
714                     dumpObject(out, o);
715             }
716         }
717     }
718
719     public static void dump(Appendable out, String indent, Collection<?>... collections) throws IOException
720     {
721         if (collections.length == 0)
722             return;
723         int size = 0;
724         for (Collection<?> c : collections)
725             size += c.size();
726         if (size == 0)
727             return;
728
729         int i = 0;
730         for (Collection<?> c : collections)
731         {
732             for (Object o : c)
733             {
734                 i++;
735                 out.append(indent).append(" +- ");
736
737                 if (o instanceof Dumpable)
738                     ((Dumpable)o).dump(out, indent + (i == size ? "    " : " |  "));
739                 else
740                     dumpObject(out, o);
741             }
742         }
743     }
744
745
746     enum Managed { POJO, MANAGED, UNMANAGED, AUTO };
747
748     private static class Bean
749     {
750         private final Object _bean;
751         private volatile Managed _managed = Managed.POJO;
752
753         private Bean(Object b)
754         {
755             _bean = b;
756         }
757
758         public boolean isManaged()
759         {
760             return _managed==Managed.MANAGED;
761         }
762
763         @Override
764         public String toString()
765         {
766             return String.format("{%s,%s}", _bean, _managed);
767         }
768     }
769
770     public void updateBean(Object oldBean, final Object newBean)
771     {
772         if (newBean!=oldBean)
773         {
774             if (oldBean!=null)
775                 removeBean(oldBean);
776             if (newBean!=null)
777                 addBean(newBean);
778         }
779     }
780
781     public void updateBeans(Object[] oldBeans, final Object[] newBeans)
782     {
783         // remove oldChildren not in newChildren
784         if (oldBeans!=null)
785         {
786             loop: for (Object o:oldBeans)
787             {
788                 if (newBeans!=null)
789                 {
790                     for (Object n:newBeans)
791                         if (o==n)
792                             continue loop;
793                 }
794                 removeBean(o);
795             }
796         }
797
798         // add new beans not in old
799         if (newBeans!=null)
800         {
801             loop: for (Object n:newBeans)
802             {
803                 if (oldBeans!=null)
804                 {
805                     for (Object o:oldBeans)
806                         if (o==n)
807                             continue loop;
808                 }
809                 addBean(n);
810             }
811         }
812     }
813 }