diff --git a/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java b/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java
index 27d9cd1b8..914f8ee63 100644
--- a/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java
+++ b/src/java/com/threerings/crowd/chat/server/ChatChannelManager.java
@@ -43,8 +43,8 @@ import com.threerings.presents.peer.data.ClientInfo;
import com.threerings.presents.peer.data.NodeObject;
import com.threerings.presents.peer.server.PeerManager;
import com.threerings.presents.server.InvocationManager;
+import com.threerings.presents.server.LifecycleManager;
import com.threerings.presents.server.PresentsDObjectMgr;
-import com.threerings.presents.server.ShutdownManager;
import com.threerings.crowd.chat.data.ChatChannel;
import com.threerings.crowd.chat.data.ChatCodes;
@@ -62,7 +62,7 @@ import static com.threerings.crowd.Log.log;
*/
@Singleton
public abstract class ChatChannelManager
- implements ChannelSpeakProvider, ShutdownManager.Shutdowner
+ implements ChannelSpeakProvider, LifecycleManager.ShutdownComponent
{
/**
* When a body becomes a member of a channel, this method should be called so that any server
@@ -114,7 +114,7 @@ public abstract class ChatChannelManager
});
}
- // from interface ShutdownManager.Shutdowner
+ // from interface LifecycleManager.Shutdowner
public void shutdown ()
{
// stop our channel closer; always be closing... except now
@@ -126,15 +126,14 @@ public abstract class ChatChannelManager
* Creates our singleton manager and registers our invocation service.
*/
@Inject protected ChatChannelManager (PresentsDObjectMgr omgr, InvocationManager invmgr,
- ShutdownManager shutmgr)
+ LifecycleManager lifemgr)
{
invmgr.registerDispatcher(new ChannelSpeakDispatcher(this), CrowdCodes.CROWD_GROUP);
- shutmgr.registerShutdowner(this);
+ lifemgr.addComponent(this);
// create and start our idle channel closer (always be closing)
_closer = new Interval(omgr) {
- @Override
- public void expired () {
+ @Override public void expired () {
closeIdleChannels();
}
};
diff --git a/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java b/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java
index f34d727bc..a854ff102 100644
--- a/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java
+++ b/src/java/com/threerings/crowd/peer/server/CrowdPeerManager.java
@@ -32,8 +32,8 @@ import com.threerings.presents.peer.server.PeerManager;
import com.threerings.presents.peer.server.PeerNode;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
+import com.threerings.presents.server.LifecycleManager;
import com.threerings.presents.server.PresentsSession;
-import com.threerings.presents.server.ShutdownManager;
import com.threerings.crowd.chat.client.ChatService;
import com.threerings.crowd.chat.data.UserMessage;
@@ -51,9 +51,9 @@ public abstract class CrowdPeerManager extends PeerManager
/**
* Creates an uninitialized peer manager.
*/
- @Inject public CrowdPeerManager (ShutdownManager shutmgr)
+ @Inject public CrowdPeerManager (LifecycleManager lifeMgr)
{
- super(shutmgr);
+ super(lifeMgr);
}
// from interface CrowdPeerProvider
diff --git a/src/java/com/threerings/crowd/server/PlaceRegistry.java b/src/java/com/threerings/crowd/server/PlaceRegistry.java
index 2fdad325b..3d559645d 100644
--- a/src/java/com/threerings/crowd/server/PlaceRegistry.java
+++ b/src/java/com/threerings/crowd/server/PlaceRegistry.java
@@ -34,7 +34,7 @@ import com.samskivert.util.IntMaps;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
-import com.threerings.presents.server.ShutdownManager;
+import com.threerings.presents.server.LifecycleManager;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
@@ -48,7 +48,7 @@ import static com.threerings.crowd.Log.log;
*/
@Singleton
public class PlaceRegistry
- implements ShutdownManager.Shutdowner
+ implements LifecycleManager.ShutdownComponent
{
/** Used in conjunction with {@link PlaceRegistry#createPlace(PlaceConfig,PreStartupHook)}. */
public static interface PreStartupHook
@@ -60,9 +60,9 @@ public class PlaceRegistry
* Creates and initializes the place registry. This is called by the server during its
* initialization phase.
*/
- @Inject public PlaceRegistry (ShutdownManager shutmgr)
+ @Inject public PlaceRegistry (LifecycleManager lifemgr)
{
- shutmgr.registerShutdowner(this);
+ lifemgr.addComponent(this);
}
/**
@@ -155,7 +155,7 @@ public class PlaceRegistry
return _pmgrs.values().iterator();
}
- // from interface ShutdownManager.Shutdowner
+ // from interface LifecycleManager.ShutdownComponent
public void shutdown ()
{
// shut down all active places
diff --git a/src/java/com/threerings/presents/peer/server/PeerManager.java b/src/java/com/threerings/presents/peer/server/PeerManager.java
index 15fedf3d4..535059723 100644
--- a/src/java/com/threerings/presents/peer/server/PeerManager.java
+++ b/src/java/com/threerings/presents/peer/server/PeerManager.java
@@ -65,10 +65,10 @@ import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.ClientManager;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
-import com.threerings.presents.server.PresentsSession;
+import com.threerings.presents.server.LifecycleManager;
import com.threerings.presents.server.PresentsDObjectMgr;
+import com.threerings.presents.server.PresentsSession;
import com.threerings.presents.server.ReportManager;
-import com.threerings.presents.server.ShutdownManager;
import com.threerings.presents.server.net.ConnectionManager;
import com.threerings.presents.peer.client.PeerService;
@@ -86,7 +86,7 @@ import static com.threerings.presents.Log.log;
* servers and uses those objects to communicate cross-node information.
*/
public abstract class PeerManager
- implements PeerProvider, ClientManager.ClientObserver, ShutdownManager.Shutdowner
+ implements PeerProvider, ClientManager.ClientObserver, LifecycleManager.ShutdownComponent
{
/**
* Used by entities that wish to know when cached data has become stale due to a change on
@@ -198,9 +198,9 @@ public abstract class PeerManager
/**
* Creates an uninitialized peer manager.
*/
- @Inject public PeerManager (ShutdownManager shutmgr)
+ @Inject public PeerManager (LifecycleManager lifeMgr)
{
- shutmgr.registerShutdowner(this);
+ lifeMgr.addComponent(this);
}
/**
@@ -753,7 +753,7 @@ public abstract class PeerManager
return _stats.clone();
}
- // from interface ShutdownManager.Shutdowner
+ // from interface LifecycleManager.ShutdownComponent
public void shutdown ()
{
if (_nodeName == null) { // sanity check
diff --git a/src/java/com/threerings/presents/server/AbstractSignalHandler.java b/src/java/com/threerings/presents/server/AbstractSignalHandler.java
index 742cc471f..843353500 100644
--- a/src/java/com/threerings/presents/server/AbstractSignalHandler.java
+++ b/src/java/com/threerings/presents/server/AbstractSignalHandler.java
@@ -52,7 +52,7 @@ public abstract class AbstractSignalHandler
protected void termReceived ()
{
log.info("Shutdown initiated by TERM signal.");
- _shutmgr.queueShutdown();
+ _lifemgr.queueShutdown();
}
/**
@@ -61,7 +61,7 @@ public abstract class AbstractSignalHandler
protected void intReceived ()
{
log.info("Shutdown initiated by INT signal.");
- _shutmgr.queueShutdown();
+ _lifemgr.queueShutdown();
}
/**
@@ -72,6 +72,6 @@ public abstract class AbstractSignalHandler
log.info(_repmgr.generateReport(ReportManager.DEFAULT_TYPE));
}
- @Inject protected ShutdownManager _shutmgr;
+ @Inject protected LifecycleManager _lifemgr;
@Inject protected ReportManager _repmgr;
}
diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java
index e1e4bd9a1..aabcf71fd 100644
--- a/src/java/com/threerings/presents/server/ClientManager.java
+++ b/src/java/com/threerings/presents/server/ClientManager.java
@@ -62,7 +62,7 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class ClientManager
- implements ClientResolutionListener, ReportManager.Reporter, ShutdownManager.Shutdowner
+ implements ClientResolutionListener, ReportManager.Reporter, LifecycleManager.Component
{
/**
* Used by {@link ClientManager#applyToClient}.
@@ -100,21 +100,10 @@ public class ClientManager
/**
* Constructs a client manager that will interact with the supplied connection manager.
*/
- @Inject public ClientManager (
- ReportManager repmgr, ShutdownManager shutmgr, PresentsDObjectMgr omgr)
+ @Inject public ClientManager (ReportManager repmgr, LifecycleManager lifemgr)
{
repmgr.registerReporter(this);
- shutmgr.registerShutdowner(this);
-
- // start up an interval that will check for expired clients and flush them from the bowels
- // of the server
- _flushClients = new Interval(omgr) {
- @Override
- public void expired () {
- flushClients();
- }
- };
- _flushClients.schedule(CLIENT_FLUSH_INTERVAL, true);
+ lifemgr.addComponent(this);
}
/**
@@ -126,26 +115,6 @@ public class ClientManager
_injector = injector;
}
- // from interface ShutdownManager.Shutdowner
- public void shutdown ()
- {
- log.info("Client manager shutting down", "ccount", _usermap.size());
-
- _flushClients.cancel();
-
- // inform all of our clients that they are being shut down
- synchronized (_usermap) {
- for (PresentsSession pc : _usermap.values()) {
- try {
- pc.shutdown();
- } catch (Exception e) {
- log.warning("Client choked in shutdown()",
- "client", StringUtil.safeToString(pc), e);
- }
- }
- }
- }
-
/**
* Configures the client manager with a factory for creating {@link PresentsSession} and {@link
* ClientResolver} classes for authenticated client connections.
@@ -354,6 +323,38 @@ public class ClientManager
_omgr.destroyObject(clobj.getOid());
}
+ // from interface LifecycleManager.Component
+ public void init ()
+ {
+ // start up an interval that will check for and flush expired clients
+ _flushClients = new Interval(_omgr) {
+ @Override public void expired () {
+ flushClients();
+ }
+ };
+ _flushClients.schedule(CLIENT_FLUSH_INTERVAL, true);
+ }
+
+ // from interface LifecycleManager.Component
+ public void shutdown ()
+ {
+ log.info("Client manager shutting down", "ccount", _usermap.size());
+
+ _flushClients.cancel();
+
+ // inform all of our clients that they are being shut down
+ synchronized (_usermap) {
+ for (PresentsSession pc : _usermap.values()) {
+ try {
+ pc.shutdown();
+ } catch (Exception e) {
+ log.warning("Client choked in shutdown()",
+ "client", StringUtil.safeToString(pc), e);
+ }
+ }
+ }
+ }
+
/**
* Renames a currently connected client from oldname to newname.
*
diff --git a/src/java/com/threerings/presents/server/LifecycleManager.java b/src/java/com/threerings/presents/server/LifecycleManager.java
new file mode 100644
index 000000000..aebea38ac
--- /dev/null
+++ b/src/java/com/threerings/presents/server/LifecycleManager.java
@@ -0,0 +1,199 @@
+//
+// $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.presents.server;
+
+import com.google.inject.Inject;
+
+import com.samskivert.util.ObserverList;
+import com.samskivert.util.RunQueue;
+import com.threerings.util.DependencyGraph;
+
+import com.threerings.presents.annotation.EventQueue;
+
+import static com.threerings.presents.Log.log;
+
+/**
+ * Manages the lifecycle (initialization and shutdown) of our various server managers and other
+ * entities.
+ */
+public class LifecycleManager
+{
+ /** 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 ();
+
+ /** Called when the server is shutting down. */
+ public void shutdown ();
+ }
+
+ /** 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 manager. This should be done during the dependency resolution
+ * phase by injecting the LifecycleManager 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 manager. 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);
+ }
+
+ /**
+ * Queues up a request to shutdown on the event thread. This method may be safely called from
+ * any thread.
+ */
+ public void queueShutdown ()
+ {
+ _eventQueue.postRunnable(new Runnable() {
+ public void run () {
+ shutdown();
+ }
+ });
+ }
+
+ /**
+ * 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 list = _initers.toObserverList();
+ _initers = null;
+ list.apply(new ObserverList.ObserverOp() {
+ 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 list = _downers.toObserverList();
+ _downers = null;
+ list.apply(new ObserverList.ObserverOp() {
+ public boolean apply (ShutdownComponent comp) {
+ log.debug("Shutting down component", "comp", comp);
+ comp.shutdown();
+ return true;
+ }
+ });
+ }
+
+ /** The queue we'll use to get onto the event thread before shutting down. */
+ @Inject @EventQueue protected RunQueue _eventQueue;
+
+ /** A dependency graph of our components arranged by initialization dependencies. */
+ protected DependencyGraph _initers = new DependencyGraph();
+
+ /** A dependency graph of our components arranged by shutdown dependencies. */
+ protected DependencyGraph _downers =
+ new DependencyGraph();
+}
diff --git a/src/java/com/threerings/presents/server/PresentsInvoker.java b/src/java/com/threerings/presents/server/PresentsInvoker.java
index 0c59d695d..7f8a6b923 100644
--- a/src/java/com/threerings/presents/server/PresentsInvoker.java
+++ b/src/java/com/threerings/presents/server/PresentsInvoker.java
@@ -33,17 +33,17 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class PresentsInvoker extends ReportingInvoker
- implements ShutdownManager.Shutdowner
+ implements LifecycleManager.ShutdownComponent
{
- @Inject public PresentsInvoker (
- PresentsDObjectMgr omgr, ShutdownManager shutmgr, ReportManager repmgr)
+ @Inject public PresentsInvoker (PresentsDObjectMgr omgr, LifecycleManager lifemgr,
+ ReportManager repmgr)
{
super("presents.Invoker", omgr, repmgr);
_omgr = omgr;
- shutmgr.registerShutdowner(this);
+ lifemgr.addComponent(this);
}
- @Override // from Invoker
+ @Override // from Invoker, LifecycleManager.ShutdownComponent
public void shutdown ()
{
// this will do a sophisticated shutdown of both ourself and the dobjmgr; note: we
diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java
index a49df79f5..9a2d1437b 100644
--- a/src/java/com/threerings/presents/server/PresentsServer.java
+++ b/src/java/com/threerings/presents/server/PresentsServer.java
@@ -159,10 +159,13 @@ public class PresentsServer
// initialize the time base services
TimeBaseProvider.init(_invmgr, _omgr);
- // Make the client manager shut down before the invoker and dobj threads. This will help
- // application code to avoid long chains of shutdown constraints (e.g. msoy bureau manager).
- _shutmgr.addConstraint(
- _clmgr, ShutdownManager.Constraint.RUNS_BEFORE, (PresentsInvoker)_invoker);
+ // make the client manager shut down before the invoker and dobj threads; this helps
+ // application code to avoid long chains of shutdown constraints
+ _lifemgr.addShutdownConstraint(
+ _clmgr, LifecycleManager.Constraint.RUNS_BEFORE, (PresentsInvoker)_invoker);
+
+ // initialize all of our registered components
+ _lifemgr.init();
}
/**
@@ -238,8 +241,8 @@ public class PresentsServer
/** The manager of invocation services. */
@Inject protected InvocationManager _invmgr;
- /** Handles orderly shutdown of our managers, etc. */
- @Inject protected ShutdownManager _shutmgr;
+ /** Handles orderly initialization and shutdown of our managers, etc. */
+ @Inject protected LifecycleManager _lifemgr;
/** Handles generation of state of the server reports. */
@Inject protected ReportManager _repmgr;
diff --git a/src/java/com/threerings/presents/server/RebootManager.java b/src/java/com/threerings/presents/server/RebootManager.java
index 4ed7582b1..37b488b4b 100644
--- a/src/java/com/threerings/presents/server/RebootManager.java
+++ b/src/java/com/threerings/presents/server/RebootManager.java
@@ -155,8 +155,7 @@ public abstract class RebootManager
_rebootSoon = false;
long firstWarnTime = (_nextReboot - (WARNINGS[0] * 60 * 1000)) - now;
_interval = new Interval(_omgr) {
- @Override
- public void expired () {
+ @Override public void expired () {
doWarning(0);
}
};
@@ -189,9 +188,9 @@ public abstract class RebootManager
/**
* Provides us with our dependencies.
*/
- protected RebootManager (ShutdownManager shutmgr, RootDObjectManager omgr)
+ protected RebootManager (LifecycleManager lmgr, RootDObjectManager omgr)
{
- _shutmgr = shutmgr;
+ _lmgr = lmgr;
_omgr = omgr;
}
@@ -257,12 +256,11 @@ public abstract class RebootManager
// wait 1 second, then do it
new Interval() { // Note: This interval does not run on the dobj thread
- @Override
- public void expired () {
+ @Override public void expired () {
// ...but we then post a LongRunnable...
_omgr.postRunnable(new PresentsDObjectMgr.LongRunnable() {
public void run () {
- _shutmgr.shutdown();
+ _lmgr.shutdown();
}
});
}
@@ -279,8 +277,7 @@ public abstract class RebootManager
// schedule the next warning
_interval = new Interval(_omgr) {
- @Override
- public void expired () {
+ @Override public void expired () {
doWarning(level + 1);
}
};
@@ -301,8 +298,7 @@ public abstract class RebootManager
log.info("Reboot delayed due to outstanding locks", "locks", _rebootLocks.elements());
broadcast("m.reboot_delayed");
_interval = new Interval(_omgr) {
- @Override
- public void expired () {
+ @Override public void expired () {
doWarning(WARNINGS.length);
}
};
@@ -325,8 +321,8 @@ public abstract class RebootManager
});
}
- /** Our shutdown manager, which is how we will reboot. */
- protected ShutdownManager _shutmgr;
+ /** Our lifecycle manager, which is how we will reboot. */
+ protected LifecycleManager _lmgr;
/** Our distributed object manager. */
protected RootDObjectManager _omgr;
diff --git a/src/java/com/threerings/presents/server/ShutdownManager.java b/src/java/com/threerings/presents/server/ShutdownManager.java
index bfaed9a1a..a418e4607 100644
--- a/src/java/com/threerings/presents/server/ShutdownManager.java
+++ b/src/java/com/threerings/presents/server/ShutdownManager.java
@@ -24,44 +24,28 @@ package com.threerings.presents.server;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import com.samskivert.util.ObserverList;
-import com.samskivert.util.RunQueue;
-
-import com.threerings.util.DependencyGraph;
-
-import com.threerings.presents.annotation.EventQueue;
-
-import static com.threerings.presents.Log.log;
-
/**
* Handles the orderly shutdown of all server services.
+ *
+ * @Deprecated use LifecycleManager
*/
@Singleton
public class ShutdownManager
{
/** Implementers of this interface will be notified when the server is shutting down. */
- public static interface Shutdowner
+ public static interface Shutdowner extends LifecycleManager.ShutdownComponent
{
- /**
- * Called when the server is shutting down.
- */
- void shutdown ();
}
/** Constraints for use with {@link ShutdownManager#addConstraint}. */
public static enum Constraint { RUNS_BEFORE, RUNS_AFTER };
- @Inject ShutdownManager (@EventQueue RunQueue dobjq)
- {
- _dobjq = dobjq;
- }
-
/**
* Registers an entity that will be notified when the server is shutting down.
*/
public void registerShutdowner (Shutdowner downer)
{
- _downers.add(downer);
+ _lifeMgr.addComponent(downer);
}
/**
@@ -69,7 +53,7 @@ public class ShutdownManager
*/
public void unregisterShutdowner (Shutdowner downer)
{
- _downers.remove(downer);
+ _lifeMgr.removeComponent(downer);
}
/**
@@ -77,12 +61,14 @@ public class ShutdownManager
*/
public void addConstraint (Shutdowner lhs, Constraint constraint, Shutdowner rhs)
{
- if (lhs == null || rhs == null) {
- throw new IllegalArgumentException("Cannot add constraint about null shutdowner.");
+ switch (constraint) {
+ case RUNS_BEFORE:
+ _lifeMgr.addShutdownConstraint(lhs, LifecycleManager.Constraint.RUNS_BEFORE, rhs);
+ break;
+ case RUNS_AFTER:
+ _lifeMgr.addShutdownConstraint(lhs, LifecycleManager.Constraint.RUNS_AFTER, rhs);
+ break;
}
- Shutdowner before = (constraint == Constraint.RUNS_BEFORE) ? lhs : rhs;
- Shutdowner after = (constraint == Constraint.RUNS_BEFORE) ? rhs : lhs;
- _downers.addDependency(after, before);
}
/**
@@ -91,11 +77,7 @@ public class ShutdownManager
*/
public void queueShutdown ()
{
- _dobjq.postRunnable(new Runnable() {
- public void run () {
- shutdown();
- }
- });
+ _lifeMgr.queueShutdown();
}
/**
@@ -103,41 +85,8 @@ public class ShutdownManager
*/
public boolean isShuttingDown ()
{
- return _downers == null;
+ return _lifeMgr.isShuttingDown();
}
- /**
- * Shuts down all shutdowners immediately on the caller's thread.
- */
- public void shutdown ()
- {
- if (_downers == null) {
- log.warning("Refusing repeat shutdown request.");
- return;
- }
-
- ObserverList downers = ObserverList.newSafeInOrder();
-
- while (!_downers.isEmpty()) {
- downers.add(_downers.removeAvailableElement());
- }
-
- _downers = null;
-
- // shut down all shutdown participants
- downers.apply(new ObserverList.ObserverOp() {
- public boolean apply (Shutdowner downer) {
- log.debug("Calling shutdown registrant", "downer", downer);
- downer.shutdown();
- return true;
- }
- });
- }
-
- /** All of the registered shutdowners along with related constraints. */
- protected DependencyGraph _downers = new DependencyGraph();
-
- /** The queue we'll use to get onto the dobjmgr thread before shutting down. */
- protected RunQueue _dobjq;
-
+ @Inject protected LifecycleManager _lifeMgr;
}
diff --git a/src/java/com/threerings/presents/server/net/ConnectionManager.java b/src/java/com/threerings/presents/server/net/ConnectionManager.java
index 5119da538..15b681c05 100644
--- a/src/java/com/threerings/presents/server/net/ConnectionManager.java
+++ b/src/java/com/threerings/presents/server/net/ConnectionManager.java
@@ -67,9 +67,9 @@ import com.threerings.presents.server.Authenticator;
import com.threerings.presents.server.ChainedAuthenticator;
import com.threerings.presents.server.ClientManager;
import com.threerings.presents.server.DummyAuthenticator;
+import com.threerings.presents.server.LifecycleManager;
import com.threerings.presents.server.PresentsDObjectMgr;
import com.threerings.presents.server.ReportManager;
-import com.threerings.presents.server.ShutdownManager;
import com.threerings.presents.util.DatagramSequencer;
import static com.threerings.presents.Log.log;
@@ -82,15 +82,15 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class ConnectionManager extends LoopingThread
- implements ShutdownManager.Shutdowner, ReportManager.Reporter
+ implements LifecycleManager.ShutdownComponent, ReportManager.Reporter
{
/**
* Creates a connection manager instance. Don't call this, Guice will do it for you.
*/
- @Inject public ConnectionManager (ShutdownManager shutmgr, ReportManager repmgr)
+ @Inject public ConnectionManager (LifecycleManager lifemgr, ReportManager repmgr)
{
super("ConnectionManager");
- shutmgr.registerShutdowner(this);
+ lifemgr.addComponent(this);
repmgr.registerReporter(this);
}
@@ -391,7 +391,7 @@ public class ConnectionManager extends LoopingThread
// if we failed to listen on at least one port, give up the ghost
if (successes == 0) {
log.warning("ConnectionManager failed to bind to any ports. Shutting down.");
- _shutmgr.queueShutdown();
+ _lifemgr.queueShutdown();
return;
}
@@ -1232,7 +1232,7 @@ public class ConnectionManager extends LoopingThread
@Inject @AuthInvoker protected Invoker _authInvoker;
@Inject protected PresentsDObjectMgr _omgr;
@Inject protected ClientManager _clmgr;
- @Inject protected ShutdownManager _shutmgr;
+ @Inject protected LifecycleManager _lifemgr;
/** How long we wait for network events before checking our running flag to see if we should
* still be running. We don't want to loop too tightly, but we need to make sure we don't sit
diff --git a/src/java/com/threerings/util/DependencyGraph.java b/src/java/com/threerings/util/DependencyGraph.java
index cbf272938..be075731b 100644
--- a/src/java/com/threerings/util/DependencyGraph.java
+++ b/src/java/com/threerings/util/DependencyGraph.java
@@ -1,3 +1,6 @@
+//
+// $Id$
+
package com.threerings.util;
import java.util.ArrayList;
@@ -6,12 +9,15 @@ 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
+ * 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 the type of object maintained in the graph.
*/
public class DependencyGraph
{
@@ -49,7 +55,7 @@ public class DependencyGraph
/**
* Removes and returns an element which is available, meaning not dependent upon any other
- * still in the graph.
+ * still in the graph.
*/
public T removeAvailableElement ()
{
@@ -126,6 +132,19 @@ public class DependencyGraph
return false;
}
+ /**
+ * Flattens this graph into an observer list in dependencys order. Empties the graph in the
+ * process.
+ */
+ public ObserverList toObserverList ()
+ {
+ ObserverList list = ObserverList.newSafeInOrder();
+ while (!isEmpty()) {
+ list.add(removeAvailableElement());
+ }
+ return list;
+ }
+
/** All the nodes included in the graph. */
protected HashMap> _nodes = Maps.newHashMap();