View Javadoc

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