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