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