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