Let's make the Lifecycle a bit more abstract and decouple it from all the

server business. I'd move the whole kit and kaboodle to samskivert but that
would mean moving the DependencyGraph as well...


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5804 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2009-05-27 19:09:47 +00:00
parent d855eb184a
commit e87c86dc2c
12 changed files with 81 additions and 87 deletions
@@ -52,7 +52,7 @@ public abstract class AbstractSignalHandler
protected void termReceived ()
{
log.info("Shutdown initiated by TERM signal.");
_lifemgr.queueShutdown();
_server.queueShutdown();
}
/**
@@ -61,7 +61,7 @@ public abstract class AbstractSignalHandler
protected void intReceived ()
{
log.info("Shutdown initiated by INT signal.");
_lifemgr.queueShutdown();
_server.queueShutdown();
}
/**
@@ -72,6 +72,6 @@ public abstract class AbstractSignalHandler
log.info(_repmgr.generateReport(ReportManager.DEFAULT_TYPE));
}
@Inject protected LifecycleManager _lifemgr;
@Inject protected PresentsServer _server;
@Inject protected ReportManager _repmgr;
}
@@ -39,6 +39,7 @@ import com.samskivert.util.Interval;
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;
@@ -62,7 +63,7 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class ClientManager
implements ClientResolutionListener, ReportManager.Reporter, LifecycleManager.Component
implements ClientResolutionListener, ReportManager.Reporter, Lifecycle.Component
{
/**
* Used by {@link ClientManager#applyToClient}.
@@ -100,10 +101,10 @@ public class ClientManager
/**
* Constructs a client manager that will interact with the supplied connection manager.
*/
@Inject public ClientManager (ReportManager repmgr, LifecycleManager lifemgr)
@Inject public ClientManager (ReportManager repmgr, Lifecycle cycle)
{
repmgr.registerReporter(this);
lifemgr.addComponent(this);
cycle.addComponent(this);
}
/**
@@ -323,7 +324,7 @@ public class ClientManager
_omgr.destroyObject(clobj.getOid());
}
// from interface LifecycleManager.Component
// from interface Lifecycle.Component
public void init ()
{
// start up an interval that will check for and flush expired clients
@@ -335,7 +336,7 @@ public class ClientManager
_flushClients.schedule(CLIENT_FLUSH_INTERVAL, true);
}
// from interface LifecycleManager.Component
// from interface Lifecycle.Component
public void shutdown ()
{
log.info("Client manager shutting down", "ccount", _usermap.size());
@@ -1,196 +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.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 ();
}
/** 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<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;
}
});
}
/** 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<InitComponent> _initers = new DependencyGraph<InitComponent>();
/** A dependency graph of our components arranged by shutdown dependencies. */
protected DependencyGraph<ShutdownComponent> _downers =
new DependencyGraph<ShutdownComponent>();
}
@@ -25,6 +25,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.samskivert.util.Invoker;
import com.threerings.util.Lifecycle;
import static com.threerings.presents.Log.log;
@@ -33,17 +34,16 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class PresentsInvoker extends ReportingInvoker
implements LifecycleManager.ShutdownComponent
implements Lifecycle.ShutdownComponent
{
@Inject public PresentsInvoker (PresentsDObjectMgr omgr, LifecycleManager lifemgr,
ReportManager repmgr)
@Inject public PresentsInvoker (PresentsDObjectMgr omgr, Lifecycle cycle, ReportManager repmgr)
{
super("presents.Invoker", omgr, repmgr);
cycle.addComponent(this);
_omgr = omgr;
lifemgr.addComponent(this);
}
@Override // from Invoker, LifecycleManager.ShutdownComponent
@Override // from Invoker, Lifecycle.ShutdownComponent
public void shutdown ()
{
// this will do a sophisticated shutdown of both ourself and the dobjmgr; note: we
@@ -30,6 +30,7 @@ import com.google.inject.Singleton;
import com.samskivert.util.Invoker;
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;
@@ -161,11 +162,11 @@ public class PresentsServer
// 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);
_lifecycle.addShutdownConstraint(
_clmgr, Lifecycle.Constraint.RUNS_BEFORE, (PresentsInvoker)_invoker);
// initialize all of our registered components
_lifemgr.init();
_lifecycle.init();
}
/**
@@ -185,6 +186,19 @@ public class PresentsServer
_omgr.run();
}
/**
* Queues up a request to shutdown on the event thread. This method may be safely called from
* any thread.
*/
public void queueShutdown ()
{
_omgr.postRunnable(new PresentsDObjectMgr.LongRunnable() {
public void run () {
_lifecycle.shutdown();
}
});
}
/**
* Defines the default object access policy for all {@link DObject} instances. The default
* default policy is to allow all subscribers but reject all modifications by the client.
@@ -242,7 +256,7 @@ public class PresentsServer
@Inject protected InvocationManager _invmgr;
/** Handles orderly initialization and shutdown of our managers, etc. */
@Inject protected LifecycleManager _lifemgr;
@Inject protected Lifecycle _lifecycle;
/** Handles generation of state of the server reports. */
@Inject protected ReportManager _repmgr;
@@ -188,9 +188,9 @@ public abstract class RebootManager
/**
* Provides us with our dependencies.
*/
protected RebootManager (LifecycleManager lmgr, RootDObjectManager omgr)
protected RebootManager (PresentsServer server, RootDObjectManager omgr)
{
_lmgr = lmgr;
_server = server;
_omgr = omgr;
}
@@ -257,12 +257,7 @@ 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 () {
// ...but we then post a LongRunnable...
_omgr.postRunnable(new PresentsDObjectMgr.LongRunnable() {
public void run () {
_lmgr.shutdown();
}
});
_server.queueShutdown(); // this posts a LongRunnable
}
}.schedule(1000);
return;
@@ -321,8 +316,8 @@ public abstract class RebootManager
});
}
/** Our lifecycle manager, which is how we will reboot. */
protected LifecycleManager _lmgr;
/** The server that we're going to reboot. */
protected PresentsServer _server;
/** Our distributed object manager. */
protected RootDObjectManager _omgr;
@@ -24,16 +24,18 @@ package com.threerings.presents.server;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.threerings.util.Lifecycle;
/**
* Handles the orderly shutdown of all server services.
*
* @Deprecated use LifecycleManager
* @Deprecated use Lifecycle
*/
@Singleton
public class ShutdownManager
{
/** Implementers of this interface will be notified when the server is shutting down. */
public static interface Shutdowner extends LifecycleManager.ShutdownComponent
public static interface Shutdowner extends Lifecycle.ShutdownComponent
{
}
@@ -45,7 +47,7 @@ public class ShutdownManager
*/
public void registerShutdowner (Shutdowner downer)
{
_lifeMgr.addComponent(downer);
_cycle.addComponent(downer);
}
/**
@@ -53,7 +55,7 @@ public class ShutdownManager
*/
public void unregisterShutdowner (Shutdowner downer)
{
_lifeMgr.removeComponent(downer);
_cycle.removeComponent(downer);
}
/**
@@ -63,10 +65,10 @@ public class ShutdownManager
{
switch (constraint) {
case RUNS_BEFORE:
_lifeMgr.addShutdownConstraint(lhs, LifecycleManager.Constraint.RUNS_BEFORE, rhs);
_cycle.addShutdownConstraint(lhs, Lifecycle.Constraint.RUNS_BEFORE, rhs);
break;
case RUNS_AFTER:
_lifeMgr.addShutdownConstraint(lhs, LifecycleManager.Constraint.RUNS_AFTER, rhs);
_cycle.addShutdownConstraint(lhs, Lifecycle.Constraint.RUNS_AFTER, rhs);
break;
}
}
@@ -77,7 +79,7 @@ public class ShutdownManager
*/
public void queueShutdown ()
{
_lifeMgr.queueShutdown();
_server.queueShutdown();
}
/**
@@ -85,8 +87,9 @@ public class ShutdownManager
*/
public boolean isShuttingDown ()
{
return _lifeMgr.isShuttingDown();
return _cycle.isShuttingDown();
}
@Inject protected LifecycleManager _lifeMgr;
@Inject protected Lifecycle _cycle;
@Inject protected PresentsServer _server;
}
@@ -56,6 +56,7 @@ 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;
@@ -67,8 +68,8 @@ 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.PresentsServer;
import com.threerings.presents.server.ReportManager;
import com.threerings.presents.util.DatagramSequencer;
@@ -82,15 +83,15 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class ConnectionManager extends LoopingThread
implements LifecycleManager.ShutdownComponent, ReportManager.Reporter
implements Lifecycle.ShutdownComponent, ReportManager.Reporter
{
/**
* Creates a connection manager instance. Don't call this, Guice will do it for you.
*/
@Inject public ConnectionManager (LifecycleManager lifemgr, ReportManager repmgr)
@Inject public ConnectionManager (Lifecycle cycle, ReportManager repmgr)
{
super("ConnectionManager");
lifemgr.addComponent(this);
cycle.addComponent(this);
repmgr.registerReporter(this);
}
@@ -391,7 +392,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.");
_lifemgr.queueShutdown();
_server.queueShutdown();
return;
}
@@ -1230,9 +1231,9 @@ public class ConnectionManager extends LoopingThread
// some dependencies
@Inject @AuthInvoker protected Invoker _authInvoker;
@Inject protected PresentsDObjectMgr _omgr;
@Inject protected ClientManager _clmgr;
@Inject protected LifecycleManager _lifemgr;
@Inject protected PresentsDObjectMgr _omgr;
@Inject protected PresentsServer _server;
/** 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