Moved Lifecycle to samskivert (my initial desire to do this came from a
non-work project). I promise to stop fooling around now. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5805 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -34,8 +34,8 @@ import com.google.inject.Singleton;
|
||||
import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.IntSet;
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.annotation.AnyThread;
|
||||
|
||||
@@ -23,7 +23,7 @@ package com.threerings.crowd.peer.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.ChainedResultListener;
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.ResultListener;
|
||||
import com.samskivert.util.ResultListenerList;
|
||||
@@ -49,7 +50,6 @@ import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
|
||||
@@ -36,10 +36,10 @@ import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.annotation.EventThread;
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
import com.samskivert.util.RunQueue;
|
||||
import com.samskivert.util.SystemInfo;
|
||||
import com.threerings.util.Lifecycle;
|
||||
|
||||
import com.threerings.presents.annotation.AuthInvoker;
|
||||
import com.threerings.presents.annotation.EventQueue;
|
||||
|
||||
@@ -24,7 +24,7 @@ package com.threerings.presents.server;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.threerings.util.Lifecycle;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
|
||||
/**
|
||||
* Handles the orderly shutdown of all server services.
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.google.inject.Singleton;
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
import com.samskivert.util.LoopingThread;
|
||||
import com.samskivert.util.Queue;
|
||||
import com.samskivert.util.ResultListener;
|
||||
@@ -56,7 +57,6 @@ import com.threerings.io.FramingOutputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.UnreliableObjectInputStream;
|
||||
import com.threerings.io.UnreliableObjectOutputStream;
|
||||
import com.threerings.util.Lifecycle;
|
||||
|
||||
import com.threerings.presents.annotation.AuthInvoker;
|
||||
import com.threerings.presents.client.Client;
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2008 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.threerings.util.DependencyGraph;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Manages the lifecycle (initialization and shutdown) of a collection of components.
|
||||
*/
|
||||
public class Lifecycle
|
||||
{
|
||||
/** An interface implemented by components which wish to participate in the lifecycle. */
|
||||
public interface BaseComponent
|
||||
{
|
||||
}
|
||||
|
||||
/** An interface implemented by components which wish to participate in the lifecycle. */
|
||||
public interface InitComponent extends BaseComponent
|
||||
{
|
||||
/** Called after dependencies have been fully resolved to initialize this component. */
|
||||
public void init ();
|
||||
}
|
||||
|
||||
/** An interface implemented by components which wish to participate in the lifecycle. */
|
||||
public interface ShutdownComponent extends BaseComponent
|
||||
{
|
||||
/** Called when the server is shutting down. */
|
||||
public void shutdown ();
|
||||
}
|
||||
|
||||
/** An interface implemented by components which wish to participate in the lifecycle. */
|
||||
public interface Component extends InitComponent, ShutdownComponent
|
||||
{
|
||||
}
|
||||
|
||||
/** Constraints for use with {@link #addConstraint}. */
|
||||
public static enum Constraint { RUNS_BEFORE, RUNS_AFTER };
|
||||
|
||||
/**
|
||||
* Registers a component with the lifecycle. This should be done during dependency resolution
|
||||
* by injecting the Lifecycle into your constructor and calling this method there.
|
||||
*/
|
||||
public void addComponent (BaseComponent comp)
|
||||
{
|
||||
if (_initers == null || _downers == null) {
|
||||
throw new IllegalStateException("Too late to register component.");
|
||||
}
|
||||
if (comp instanceof InitComponent) {
|
||||
_initers.add((InitComponent)comp);
|
||||
}
|
||||
if (comp instanceof ShutdownComponent) {
|
||||
_downers.add((ShutdownComponent)comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a component from the lifecycle. This is generally not used.
|
||||
*/
|
||||
public void removeComponent (BaseComponent comp)
|
||||
{
|
||||
if (_initers != null && comp instanceof InitComponent) {
|
||||
_initers.remove((InitComponent)comp);
|
||||
}
|
||||
if (_downers != null && comp instanceof ShutdownComponent) {
|
||||
_downers.remove((ShutdownComponent)comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a constraint that a certain component must be initialized before another.
|
||||
*/
|
||||
public void addInitConstraint (InitComponent lhs, Constraint constraint, InitComponent rhs)
|
||||
{
|
||||
if (lhs == null || rhs == null) {
|
||||
throw new IllegalArgumentException("Cannot add constraint about null component.");
|
||||
}
|
||||
InitComponent before = (constraint == Constraint.RUNS_BEFORE) ? lhs : rhs;
|
||||
InitComponent after = (constraint == Constraint.RUNS_BEFORE) ? rhs : lhs;
|
||||
_initers.addDependency(after, before);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a constraint that a certain component must be shutdown before another.
|
||||
*/
|
||||
public void addShutdownConstraint (ShutdownComponent lhs, Constraint constraint,
|
||||
ShutdownComponent rhs)
|
||||
{
|
||||
if (lhs == null || rhs == null) {
|
||||
throw new IllegalArgumentException("Cannot add constraint about null component.");
|
||||
}
|
||||
ShutdownComponent before = (constraint == Constraint.RUNS_BEFORE) ? lhs : rhs;
|
||||
ShutdownComponent after = (constraint == Constraint.RUNS_BEFORE) ? rhs : lhs;
|
||||
_downers.addDependency(after, before);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we're in the process of shutting down.
|
||||
*/
|
||||
public boolean isShuttingDown ()
|
||||
{
|
||||
return (_downers == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all components immediately on the caller's thread.
|
||||
*/
|
||||
public void init ()
|
||||
{
|
||||
if (_initers == null) {
|
||||
log.warning("Refusing repeat init() request.");
|
||||
return;
|
||||
}
|
||||
|
||||
ObserverList<InitComponent> list = _initers.toObserverList();
|
||||
_initers = null;
|
||||
list.apply(new ObserverList.ObserverOp<InitComponent>() {
|
||||
public boolean apply (InitComponent comp) {
|
||||
log.debug("Initializing component", "comp", comp);
|
||||
comp.init();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down all components immediately on the caller's thread.
|
||||
*/
|
||||
public void shutdown ()
|
||||
{
|
||||
if (_downers == null) {
|
||||
log.warning("Refusing repeat shutdown() request.");
|
||||
return;
|
||||
}
|
||||
|
||||
ObserverList<ShutdownComponent> list = _downers.toObserverList();
|
||||
_downers = null;
|
||||
list.apply(new ObserverList.ObserverOp<ShutdownComponent>() {
|
||||
public boolean apply (ShutdownComponent comp) {
|
||||
log.debug("Shutting down component", "comp", comp);
|
||||
comp.shutdown();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** A dependency graph of our components arranged by initialization dependencies. */
|
||||
protected DependencyGraph<InitComponent> _initers = new DependencyGraph<InitComponent>();
|
||||
|
||||
/** A dependency graph of our components arranged by shutdown dependencies. */
|
||||
protected DependencyGraph<ShutdownComponent> _downers =
|
||||
new DependencyGraph<ShutdownComponent>();
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
/**
|
||||
* Maintains a bidirectional graph to manage the order that the items are removed. Children must
|
||||
* wait until their parents are accessed - thus removing an available element means that a node
|
||||
* without parents (an orphan) is removed and returned and the rest of the graph is updated to
|
||||
* reflect that removal.
|
||||
*
|
||||
* @param <T> the type of object maintained in the graph.
|
||||
*/
|
||||
public class DependencyGraph<T>
|
||||
{
|
||||
/**
|
||||
* Adds an element with no initial dependencies from the graph.
|
||||
*/
|
||||
public void add (T element)
|
||||
{
|
||||
DependencyNode<T> node = new DependencyNode<T>(element);
|
||||
_nodes.put(element, node);
|
||||
_orphans.add(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element and its dependencies from the graph.
|
||||
*/
|
||||
public void remove (T element)
|
||||
{
|
||||
DependencyNode<T> node = _nodes.remove(element);
|
||||
_orphans.remove(element);
|
||||
|
||||
// Remove ourselves as a child of our parents.
|
||||
for (DependencyNode<T> parent : node.parents) {
|
||||
parent.children.remove(node);
|
||||
}
|
||||
|
||||
// Remove ourselves as a parent of our children, possibly orphaning them.
|
||||
for (DependencyNode<T> child : node.children) {
|
||||
child.parents.remove(node);
|
||||
if (child.parents.isEmpty()) {
|
||||
_orphans.add(child.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns an element which is available, meaning not dependent upon any other
|
||||
* still in the graph.
|
||||
*/
|
||||
public T removeAvailableElement ()
|
||||
{
|
||||
T elem = _orphans.get(0);
|
||||
remove(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the graph.
|
||||
*/
|
||||
public int size ()
|
||||
{
|
||||
return _nodes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there are no more elements in the graph.
|
||||
*/
|
||||
public boolean isEmpty ()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a new dependency of the dependant upon the dependee.
|
||||
*/
|
||||
public void addDependency (T dependant, T dependee)
|
||||
{
|
||||
_orphans.remove(dependant);
|
||||
DependencyNode<T> dependantNode = _nodes.get(dependant);
|
||||
DependencyNode<T> dependeeNode = _nodes.get(dependee);
|
||||
|
||||
if (dependsOn(dependee, dependant)) {
|
||||
throw new IllegalArgumentException("Refusing to create circular dependency.");
|
||||
}
|
||||
dependantNode.parents.add(dependeeNode);
|
||||
dependeeNode.children.add(dependantNode);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether elem1 is designated to depend on elem2.
|
||||
*/
|
||||
public boolean dependsOn (T elem1, T elem2)
|
||||
{
|
||||
DependencyNode<T> node1 = _nodes.get(elem1);
|
||||
DependencyNode<T> node2 = _nodes.get(elem2);
|
||||
|
||||
ArrayList<DependencyNode<T>> nodesToCheck = Lists.newArrayList();
|
||||
ArrayList<DependencyNode<T>> nodesAlreadyChecked = Lists.newArrayList();
|
||||
nodesToCheck.addAll(node1.parents);
|
||||
|
||||
// We prevent circular dependencies when we add dependencies. Otherwise, this'd be
|
||||
// potentially non-terminating.
|
||||
while (!nodesToCheck.isEmpty()) {
|
||||
// We take it off the end since we don't care about order and this is faster.
|
||||
DependencyNode<T> checkNode = nodesToCheck.remove(nodesToCheck.size() - 1);
|
||||
|
||||
if (nodesAlreadyChecked.contains(checkNode)) {
|
||||
// We've seen him before, no need to check again.
|
||||
continue;
|
||||
|
||||
} else if (checkNode == node2) {
|
||||
// We've found our dependency
|
||||
return true;
|
||||
|
||||
} else {
|
||||
nodesAlreadyChecked.add(checkNode);
|
||||
nodesToCheck.addAll(checkNode.parents);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens this graph into an observer list in dependencys order. Empties the graph in the
|
||||
* process.
|
||||
*/
|
||||
public ObserverList<T> toObserverList ()
|
||||
{
|
||||
ObserverList<T> list = ObserverList.newSafeInOrder();
|
||||
while (!isEmpty()) {
|
||||
list.add(removeAvailableElement());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** All the nodes included in the graph. */
|
||||
protected HashMap<T, DependencyNode<T>> _nodes = Maps.newHashMap();
|
||||
|
||||
/** Nodes in the graph with no parents/dependencies. */
|
||||
protected ArrayList<T> _orphans = Lists.newArrayList();
|
||||
|
||||
/** Represents a node in our dependency graph. */
|
||||
protected class DependencyNode<DT>
|
||||
{
|
||||
public DT content;
|
||||
public ArrayList<DependencyNode<DT>> parents;
|
||||
public ArrayList<DependencyNode<DT>> children;
|
||||
|
||||
public DependencyNode (DT contents)
|
||||
{
|
||||
this.content = contents;
|
||||
this.parents = Lists.newArrayList();
|
||||
this.children = Lists.newArrayList();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user