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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util.component;
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;
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;
34 * An ContainerLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans.
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.
39 * When a {@link LifeCycle} bean is added without a managed state being specified the state is determined heuristically:
41 * <li>If the added bean is running, it will be added as an unmanaged bean.
42 * <li>If the added bean is !running and the container is !running, it will be added as an AUTO bean (see below).
43 * <li>If the added bean is !running and the container is starting, it will be added as an managed bean and will be started (this handles the frequent case of
44 * new beans added during calls to doStart).
45 * <li>If the added bean is !running and the container is started, it will be added as an unmanaged bean.
47 * When the container is started, then all contained managed beans will also be started. Any contained Auto beans
48 * will be check for their status and if already started will be switched unmanaged beans, else they will be
49 * started and switched to managed beans. Beans added after a container is started are not started and their state needs to
50 * be explicitly managed.
52 * When stopping the container, a contained bean will be stopped by this aggregate only if it
53 * is started by this aggregate.
55 * The methods {@link #addBean(Object, boolean)}, {@link #manage(Object)} and {@link #unmanage(Object)} can be used to
56 * explicitly control the life cycle relationship.
58 * If adding a bean that is shared between multiple {@link ContainerLifeCycle} instances, then it should be started before being added, so it is unmanaged, or
59 * the API must be used to explicitly set it as unmanaged.
61 * This class also provides utility methods to dump deep structures of objects. It the dump, the following symbols are used to indicate the type of contained object:
63 * SomeContainerLifeCycleInstance
64 * +- contained POJO instance
65 * += contained MANAGED object, started and stopped with this instance
66 * +~ referenced UNMANAGED object, with separate lifecycle
67 * +? referenced AUTO object that could become MANAGED or UNMANAGED.
71 /* ------------------------------------------------------------ */
74 @ManagedObject("Implementation of Container and LifeCycle")
75 public class ContainerLifeCycle extends AbstractLifeCycle implements Container, Destroyable, Dumpable
77 private static final Logger LOG = Log.getLogger(ContainerLifeCycle.class);
78 private final List<Bean> _beans = new CopyOnWriteArrayList<>();
79 private final List<Container.Listener> _listeners = new CopyOnWriteArrayList<>();
80 private boolean _doStarted = false;
83 public ContainerLifeCycle()
88 * Starts the managed lifecycle beans in the order they were added.
91 protected void doStart() throws Exception
93 // indicate that we are started, so that addBean will start other beans added.
96 // start our managed and auto beans
99 if (b._bean instanceof LifeCycle)
101 LifeCycle l = (LifeCycle)b._bean;
125 * Starts the given lifecycle.
130 protected void start(LifeCycle l) throws Exception
136 * Stops the given lifecycle.
141 protected void stop(LifeCycle l) throws Exception
147 * Stops the managed lifecycle beans in the reverse order they were added.
150 protected void doStop() throws Exception
154 List<Bean> reverse = new ArrayList<>(_beans);
155 Collections.reverse(reverse);
156 for (Bean b : reverse)
158 if (b._managed==Managed.MANAGED && b._bean instanceof LifeCycle)
160 LifeCycle l = (LifeCycle)b._bean;
168 * Destroys the managed Destroyable beans in the reverse order they were added.
171 public void destroy()
173 List<Bean> reverse = new ArrayList<>(_beans);
174 Collections.reverse(reverse);
175 for (Bean b : reverse)
177 if (b._bean instanceof Destroyable && (b._managed==Managed.MANAGED || b._managed==Managed.POJO))
179 Destroyable d = (Destroyable)b._bean;
188 * @param bean the bean to test
189 * @return whether this aggregate contains the bean
191 public boolean contains(Object bean)
193 for (Bean b : _beans)
200 * @param bean the bean to test
201 * @return whether this aggregate contains and manages the bean
203 public boolean isManaged(Object bean)
205 for (Bean b : _beans)
207 return b.isManaged();
212 * Adds the given bean, detecting whether to manage it or not.
213 * If the bean is a {@link LifeCycle}, then it will be managed if it is not
214 * already started and not managed if it is already started.
215 * The {@link #addBean(Object, boolean)}
216 * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)}
217 * methods may be used after an add to change the status.
219 * @param o the bean object to add
220 * @return true if the bean was added, false if it was already present
223 public boolean addBean(Object o)
225 if (o instanceof LifeCycle)
227 LifeCycle l = (LifeCycle)o;
228 return addBean(o,l.isRunning()?Managed.UNMANAGED:Managed.AUTO);
231 return addBean(o,Managed.POJO);
235 * Adds the given bean, explicitly managing it or not.
237 * @param o The bean object to add
238 * @param managed whether to managed the lifecycle of the bean
239 * @return true if the bean was added, false if it was already present
241 public boolean addBean(Object o, boolean managed)
243 if (o instanceof LifeCycle)
244 return addBean(o,managed?Managed.MANAGED:Managed.UNMANAGED);
245 return addBean(o,managed?Managed.POJO:Managed.UNMANAGED);
248 public boolean addBean(Object o, Managed managed)
253 Bean new_bean = new Bean(o);
255 // if the bean is a Listener
256 if (o instanceof Container.Listener)
257 addEventListener((Container.Listener)o);
260 _beans.add(new_bean);
262 // Tell existing listeners about the new bean
263 for (Container.Listener l:_listeners)
277 if (isStarting() && _doStarted)
279 LifeCycle l = (LifeCycle)o;
286 if (o instanceof LifeCycle)
288 LifeCycle l = (LifeCycle)o;
299 new_bean._managed=Managed.AUTO;
301 else if (isStarted())
304 new_bean._managed=Managed.AUTO;
307 new_bean._managed=Managed.POJO;
311 new_bean._managed=Managed.POJO;
314 catch (RuntimeException | Error e)
320 throw new RuntimeException(e);
323 if (LOG.isDebugEnabled())
324 LOG.debug("{} added {}",this,new_bean);
330 /* ------------------------------------------------------------ */
331 /** Add a managed lifecycle.
332 * <p>This is a conveniance method that uses addBean(lifecycle,true)
333 * and then ensures that the added bean is started iff this container
334 * is running. Exception from nested calls to start are caught and
335 * wrapped as RuntimeExceptions
338 public void addManaged(LifeCycle lifecycle)
340 addBean(lifecycle,true);
343 if (isRunning() && !lifecycle.isRunning())
346 catch (RuntimeException | Error e)
352 throw new RuntimeException(e);
357 public void addEventListener(Container.Listener listener)
359 if (_listeners.contains(listener))
362 _listeners.add(listener);
364 // tell it about existing beans
367 listener.beanAdded(this,b._bean);
369 // handle inheritance
370 if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
372 if (b._bean instanceof ContainerLifeCycle)
373 ((ContainerLifeCycle)b._bean).addBean(listener, false);
375 ((Container)b._bean).addBean(listener);
381 * Manages a bean already contained by this aggregate, so that it is started/stopped/destroyed with this
384 * @param bean The bean to manage (must already have been added).
386 public void manage(Object bean)
388 for (Bean b : _beans)
396 throw new IllegalArgumentException("Unknown bean " + bean);
399 private void manage(Bean bean)
401 if (bean._managed!=Managed.MANAGED)
403 bean._managed=Managed.MANAGED;
405 if (bean._bean instanceof Container)
407 for (Container.Listener l:_listeners)
409 if (l instanceof InheritedListener)
411 if (bean._bean instanceof ContainerLifeCycle)
412 ((ContainerLifeCycle)bean._bean).addBean(l,false);
414 ((Container)bean._bean).addBean(l);
419 if (bean._bean instanceof AbstractLifeCycle)
421 ((AbstractLifeCycle)bean._bean).setStopTimeout(getStopTimeout());
427 * Unmanages a bean already contained by this aggregate, so that it is not started/stopped/destroyed with this
430 * @param bean The bean to unmanage (must already have been added).
432 public void unmanage(Object bean)
434 for (Bean b : _beans)
442 throw new IllegalArgumentException("Unknown bean " + bean);
445 private void unmanage(Bean bean)
447 if (bean._managed!=Managed.UNMANAGED)
449 if (bean._managed==Managed.MANAGED && bean._bean instanceof Container)
451 for (Container.Listener l:_listeners)
453 if (l instanceof InheritedListener)
454 ((Container)bean._bean).removeBean(l);
457 bean._managed=Managed.UNMANAGED;
462 public Collection<Object> getBeans()
464 return getBeans(Object.class);
467 public void setBeans(Collection<Object> beans)
469 for (Object bean : beans)
474 public <T> Collection<T> getBeans(Class<T> clazz)
476 ArrayList<T> beans = new ArrayList<>();
477 for (Bean b : _beans)
479 if (clazz.isInstance(b._bean))
480 beans.add(clazz.cast(b._bean));
486 public <T> T getBean(Class<T> clazz)
488 for (Bean b : _beans)
490 if (clazz.isInstance(b._bean))
491 return clazz.cast(b._bean);
499 public void removeBeans()
501 ArrayList<Bean> beans= new ArrayList<>(_beans);
506 private Bean getBean(Object o)
508 for (Bean b : _beans)
517 public boolean removeBean(Object o)
520 return b!=null && remove(b);
523 private boolean remove(Bean bean)
525 if (_beans.remove(bean))
527 boolean wasManaged = bean.isManaged();
531 for (Container.Listener l:_listeners)
532 l.beanRemoved(this,bean._bean);
534 if (bean._bean instanceof Container.Listener)
535 removeEventListener((Container.Listener)bean._bean);
537 // stop managed beans
538 if (wasManaged && bean._bean instanceof LifeCycle)
542 stop((LifeCycle)bean._bean);
544 catch(RuntimeException | Error e)
550 throw new RuntimeException(e);
559 public void removeEventListener(Container.Listener listener)
561 if (_listeners.remove(listener))
563 // remove existing beans
566 listener.beanRemoved(this,b._bean);
568 if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
569 ((Container)b._bean).removeBean(listener);
575 public void setStopTimeout(long stopTimeout)
577 super.setStopTimeout(stopTimeout);
578 for (Bean bean : _beans)
580 if (bean.isManaged() && bean._bean instanceof AbstractLifeCycle)
581 ((AbstractLifeCycle)bean._bean).setStopTimeout(stopTimeout);
586 * Dumps to {@link System#err}.
589 @ManagedOperation("Dump the object to stderr")
590 public void dumpStdErr()
594 dump(System.err, "");
596 catch (IOException e)
603 @ManagedOperation("Dump the object to a string")
609 public static String dump(Dumpable dumpable)
611 StringBuilder b = new StringBuilder();
614 dumpable.dump(b, "");
616 catch (IOException e)
623 public void dump(Appendable out) throws IOException
628 protected void dumpThis(Appendable out) throws IOException
630 out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n");
633 public static void dumpObject(Appendable out, Object o) throws IOException
637 if (o instanceof LifeCycle)
638 out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n");
640 out.append(String.valueOf(o)).append("\n");
644 out.append(" => ").append(th.toString()).append('\n');
649 public void dump(Appendable out, String indent) throws IOException
651 dumpBeans(out,indent);
654 protected void dumpBeans(Appendable out, String indent, Collection<?>... collections) throws IOException
657 int size = _beans.size();
658 for (Collection<?> c : collections)
663 for (Bean b : _beans)
670 out.append(indent).append(" +- ");
671 if (b._bean instanceof Dumpable)
672 ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | "));
674 dumpObject(out, b._bean);
678 out.append(indent).append(" += ");
679 if (b._bean instanceof Dumpable)
680 ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | "));
682 dumpObject(out, b._bean);
686 out.append(indent).append(" +~ ");
687 dumpObject(out, b._bean);
691 out.append(indent).append(" +? ");
692 if (b._bean instanceof Dumpable)
693 ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | "));
695 dumpObject(out, b._bean);
702 out.append(indent).append(" |\n");
704 for (Collection<?> c : collections)
709 out.append(indent).append(" +> ");
711 if (o instanceof Dumpable)
712 ((Dumpable)o).dump(out, indent + (i == size ? " " : " | "));
719 public static void dump(Appendable out, String indent, Collection<?>... collections) throws IOException
721 if (collections.length == 0)
724 for (Collection<?> c : collections)
730 for (Collection<?> c : collections)
735 out.append(indent).append(" +- ");
737 if (o instanceof Dumpable)
738 ((Dumpable)o).dump(out, indent + (i == size ? " " : " | "));
746 enum Managed { POJO, MANAGED, UNMANAGED, AUTO };
748 private static class Bean
750 private final Object _bean;
751 private volatile Managed _managed = Managed.POJO;
753 private Bean(Object b)
758 public boolean isManaged()
760 return _managed==Managed.MANAGED;
764 public String toString()
766 return String.format("{%s,%s}", _bean, _managed);
770 public void updateBean(Object oldBean, final Object newBean)
772 if (newBean!=oldBean)
781 public void updateBeans(Object[] oldBeans, final Object[] newBeans)
783 // remove oldChildren not in newChildren
786 loop: for (Object o:oldBeans)
790 for (Object n:newBeans)
798 // add new beans not in old
801 loop: for (Object n:newBeans)
805 for (Object o:oldBeans)