View Javadoc

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 the lifecycle to start
128      * @throws Exception if unable to start lifecycle
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 the lifecycle to stop
139      * @throws Exception if unable to stop the lifecycle
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                 stop(l);
162             }
163         }
164     }
165 
166     /**
167      * Destroys the managed Destroyable beans in the reverse order they were added.
168      */
169     @Override
170     public void destroy()
171     {
172         List<Bean> reverse = new ArrayList<>(_beans);
173         Collections.reverse(reverse);
174         for (Bean b : reverse)
175         {
176             if (b._bean instanceof Destroyable && (b._managed==Managed.MANAGED || b._managed==Managed.POJO))
177             {
178                 Destroyable d = (Destroyable)b._bean;
179                 d.destroy();
180             }
181         }
182         _beans.clear();
183     }
184 
185 
186     /**
187      * @param bean the bean to test
188      * @return whether this aggregate contains the bean
189      */
190     public boolean contains(Object bean)
191     {
192         for (Bean b : _beans)
193             if (b._bean == bean)
194                 return true;
195         return false;
196     }
197 
198     /**
199      * @param bean the bean to test
200      * @return whether this aggregate contains and manages the bean
201      */
202     public boolean isManaged(Object bean)
203     {
204         for (Bean b : _beans)
205             if (b._bean == bean)
206                 return b.isManaged();
207         return false;
208     }
209 
210     /**
211      * Adds the given bean, detecting whether to manage it or not.
212      * If the bean is a {@link LifeCycle}, then it will be managed if it is not
213      * already started and not managed if it is already started.
214      * The {@link #addBean(Object, boolean)}
215      * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)}
216      * methods may be used after an add to change the status.
217      *
218      * @param o the bean object to add
219      * @return true if the bean was added, false if it was already present
220      */
221     @Override
222     public boolean addBean(Object o)
223     {
224         if (o instanceof LifeCycle)
225         {
226             LifeCycle l = (LifeCycle)o;
227             return addBean(o,l.isRunning()?Managed.UNMANAGED:Managed.AUTO);
228         }
229 
230         return addBean(o,Managed.POJO);
231     }
232 
233     /**
234      * Adds the given bean, explicitly managing it or not.
235      *
236      * @param o       The bean object to add
237      * @param managed whether to managed the lifecycle of the bean
238      * @return true if the bean was added, false if it was already present
239      */
240     public boolean addBean(Object o, boolean managed)
241     {
242         if (o instanceof LifeCycle)
243             return addBean(o,managed?Managed.MANAGED:Managed.UNMANAGED);
244         return addBean(o,managed?Managed.POJO:Managed.UNMANAGED);
245     }
246 
247     public boolean addBean(Object o, Managed managed)
248     {
249         if (contains(o))
250             return false;
251 
252         Bean new_bean = new Bean(o);
253 
254         // if the bean is a Listener
255         if (o instanceof Container.Listener)
256             addEventListener((Container.Listener)o);
257 
258         // Add the bean
259         _beans.add(new_bean);
260 
261         // Tell existing listeners about the new bean
262         for (Container.Listener l:_listeners)
263             l.beanAdded(this,o);
264 
265         try
266         {
267             switch (managed)
268             {
269                 case UNMANAGED:
270                     unmanage(new_bean);
271                     break;
272 
273                 case MANAGED:
274                     manage(new_bean);
275 
276                     if (isStarting() && _doStarted)
277                     {
278                         LifeCycle l = (LifeCycle)o;
279                         if (!l.isRunning())
280                             start(l);
281                     }
282                     break;
283 
284                 case AUTO:
285                     if (o instanceof LifeCycle)
286                     {
287                         LifeCycle l = (LifeCycle)o;
288                         if (isStarting())
289                         {
290                             if (l.isRunning())
291                                 unmanage(new_bean);
292                             else if (_doStarted)
293                             {
294                                 manage(new_bean);
295                                 start(l);
296                             }
297                             else
298                                 new_bean._managed=Managed.AUTO;      
299                         }
300                         else if (isStarted())
301                             unmanage(new_bean);
302                         else
303                             new_bean._managed=Managed.AUTO;
304                     }
305                     else
306                         new_bean._managed=Managed.POJO;
307                     break;
308 
309                 case POJO:
310                     new_bean._managed=Managed.POJO;
311             }
312         }
313         catch (RuntimeException | Error e)
314         {
315             throw e;
316         }
317         catch (Exception e)
318         {
319             throw new RuntimeException(e);
320         }
321 
322         if (LOG.isDebugEnabled())
323             LOG.debug("{} added {}",this,new_bean);
324 
325         return true;
326     }
327 
328     
329     /* ------------------------------------------------------------ */
330     /** Add a managed lifecycle.
331      * <p>This is a convenience method that uses addBean(lifecycle,true)
332      * and then ensures that the added bean is started iff this container
333      * is running.  Exception from nested calls to start are caught and 
334      * wrapped as RuntimeExceptions
335      * @param lifecycle the managed lifecycle to add
336      */
337     public void addManaged(LifeCycle lifecycle)
338     {
339         addBean(lifecycle,true);
340         try
341         {
342             if (isRunning() && !lifecycle.isRunning())
343                 start(lifecycle);
344         }
345         catch (RuntimeException | Error e)
346         {
347             throw e;
348         }
349         catch (Exception e)
350         {
351             throw new RuntimeException(e);
352         }
353     }
354 
355     @Override
356     public void addEventListener(Container.Listener listener)
357     {
358         if (_listeners.contains(listener))
359             return;
360         
361         _listeners.add(listener);
362 
363         // tell it about existing beans
364         for (Bean b:_beans)
365         {
366             listener.beanAdded(this,b._bean);
367 
368             // handle inheritance
369             if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
370             {
371                 if (b._bean instanceof ContainerLifeCycle)
372                      ((ContainerLifeCycle)b._bean).addBean(listener, false);
373                  else
374                      ((Container)b._bean).addBean(listener);
375             }
376         }
377     }
378 
379     /**
380      * Manages a bean already contained by this aggregate, so that it is started/stopped/destroyed with this
381      * aggregate.
382      *
383      * @param bean The bean to manage (must already have been added).
384      */
385     public void manage(Object bean)
386     {
387         for (Bean b : _beans)
388         {
389             if (b._bean == bean)
390             {
391                 manage(b);
392                 return;
393             }
394         }
395         throw new IllegalArgumentException("Unknown bean " + bean);
396     }
397 
398     private void manage(Bean bean)
399     {
400         if (bean._managed!=Managed.MANAGED)
401         {
402             bean._managed=Managed.MANAGED;
403 
404             if (bean._bean instanceof Container)
405             {
406                 for (Container.Listener l:_listeners)
407                 {
408                     if (l instanceof InheritedListener)
409                     {
410                         if (bean._bean instanceof ContainerLifeCycle)
411                             ((ContainerLifeCycle)bean._bean).addBean(l,false);
412                         else
413                             ((Container)bean._bean).addBean(l);
414                     }
415                 }
416             }
417 
418             if (bean._bean instanceof AbstractLifeCycle)
419             {
420                 ((AbstractLifeCycle)bean._bean).setStopTimeout(getStopTimeout());
421             }
422         }
423     }
424 
425     /**
426      * Unmanages a bean already contained by this aggregate, so that it is not started/stopped/destroyed with this
427      * aggregate.
428      *
429      * @param bean The bean to unmanage (must already have been added).
430      */
431     public void unmanage(Object bean)
432     {
433         for (Bean b : _beans)
434         {
435             if (b._bean == bean)
436             {
437                 unmanage(b);
438                 return;
439             }
440         }
441         throw new IllegalArgumentException("Unknown bean " + bean);
442     }
443 
444     private void unmanage(Bean bean)
445     {
446         if (bean._managed!=Managed.UNMANAGED)
447         {
448             if (bean._managed==Managed.MANAGED && bean._bean instanceof Container)
449             {
450                 for (Container.Listener l:_listeners)
451                 {
452                     if (l instanceof InheritedListener)
453                         ((Container)bean._bean).removeBean(l);
454                 }
455             }
456             bean._managed=Managed.UNMANAGED;
457         }
458     }
459 
460     @Override
461     public Collection<Object> getBeans()
462     {
463         return getBeans(Object.class);
464     }
465 
466     public void setBeans(Collection<Object> beans)
467     {
468         for (Object bean : beans)
469             addBean(bean);
470     }
471 
472     @Override
473     public <T> Collection<T> getBeans(Class<T> clazz)
474     {
475         ArrayList<T> beans = new ArrayList<>();
476         for (Bean b : _beans)
477         {
478             if (clazz.isInstance(b._bean))
479                 beans.add(clazz.cast(b._bean));
480         }
481         return beans;
482     }
483 
484     @Override
485     public <T> T getBean(Class<T> clazz)
486     {
487         for (Bean b : _beans)
488         {
489             if (clazz.isInstance(b._bean))
490                 return clazz.cast(b._bean);
491         }
492         return null;
493     }
494 
495     /**
496      * Removes all bean
497      */
498     public void removeBeans()
499     {
500         ArrayList<Bean> beans= new ArrayList<>(_beans);
501         for (Bean b : beans)
502             remove(b);
503     }
504 
505     private Bean getBean(Object o)
506     {
507         for (Bean b : _beans)
508         {
509             if (b._bean == o)
510                 return b;
511         }
512         return null;
513     }
514 
515     @Override
516     public boolean removeBean(Object o)
517     {
518         Bean b=getBean(o);
519         return b!=null && remove(b);
520     }
521 
522     private boolean remove(Bean bean)
523     {
524         if (_beans.remove(bean))
525         {
526             boolean wasManaged = bean.isManaged();
527             
528             unmanage(bean);
529 
530             for (Container.Listener l:_listeners)
531                 l.beanRemoved(this,bean._bean);
532 
533             if (bean._bean instanceof Container.Listener)
534                 removeEventListener((Container.Listener)bean._bean);
535 
536             // stop managed beans
537             if (wasManaged && bean._bean instanceof LifeCycle)
538             {
539                 try
540                 {
541                     stop((LifeCycle)bean._bean);
542                 }
543                 catch(RuntimeException | Error e)
544                 {
545                     throw e;
546                 }
547                 catch (Exception e)
548                 {
549                     throw new RuntimeException(e);
550                 }
551             }
552             return true;
553         }
554         return false;
555     }
556 
557     @Override
558     public void removeEventListener(Container.Listener listener)
559     {
560         if (_listeners.remove(listener))
561         {
562             // remove existing beans
563             for (Bean b:_beans)
564             {
565                 listener.beanRemoved(this,b._bean);
566 
567                 if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
568                     ((Container)b._bean).removeBean(listener);
569             }
570         }
571     }
572 
573     @Override
574     public void setStopTimeout(long stopTimeout)
575     {
576         super.setStopTimeout(stopTimeout);
577         for (Bean bean : _beans)
578         {
579             if (bean.isManaged() && bean._bean instanceof AbstractLifeCycle)
580                 ((AbstractLifeCycle)bean._bean).setStopTimeout(stopTimeout);
581         }
582     }
583 
584     /**
585      * Dumps to {@link System#err}.
586      * @see #dump()
587      */
588     @ManagedOperation("Dump the object to stderr")
589     public void dumpStdErr()
590     {
591         try
592         {
593             dump(System.err, "");
594         }
595         catch (IOException e)
596         {
597             LOG.warn(e);
598         }
599     }
600 
601     @Override
602     @ManagedOperation("Dump the object to a string")
603     public String dump()
604     {
605         return dump(this);
606     }
607 
608     public static String dump(Dumpable dumpable)
609     {
610         StringBuilder b = new StringBuilder();
611         try
612         {
613             dumpable.dump(b, "");
614         }
615         catch (IOException e)
616         {
617             LOG.warn(e);
618         }
619         return b.toString();
620     }
621 
622     public void dump(Appendable out) throws IOException
623     {
624         dump(out, "");
625     }
626 
627     protected void dumpThis(Appendable out) throws IOException
628     {
629         out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n");
630     }
631 
632     public static void dumpObject(Appendable out, Object o) throws IOException
633     {
634         try
635         {
636             if (o instanceof LifeCycle)
637                 out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n");
638             else
639                 out.append(String.valueOf(o)).append("\n");
640         }
641         catch (Throwable th)
642         {
643             out.append(" => ").append(th.toString()).append('\n');
644         }
645     }
646 
647     @Override
648     public void dump(Appendable out, String indent) throws IOException
649     {
650         dumpBeans(out,indent);
651     }
652 
653     protected void dumpBeans(Appendable out, String indent, Collection<?>... collections) throws IOException
654     {
655         dumpThis(out);
656         int size = _beans.size();
657         for (Collection<?> c : collections)
658             size += c.size();
659         if (size == 0)
660             return;
661         int i = 0;
662         for (Bean b : _beans)
663         {
664             i++;
665 
666             switch(b._managed)
667             {
668                 case POJO:
669                     out.append(indent).append(" +- ");
670                     if (b._bean instanceof Dumpable)
671                         ((Dumpable)b._bean).dump(out, indent + (i == size ? "    " : " |  "));
672                     else
673                         dumpObject(out, b._bean);
674                     break;
675 
676                 case MANAGED:
677                     out.append(indent).append(" += ");
678                     if (b._bean instanceof Dumpable)
679                         ((Dumpable)b._bean).dump(out, indent + (i == size ? "    " : " |  "));
680                     else
681                         dumpObject(out, b._bean);
682                     break;
683 
684                 case UNMANAGED:
685                     out.append(indent).append(" +~ ");
686                     dumpObject(out, b._bean);
687                     break;
688 
689                 case AUTO:
690                     out.append(indent).append(" +? ");
691                     if (b._bean instanceof Dumpable)
692                         ((Dumpable)b._bean).dump(out, indent + (i == size ? "    " : " |  "));
693                     else
694                         dumpObject(out, b._bean);
695                     break;
696 
697             }
698         }
699 
700         if (i<size)
701             out.append(indent).append(" |\n");
702 
703         for (Collection<?> c : collections)
704         {
705             for (Object o : c)
706             {
707                 i++;
708                 out.append(indent).append(" +> ");
709 
710                 if (o instanceof Dumpable)
711                     ((Dumpable)o).dump(out, indent + (i == size ? "    " : " |  "));
712                 else
713                     dumpObject(out, o);
714             }
715         }
716     }
717 
718     public static void dump(Appendable out, String indent, Collection<?>... collections) throws IOException
719     {
720         if (collections.length == 0)
721             return;
722         int size = 0;
723         for (Collection<?> c : collections)
724             size += c.size();
725         if (size == 0)
726             return;
727 
728         int i = 0;
729         for (Collection<?> c : collections)
730         {
731             for (Object o : c)
732             {
733                 i++;
734                 out.append(indent).append(" +- ");
735 
736                 if (o instanceof Dumpable)
737                     ((Dumpable)o).dump(out, indent + (i == size ? "    " : " |  "));
738                 else
739                     dumpObject(out, o);
740             }
741         }
742     }
743 
744 
745     enum Managed { POJO, MANAGED, UNMANAGED, AUTO };
746 
747     private static class Bean
748     {
749         private final Object _bean;
750         private volatile Managed _managed = Managed.POJO;
751 
752         private Bean(Object b)
753         {
754             _bean = b;
755         }
756 
757         public boolean isManaged()
758         {
759             return _managed==Managed.MANAGED;
760         }
761 
762         @Override
763         public String toString()
764         {
765             return String.format("{%s,%s}", _bean, _managed);
766         }
767     }
768 
769     public void updateBean(Object oldBean, final Object newBean)
770     {
771         if (newBean!=oldBean)
772         {
773             if (oldBean!=null)
774                 removeBean(oldBean);
775             if (newBean!=null)
776                 addBean(newBean);
777         }
778     }
779     
780     public void updateBean(Object oldBean, final Object newBean, boolean managed)
781     {
782         if (newBean!=oldBean)
783         {
784             if (oldBean!=null)
785                 removeBean(oldBean);
786             if (newBean!=null)
787                 addBean(newBean,managed);
788         }
789     }
790 
791     public void updateBeans(Object[] oldBeans, final Object[] newBeans)
792     {
793         // remove oldChildren not in newChildren
794         if (oldBeans!=null)
795         {
796             loop: for (Object o:oldBeans)
797             {
798                 if (newBeans!=null)
799                 {
800                     for (Object n:newBeans)
801                         if (o==n)
802                             continue loop;
803                 }
804                 removeBean(o);
805             }
806         }
807 
808         // add new beans not in old
809         if (newBeans!=null)
810         {
811             loop: for (Object n:newBeans)
812             {
813                 if (oldBeans!=null)
814                 {
815                     for (Object o:oldBeans)
816                         if (o==n)
817                             continue loop;
818                 }
819                 addBean(n);
820             }
821         }
822     }
823 }