2 // ========================================================================
3 // Copyright (c) 1995-2014 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 LOG.debug("{} added {}",this,new_bean);
329 /* ------------------------------------------------------------ */
330 /** Add a managed lifecycle.
331 * <p>This is a conveniance method that uses addBean(lifecycle,true)
332 * and then ensures that the added bean is started iff this container
333 * is running. Exception from nested calls to start are caught and
334 * wrapped as RuntimeExceptions
337 public void addManaged(LifeCycle lifecycle)
339 addBean(lifecycle,true);
342 if (isRunning() && !lifecycle.isRunning())
345 catch (RuntimeException | Error e)
351 throw new RuntimeException(e);
356 public void addEventListener(Container.Listener listener)
358 if (_listeners.contains(listener))
361 _listeners.add(listener);
363 // tell it about existing beans
366 listener.beanAdded(this,b._bean);
368 // handle inheritance
369 if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
371 if (b._bean instanceof ContainerLifeCycle)
372 ((ContainerLifeCycle)b._bean).addBean(listener, false);
374 ((Container)b._bean).addBean(listener);
380 * Manages a bean already contained by this aggregate, so that it is started/stopped/destroyed with this
383 * @param bean The bean to manage (must already have been added).
385 public void manage(Object bean)
387 for (Bean b : _beans)
395 throw new IllegalArgumentException("Unknown bean " + bean);
398 private void manage(Bean bean)
400 if (bean._managed!=Managed.MANAGED)
402 bean._managed=Managed.MANAGED;
404 if (bean._bean instanceof Container)
406 for (Container.Listener l:_listeners)
408 if (l instanceof InheritedListener)
410 if (bean._bean instanceof ContainerLifeCycle)
411 ((ContainerLifeCycle)bean._bean).addBean(l,false);
413 ((Container)bean._bean).addBean(l);
418 if (bean._bean instanceof AbstractLifeCycle)
420 ((AbstractLifeCycle)bean._bean).setStopTimeout(getStopTimeout());
426 * Unmanages a bean already contained by this aggregate, so that it is not started/stopped/destroyed with this
429 * @param bean The bean to unmanage (must already have been added).
431 public void unmanage(Object bean)
433 for (Bean b : _beans)
441 throw new IllegalArgumentException("Unknown bean " + bean);
444 private void unmanage(Bean bean)
446 if (bean._managed!=Managed.UNMANAGED)
448 if (bean._managed==Managed.MANAGED && bean._bean instanceof Container)
450 for (Container.Listener l:_listeners)
452 if (l instanceof InheritedListener)
453 ((Container)bean._bean).removeBean(l);
456 bean._managed=Managed.UNMANAGED;
461 public Collection<Object> getBeans()
463 return getBeans(Object.class);
466 public void setBeans(Collection<Object> beans)
468 for (Object bean : beans)
473 public <T> Collection<T> getBeans(Class<T> clazz)
475 ArrayList<T> beans = new ArrayList<>();
476 for (Bean b : _beans)
478 if (clazz.isInstance(b._bean))
479 beans.add(clazz.cast(b._bean));
485 public <T> T getBean(Class<T> clazz)
487 for (Bean b : _beans)
489 if (clazz.isInstance(b._bean))
490 return clazz.cast(b._bean);
498 public void removeBeans()
500 ArrayList<Bean> beans= new ArrayList<>(_beans);
505 private Bean getBean(Object o)
507 for (Bean b : _beans)
516 public boolean removeBean(Object o)
519 return b!=null && remove(b);
522 private boolean remove(Bean bean)
524 if (_beans.remove(bean))
529 for (Container.Listener l:_listeners)
530 l.beanRemoved(this,bean._bean);
532 if (bean._bean instanceof Container.Listener)
533 removeEventListener((Container.Listener)bean._bean);
535 // stop managed beans
536 if (bean._managed==Managed.MANAGED && bean._bean instanceof LifeCycle)
540 stop((LifeCycle)bean._bean);
542 catch(RuntimeException | Error e)
548 throw new RuntimeException(e);
557 public void removeEventListener(Container.Listener listener)
559 if (_listeners.remove(listener))
561 // remove existing beans
564 listener.beanRemoved(this,b._bean);
566 if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
567 ((Container)b._bean).removeBean(listener);
573 public void setStopTimeout(long stopTimeout)
575 super.setStopTimeout(stopTimeout);
576 for (Bean bean : _beans)
578 if (bean.isManaged() && bean._bean instanceof AbstractLifeCycle)
579 ((AbstractLifeCycle)bean._bean).setStopTimeout(stopTimeout);
584 * Dumps to {@link System#err}.
587 @ManagedOperation("Dump the object to stderr")
588 public void dumpStdErr()
592 dump(System.err, "");
594 catch (IOException e)
601 @ManagedOperation("Dump the object to a string")
607 public static String dump(Dumpable dumpable)
609 StringBuilder b = new StringBuilder();
612 dumpable.dump(b, "");
614 catch (IOException e)
621 public void dump(Appendable out) throws IOException
626 protected void dumpThis(Appendable out) throws IOException
628 out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n");
631 public static void dumpObject(Appendable out, Object o) throws IOException
635 if (o instanceof LifeCycle)
636 out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n");
638 out.append(String.valueOf(o)).append("\n");
642 out.append(" => ").append(th.toString()).append('\n');
647 public void dump(Appendable out, String indent) throws IOException
649 dumpBeans(out,indent);
652 protected void dumpBeans(Appendable out, String indent, Collection<?>... collections) throws IOException
655 int size = _beans.size();
656 for (Collection<?> c : collections)
661 for (Bean b : _beans)
668 out.append(indent).append(" +- ");
669 if (b._bean instanceof Dumpable)
670 ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | "));
672 dumpObject(out, b._bean);
676 out.append(indent).append(" += ");
677 if (b._bean instanceof Dumpable)
678 ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | "));
680 dumpObject(out, b._bean);
684 out.append(indent).append(" +~ ");
685 dumpObject(out, b._bean);
689 out.append(indent).append(" +? ");
690 if (b._bean instanceof Dumpable)
691 ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | "));
693 dumpObject(out, b._bean);
700 out.append(indent).append(" |\n");
702 for (Collection<?> c : collections)
707 out.append(indent).append(" +> ");
709 if (o instanceof Dumpable)
710 ((Dumpable)o).dump(out, indent + (i == size ? " " : " | "));
717 public static void dump(Appendable out, String indent, Collection<?>... collections) throws IOException
719 if (collections.length == 0)
722 for (Collection<?> c : collections)
728 for (Collection<?> c : collections)
733 out.append(indent).append(" +- ");
735 if (o instanceof Dumpable)
736 ((Dumpable)o).dump(out, indent + (i == size ? " " : " | "));
744 enum Managed { POJO, MANAGED, UNMANAGED, AUTO };
746 private static class Bean
748 private final Object _bean;
749 private volatile Managed _managed = Managed.POJO;
751 private Bean(Object b)
756 public boolean isManaged()
758 return _managed==Managed.MANAGED;
762 public String toString()
764 return String.format("{%s,%s}", _bean, _managed);
768 public void updateBean(Object oldBean, final Object newBean)
770 if (newBean!=oldBean)
779 public void updateBeans(Object[] oldBeans, final Object[] newBeans)
781 // remove oldChildren not in newChildren
784 loop: for (Object o:oldBeans)
788 for (Object n:newBeans)
796 // add new beans not in old
799 loop: for (Object n:newBeans)
803 for (Object o:oldBeans)