X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Futil%2Fcomponent%2FContainerLifeCycle.java;fp=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Futil%2Fcomponent%2FContainerLifeCycle.java;h=464c0f7e4a88b18e2540b9c99d609076f4b91ecc;hp=0000000000000000000000000000000000000000;hb=73ef54a38e3930a1a789cdc6b5fa23cdd4c9d086;hpb=515007c7c1351045420669d65b59c08fa46850f2 diff --git a/lib/jetty/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/lib/jetty/org/eclipse/jetty/util/component/ContainerLifeCycle.java new file mode 100644 index 00000000..464c0f7e --- /dev/null +++ b/lib/jetty/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -0,0 +1,811 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util.component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.annotation.ManagedOperation; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +/** + * An ContainerLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans. + *

+ * 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. + * An unmanaged bean is associated with the aggregate for the purposes of {@link #dump()}, but it's lifecycle must be managed externally. + *

+ * When a {@link LifeCycle} bean is added without a managed state being specified the state is determined heuristically: + *

+ * When the container is started, then all contained managed beans will also be started. Any contained Auto beans + * will be check for their status and if already started will be switched unmanaged beans, else they will be + * started and switched to managed beans. Beans added after a container is started are not started and their state needs to + * be explicitly managed. + *

+ * When stopping the container, a contained bean will be stopped by this aggregate only if it + * is started by this aggregate. + *

+ * The methods {@link #addBean(Object, boolean)}, {@link #manage(Object)} and {@link #unmanage(Object)} can be used to + * explicitly control the life cycle relationship. + *

+ * 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 + * the API must be used to explicitly set it as unmanaged. + *

+ * 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: + *

+ * SomeContainerLifeCycleInstance
+ *   +- contained POJO instance
+ *   += contained MANAGED object, started and stopped with this instance
+ *   +~ referenced UNMANAGED object, with separate lifecycle
+ *   +? referenced AUTO object that could become MANAGED or UNMANAGED.
+ * 
+ */ + +/* ------------------------------------------------------------ */ +/** + */ +@ManagedObject("Implementation of Container and LifeCycle") +public class ContainerLifeCycle extends AbstractLifeCycle implements Container, Destroyable, Dumpable +{ + private static final Logger LOG = Log.getLogger(ContainerLifeCycle.class); + private final List _beans = new CopyOnWriteArrayList<>(); + private final List _listeners = new CopyOnWriteArrayList<>(); + private boolean _doStarted = false; + + + public ContainerLifeCycle() + { + } + + /** + * Starts the managed lifecycle beans in the order they were added. + */ + @Override + protected void doStart() throws Exception + { + // indicate that we are started, so that addBean will start other beans added. + _doStarted = true; + + // start our managed and auto beans + for (Bean b : _beans) + { + if (b._bean instanceof LifeCycle) + { + LifeCycle l = (LifeCycle)b._bean; + switch(b._managed) + { + case MANAGED: + if (!l.isRunning()) + start(l); + break; + case AUTO: + if (l.isRunning()) + unmanage(b); + else + { + manage(b); + start(l); + } + break; + } + } + } + + super.doStart(); + } + + /** + * Starts the given lifecycle. + * + * @param l + * @throws Exception + */ + protected void start(LifeCycle l) throws Exception + { + l.start(); + } + + /** + * Stops the given lifecycle. + * + * @param l + * @throws Exception + */ + protected void stop(LifeCycle l) throws Exception + { + l.stop(); + } + + /** + * Stops the managed lifecycle beans in the reverse order they were added. + */ + @Override + protected void doStop() throws Exception + { + _doStarted = false; + super.doStop(); + List reverse = new ArrayList<>(_beans); + Collections.reverse(reverse); + for (Bean b : reverse) + { + if (b._managed==Managed.MANAGED && b._bean instanceof LifeCycle) + { + LifeCycle l = (LifeCycle)b._bean; + if (l.isRunning()) + stop(l); + } + } + } + + /** + * Destroys the managed Destroyable beans in the reverse order they were added. + */ + @Override + public void destroy() + { + List reverse = new ArrayList<>(_beans); + Collections.reverse(reverse); + for (Bean b : reverse) + { + if (b._bean instanceof Destroyable && (b._managed==Managed.MANAGED || b._managed==Managed.POJO)) + { + Destroyable d = (Destroyable)b._bean; + d.destroy(); + } + } + _beans.clear(); + } + + + /** + * @param bean the bean to test + * @return whether this aggregate contains the bean + */ + public boolean contains(Object bean) + { + for (Bean b : _beans) + if (b._bean == bean) + return true; + return false; + } + + /** + * @param bean the bean to test + * @return whether this aggregate contains and manages the bean + */ + public boolean isManaged(Object bean) + { + for (Bean b : _beans) + if (b._bean == bean) + return b.isManaged(); + return false; + } + + /** + * Adds the given bean, detecting whether to manage it or not. + * If the bean is a {@link LifeCycle}, then it will be managed if it is not + * already started and not managed if it is already started. + * The {@link #addBean(Object, boolean)} + * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)} + * methods may be used after an add to change the status. + * + * @param o the bean object to add + * @return true if the bean was added, false if it was already present + */ + @Override + public boolean addBean(Object o) + { + if (o instanceof LifeCycle) + { + LifeCycle l = (LifeCycle)o; + return addBean(o,l.isRunning()?Managed.UNMANAGED:Managed.AUTO); + } + + return addBean(o,Managed.POJO); + } + + /** + * Adds the given bean, explicitly managing it or not. + * + * @param o The bean object to add + * @param managed whether to managed the lifecycle of the bean + * @return true if the bean was added, false if it was already present + */ + public boolean addBean(Object o, boolean managed) + { + if (o instanceof LifeCycle) + return addBean(o,managed?Managed.MANAGED:Managed.UNMANAGED); + return addBean(o,managed?Managed.POJO:Managed.UNMANAGED); + } + + public boolean addBean(Object o, Managed managed) + { + if (contains(o)) + return false; + + Bean new_bean = new Bean(o); + + // if the bean is a Listener + if (o instanceof Container.Listener) + addEventListener((Container.Listener)o); + + // Add the bean + _beans.add(new_bean); + + // Tell existing listeners about the new bean + for (Container.Listener l:_listeners) + l.beanAdded(this,o); + + try + { + switch (managed) + { + case UNMANAGED: + unmanage(new_bean); + break; + + case MANAGED: + manage(new_bean); + + if (isStarting() && _doStarted) + { + LifeCycle l = (LifeCycle)o; + if (!l.isRunning()) + start(l); + } + break; + + case AUTO: + if (o instanceof LifeCycle) + { + LifeCycle l = (LifeCycle)o; + if (isStarting()) + { + if (l.isRunning()) + unmanage(new_bean); + else if (_doStarted) + { + manage(new_bean); + start(l); + } + else + new_bean._managed=Managed.AUTO; + } + else if (isStarted()) + unmanage(new_bean); + else + new_bean._managed=Managed.AUTO; + } + else + new_bean._managed=Managed.POJO; + break; + + case POJO: + new_bean._managed=Managed.POJO; + } + } + catch (RuntimeException | Error e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + LOG.debug("{} added {}",this,new_bean); + + return true; + } + + + /* ------------------------------------------------------------ */ + /** Add a managed lifecycle. + *

This is a conveniance method that uses addBean(lifecycle,true) + * and then ensures that the added bean is started iff this container + * is running. Exception from nested calls to start are caught and + * wrapped as RuntimeExceptions + * @param lifecycle + */ + public void addManaged(LifeCycle lifecycle) + { + addBean(lifecycle,true); + try + { + if (isRunning() && !lifecycle.isRunning()) + start(lifecycle); + } + catch (RuntimeException | Error e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + public void addEventListener(Container.Listener listener) + { + if (_listeners.contains(listener)) + return; + + _listeners.add(listener); + + // tell it about existing beans + for (Bean b:_beans) + { + listener.beanAdded(this,b._bean); + + // handle inheritance + if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container) + { + if (b._bean instanceof ContainerLifeCycle) + ((ContainerLifeCycle)b._bean).addBean(listener, false); + else + ((Container)b._bean).addBean(listener); + } + } + } + + /** + * Manages a bean already contained by this aggregate, so that it is started/stopped/destroyed with this + * aggregate. + * + * @param bean The bean to manage (must already have been added). + */ + public void manage(Object bean) + { + for (Bean b : _beans) + { + if (b._bean == bean) + { + manage(b); + return; + } + } + throw new IllegalArgumentException("Unknown bean " + bean); + } + + private void manage(Bean bean) + { + if (bean._managed!=Managed.MANAGED) + { + bean._managed=Managed.MANAGED; + + if (bean._bean instanceof Container) + { + for (Container.Listener l:_listeners) + { + if (l instanceof InheritedListener) + { + if (bean._bean instanceof ContainerLifeCycle) + ((ContainerLifeCycle)bean._bean).addBean(l,false); + else + ((Container)bean._bean).addBean(l); + } + } + } + + if (bean._bean instanceof AbstractLifeCycle) + { + ((AbstractLifeCycle)bean._bean).setStopTimeout(getStopTimeout()); + } + } + } + + /** + * Unmanages a bean already contained by this aggregate, so that it is not started/stopped/destroyed with this + * aggregate. + * + * @param bean The bean to unmanage (must already have been added). + */ + public void unmanage(Object bean) + { + for (Bean b : _beans) + { + if (b._bean == bean) + { + unmanage(b); + return; + } + } + throw new IllegalArgumentException("Unknown bean " + bean); + } + + private void unmanage(Bean bean) + { + if (bean._managed!=Managed.UNMANAGED) + { + if (bean._managed==Managed.MANAGED && bean._bean instanceof Container) + { + for (Container.Listener l:_listeners) + { + if (l instanceof InheritedListener) + ((Container)bean._bean).removeBean(l); + } + } + bean._managed=Managed.UNMANAGED; + } + } + + @Override + public Collection getBeans() + { + return getBeans(Object.class); + } + + public void setBeans(Collection beans) + { + for (Object bean : beans) + addBean(bean); + } + + @Override + public Collection getBeans(Class clazz) + { + ArrayList beans = new ArrayList<>(); + for (Bean b : _beans) + { + if (clazz.isInstance(b._bean)) + beans.add(clazz.cast(b._bean)); + } + return beans; + } + + @Override + public T getBean(Class clazz) + { + for (Bean b : _beans) + { + if (clazz.isInstance(b._bean)) + return clazz.cast(b._bean); + } + return null; + } + + /** + * Removes all bean + */ + public void removeBeans() + { + ArrayList beans= new ArrayList<>(_beans); + for (Bean b : beans) + remove(b); + } + + private Bean getBean(Object o) + { + for (Bean b : _beans) + { + if (b._bean == o) + return b; + } + return null; + } + + @Override + public boolean removeBean(Object o) + { + Bean b=getBean(o); + return b!=null && remove(b); + } + + private boolean remove(Bean bean) + { + if (_beans.remove(bean)) + { + + unmanage(bean); + + for (Container.Listener l:_listeners) + l.beanRemoved(this,bean._bean); + + if (bean._bean instanceof Container.Listener) + removeEventListener((Container.Listener)bean._bean); + + // stop managed beans + if (bean._managed==Managed.MANAGED && bean._bean instanceof LifeCycle) + { + try + { + stop((LifeCycle)bean._bean); + } + catch(RuntimeException | Error e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + return true; + } + return false; + } + + @Override + public void removeEventListener(Container.Listener listener) + { + if (_listeners.remove(listener)) + { + // remove existing beans + for (Bean b:_beans) + { + listener.beanRemoved(this,b._bean); + + if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container) + ((Container)b._bean).removeBean(listener); + } + } + } + + @Override + public void setStopTimeout(long stopTimeout) + { + super.setStopTimeout(stopTimeout); + for (Bean bean : _beans) + { + if (bean.isManaged() && bean._bean instanceof AbstractLifeCycle) + ((AbstractLifeCycle)bean._bean).setStopTimeout(stopTimeout); + } + } + + /** + * Dumps to {@link System#err}. + * @see #dump() + */ + @ManagedOperation("Dump the object to stderr") + public void dumpStdErr() + { + try + { + dump(System.err, ""); + } + catch (IOException e) + { + LOG.warn(e); + } + } + + @Override + @ManagedOperation("Dump the object to a string") + public String dump() + { + return dump(this); + } + + public static String dump(Dumpable dumpable) + { + StringBuilder b = new StringBuilder(); + try + { + dumpable.dump(b, ""); + } + catch (IOException e) + { + LOG.warn(e); + } + return b.toString(); + } + + public void dump(Appendable out) throws IOException + { + dump(out, ""); + } + + protected void dumpThis(Appendable out) throws IOException + { + out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n"); + } + + public static void dumpObject(Appendable out, Object o) throws IOException + { + try + { + if (o instanceof LifeCycle) + out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n"); + else + out.append(String.valueOf(o)).append("\n"); + } + catch (Throwable th) + { + out.append(" => ").append(th.toString()).append('\n'); + } + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + dumpBeans(out,indent); + } + + protected void dumpBeans(Appendable out, String indent, Collection... collections) throws IOException + { + dumpThis(out); + int size = _beans.size(); + for (Collection c : collections) + size += c.size(); + if (size == 0) + return; + int i = 0; + for (Bean b : _beans) + { + i++; + + switch(b._managed) + { + case POJO: + out.append(indent).append(" +- "); + if (b._bean instanceof Dumpable) + ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, b._bean); + break; + + case MANAGED: + out.append(indent).append(" += "); + if (b._bean instanceof Dumpable) + ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, b._bean); + break; + + case UNMANAGED: + out.append(indent).append(" +~ "); + dumpObject(out, b._bean); + break; + + case AUTO: + out.append(indent).append(" +? "); + if (b._bean instanceof Dumpable) + ((Dumpable)b._bean).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, b._bean); + break; + + } + } + + if (i c : collections) + { + for (Object o : c) + { + i++; + out.append(indent).append(" +> "); + + if (o instanceof Dumpable) + ((Dumpable)o).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, o); + } + } + } + + public static void dump(Appendable out, String indent, Collection... collections) throws IOException + { + if (collections.length == 0) + return; + int size = 0; + for (Collection c : collections) + size += c.size(); + if (size == 0) + return; + + int i = 0; + for (Collection c : collections) + { + for (Object o : c) + { + i++; + out.append(indent).append(" +- "); + + if (o instanceof Dumpable) + ((Dumpable)o).dump(out, indent + (i == size ? " " : " | ")); + else + dumpObject(out, o); + } + } + } + + + enum Managed { POJO, MANAGED, UNMANAGED, AUTO }; + + private static class Bean + { + private final Object _bean; + private volatile Managed _managed = Managed.POJO; + + private Bean(Object b) + { + _bean = b; + } + + public boolean isManaged() + { + return _managed==Managed.MANAGED; + } + + @Override + public String toString() + { + return String.format("{%s,%s}", _bean, _managed); + } + } + + public void updateBean(Object oldBean, final Object newBean) + { + if (newBean!=oldBean) + { + if (oldBean!=null) + removeBean(oldBean); + if (newBean!=null) + addBean(newBean); + } + } + + public void updateBeans(Object[] oldBeans, final Object[] newBeans) + { + // remove oldChildren not in newChildren + if (oldBeans!=null) + { + loop: for (Object o:oldBeans) + { + if (newBeans!=null) + { + for (Object n:newBeans) + if (o==n) + continue loop; + } + removeBean(o); + } + } + + // add new beans not in old + if (newBeans!=null) + { + loop: for (Object n:newBeans) + { + if (oldBeans!=null) + { + for (Object o:oldBeans) + if (o==n) + continue loop; + } + addBean(n); + } + } + } +}