Lifecycle management helper classes.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2559 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
samskivert
2009-05-27 19:15:18 +00:00
parent 933c486986
commit 68900548c1
2 changed files with 349 additions and 0 deletions
@@ -0,0 +1,179 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2009 Michael Bayne
//
// 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.samskivert.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 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);
List<DependencyNode<T>> nodesToCheck = new ArrayList<DependencyNode<T>>();
List<DependencyNode<T>> nodesAlreadyChecked = new ArrayList<DependencyNode<T>>();
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 Map<T, DependencyNode<T>> _nodes = new HashMap<T, DependencyNode<T>>();
/** Nodes in the graph with no parents/dependencies. */
protected List<T> _orphans = new ArrayList<T>();
/** Represents a node in our dependency graph. */
protected static class DependencyNode<DT>
{
public DT content;
public List<DependencyNode<DT>> parents = new ArrayList<DependencyNode<DT>>();
public List<DependencyNode<DT>> children = new ArrayList<DependencyNode<DT>>();
public DependencyNode (DT contents) {
this.content = contents;
}
}
}
+170
View File
@@ -0,0 +1,170 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2009 Michael Bayne
//
// 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.samskivert.util;
import static com.samskivert.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>();
}