diff --git a/src/java/com/threerings/presents/server/AbstractSignalHandler.java b/src/java/com/threerings/presents/server/AbstractSignalHandler.java
new file mode 100644
index 000000000..90b1f0b48
--- /dev/null
+++ b/src/java/com/threerings/presents/server/AbstractSignalHandler.java
@@ -0,0 +1,63 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2007 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 static com.threerings.presents.Log.log;
+
+/**
+ * A base class that is used to wire up signal handling in one of a couple of possible ways.
+ */
+public abstract class AbstractSignalHandler
+{
+ public boolean init (PresentsServer server)
+ {
+ _server = server;
+ return registerHandlers();
+ }
+
+ /**
+ * Signal handler implementations should wire themselves up in the call to this method.
+ *
+ * @return true if the handlers were successfully wired up, false if they were not able to be
+ * wired up.
+ */
+ protected abstract boolean registerHandlers ();
+
+ /**
+ * Implementations should call this method when a SIGINT is received.
+ */
+ protected void intReceived ()
+ {
+ log.info("Shutdown initiated by INT signal.");
+ _server.queueShutdown();
+ }
+
+ /**
+ * Implementations should call this method when a SIGHUP is received.
+ */
+ protected void hupReceived ()
+ {
+ log.info(_server.generateReport());
+ }
+
+ protected PresentsServer _server;
+}
diff --git a/src/java/com/threerings/presents/server/NativeSignalHandler.java b/src/java/com/threerings/presents/server/NativeSignalHandler.java
new file mode 100644
index 000000000..173d84181
--- /dev/null
+++ b/src/java/com/threerings/presents/server/NativeSignalHandler.java
@@ -0,0 +1,60 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2007 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.threerings.util.signal.SignalManager;
+
+import static com.threerings.presents.Log.log;
+
+/**
+ * Handles signals using Narya's native libsignal.
+ */
+public class NativeSignalHandler extends AbstractSignalHandler
+ implements SignalManager.SignalHandler
+{
+ // from interface SignalManager.SignalHandler
+ public boolean signalReceived (int signo)
+ {
+ switch (signo) {
+ case SignalManager.SIGINT:
+ intReceived();
+ break;
+ case SignalManager.SIGHUP:
+ hupReceived();
+ break;
+ default:
+ log.warning("Received unknown signal [signo=" + signo + "].");
+ break;
+ }
+ return true;
+ }
+
+ protected boolean registerHandlers ()
+ {
+ if (!SignalManager.servicesAvailable()) {
+ return false;
+ }
+ SignalManager.registerSignalHandler(SignalManager.SIGINT, this);
+ SignalManager.registerSignalHandler(SignalManager.SIGHUP, this);
+ return true;
+ }
+}
diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java
index 072f7f4ff..ec241c271 100644
--- a/src/java/com/threerings/presents/server/PresentsServer.java
+++ b/src/java/com/threerings/presents/server/PresentsServer.java
@@ -28,49 +28,37 @@ import com.samskivert.util.ObserverList;
import com.samskivert.util.StringUtil;
import com.samskivert.util.SystemInfo;
-import com.threerings.util.signal.SignalManager;
-
import com.threerings.presents.Log;
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.AccessController;
import com.threerings.presents.server.net.ConnectionManager;
/**
- * The presents server provides a central point of access to the various
- * facilities that make up the presents framework. To facilitate extension
- * and customization, a single instance of the presents server should be
- * created and initialized in a process. To facilitate easy access to the
- * services provided by the presents server, static references to the
- * various managers are made available in the PresentsServer
- * class. These will be configured when the singleton instance is
- * initialized.
+ * The presents server provides a central point of access to the various facilities that make up
+ * the presents framework. To facilitate extension and customization, a single instance of the
+ * presents server should be created and initialized in a process. To facilitate easy access to the
+ * services provided by the presents server, static references to the various managers are made
+ * available in the PresentsServer class. These will be configured when the singleton
+ * instance is initialized.
*/
public class PresentsServer
- implements SignalManager.SignalHandler
{
- /** Used to generate "state of the server" reports. See {@link
- * #registerReporter}. */
+ /** Used to generate "state of the server" reports. See {@link #registerReporter}. */
public static interface Reporter
{
/**
- * Requests that this reporter append its report to the supplied
- * string buffer.
+ * Requests that this reporter append its report to the supplied string buffer.
*
- * @param buffer the string buffer to which the report text should
- * be appended.
- * @param now the time at which the report generation began, in
- * epoch millis.
- * @param sinceLast number of milliseconds since the last time we
- * generated a report.
- * @param reset if true, all accumulating stats should be reset, if
- * false they should be allowed to continue to accumulate.
+ * @param buffer the string buffer to which the report text should be appended.
+ * @param now the time at which the report generation began, in epoch millis.
+ * @param sinceLast number of milliseconds since the last time we generated a report.
+ * @param reset if true, all accumulating stats should be reset, if false they should be
+ * allowed to continue to accumulate.
*/
- public void appendReport (
- StringBuilder buffer, long now, long sinceLast, boolean reset);
+ public void appendReport (StringBuilder buffer, long now, long sinceLast, boolean reset);
}
- /** Implementers of this interface will be notified when the server is
- * shutting down. */
+ /** Implementers of this interface will be notified when the server is shutting down. */
public static interface Shutdowner
{
/**
@@ -91,9 +79,9 @@ public class PresentsServer
/** The invocation manager. */
public static InvocationManager invmgr;
- /** This is used to invoke background tasks that should not be allowed
- * to tie up the distributed object manager thread. This is generally
- * used to talk to databases and other (relatively) slow entities. */
+ /** This is used to invoke background tasks that should not be allowed to tie up the
+ * distributed object manager thread. This is generally used to talk to databases and other
+ * (relatively) slow entities. */
public static PresentsInvoker invoker;
/**
@@ -104,17 +92,22 @@ public class PresentsServer
{
// output general system information
SystemInfo si = new SystemInfo();
- Log.info("Starting up server [os=" + si.osToString() +
- ", jvm=" + si.jvmToString() +
+ Log.info("Starting up server [os=" + si.osToString() + ", jvm=" + si.jvmToString() +
", mem=" + si.memoryToString() + "].");
// register SIGINT (ctrl-c) and a SIGHUP handlers
- SignalManager.registerSignalHandler(SignalManager.SIGINT, this);
- SignalManager.registerSignalHandler(SignalManager.SIGHUP, this);
+ boolean registered = false;
+ try {
+ registered = new SunSignalHandler().init(this);
+ } catch (Throwable t) {
+ t.printStackTrace(System.err);
+ }
+ if (!registered) {
+ new NativeSignalHandler().init(this);
+ }
// create our list of shutdowners
- _downers = new ObserverList(
- ObserverList.SAFE_IN_ORDER_NOTIFY);
+ _downers = new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
// create our distributed object manager
omgr = createDObjectManager();
@@ -181,8 +174,7 @@ public class PresentsServer
}
/**
- * Returns the port on which the connection manager will listen for
- * client connections.
+ * Returns the port on which the connection manager will listen for client connections.
*/
protected int[] getListenPorts ()
{
@@ -190,13 +182,12 @@ public class PresentsServer
}
/**
- * Starts up all of the server services and enters the main server
- * event loop.
+ * Starts up all of the server services and enters the main server event loop.
*/
public void run ()
{
- // post a unit that will start up the connection manager when
- // everything else in the dobjmgr queue is processed
+ // post a unit that will start up the connection manager when everything else in the
+ // dobjmgr queue is processed
omgr.postRunnable(new Runnable() {
public void run () {
// start up the connection manager
@@ -207,33 +198,9 @@ public class PresentsServer
omgr.run();
}
- // documentation inherited from interface
- public boolean signalReceived (int signo)
- {
- switch (signo) {
- case SignalManager.SIGINT:
- // this is called when we receive a ctrl-c
- Log.info("Shutdown initiated by received signal (" + signo + ")");
- queueShutdown();
- break;
-
- case SignalManager.SIGHUP:
- // generate a system status report
- Log.info(generateReport());
- break;
-
- default:
- Log.warning("Received unknown signal [signo=" + signo + "].");
- break;
- }
-
- return true;
- }
-
/**
- * A report is generated by the presents server periodically in which
- * server entities can participate by registering a {@link Reporter}
- * with this method.
+ * A report is generated by the presents server periodically in which server entities can
+ * participate by registering a {@link Reporter} with this method.
*/
public static void registerReporter (Reporter reporter)
{
@@ -241,8 +208,7 @@ public class PresentsServer
}
/**
- * Generates a report for all system services registered as a {@link
- * Reporter}.
+ * Generates a report for all system services registered as a {@link Reporter}.
*/
public static String generateReport ()
{
@@ -306,9 +272,8 @@ public class PresentsServer
}
/**
- * Logs the state of the server report via the default logging mechanism.
- * Derived classes may wish to log the state of the server report via a
- * different means.
+ * Logs the state of the server report via the default logging mechanism. Derived classes may
+ * wish to log the state of the server report via a different means.
*/
protected void logReport (String report)
{
@@ -316,9 +281,8 @@ public class PresentsServer
}
/**
- * Requests that the server shut down. All registered shutdown
- * participants will be shut down, following which the server process
- * will be terminated.
+ * Requests that the server shut down. All registered shutdown participants will be shut down,
+ * following which the server process will be terminated.
*/
public void shutdown ()
{
@@ -329,8 +293,8 @@ public class PresentsServer
}
_downers = null;
- // shut down the connection manager (this will cease all network
- // activity but not actually close the connections)
+ // shut down the connection manager (this will cease all network activity but not actually
+ // close the connections)
if (conmgr.isRunning()) {
conmgr.shutdown();
}
@@ -343,14 +307,14 @@ public class PresentsServer
}
});
- // finally shut down the invoker and distributed object manager
- // (The invoker does both for us.)
+ // finally shut down the invoker and distributed object manager (The invoker does both for
+ // us.)
invoker.shutdown();
}
/**
- * Queues up a request to shutdown on the dobjmgr thread. This method
- * may be safely called from any thread.
+ * Queues up a request to shutdown on the dobjmgr thread. This method may be safely called from
+ * any thread.
*/
public void queueShutdown ()
{
@@ -362,8 +326,7 @@ public class PresentsServer
}
/**
- * Registers an entity that will be notified when the server is
- * shutting down.
+ * Registers an entity that will be notified when the server is shutting down.
*/
public static void registerShutdowner (Shutdowner downer)
{
@@ -387,8 +350,7 @@ public class PresentsServer
// initialize the server
server.init();
- // check to see if we should load and invoke a test module
- // before running the server
+ // check to see if we should load and invoke a test module before running the server
String testmod = System.getProperty("test_module");
if (testmod != null) {
try {
@@ -403,8 +365,8 @@ public class PresentsServer
}
}
- // start the server to running (this method call won't return
- // until the server is shut down)
+ // start the server to running (this method call won't return until the server is shut
+ // down)
server.run();
} catch (Exception e) {
diff --git a/src/java/com/threerings/presents/server/SunSignalHandler.java b/src/java/com/threerings/presents/server/SunSignalHandler.java
new file mode 100644
index 000000000..d99e17941
--- /dev/null
+++ b/src/java/com/threerings/presents/server/SunSignalHandler.java
@@ -0,0 +1,56 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2007 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 sun.misc.Signal;
+import sun.misc.SignalHandler;
+
+import static com.threerings.presents.Log.log;
+
+/**
+ * Handles signals using Sun's undocumented Signal class.
+ */
+public class SunSignalHandler extends AbstractSignalHandler
+ implements SignalHandler
+{
+ // from interface SignalHandler
+ public void handle (Signal sig)
+ {
+ SignalHandler chain = null;
+ if (sig.getName().equals("INT")) {
+ intReceived();
+ } else if (sig.getName().equals("HUP")) {
+ hupReceived();
+ } else {
+ log.warning("Received unknown signal '" + sig.getName() + "'.");
+ }
+ }
+
+ protected boolean registerHandlers ()
+ {
+ // we don't track and call the chained handlers for INT and HUP because those exit the JVM
+ // which we do not want to do
+ Signal.handle(new Signal("INT"), this);
+ Signal.handle(new Signal("HUP"), this);
+ return true;
+ }
+}
diff --git a/src/java/com/threerings/util/signal/SignalManager.java b/src/java/com/threerings/util/signal/SignalManager.java
index e78532cb8..be54a2d0c 100644
--- a/src/java/com/threerings/util/signal/SignalManager.java
+++ b/src/java/com/threerings/util/signal/SignalManager.java
@@ -27,10 +27,9 @@ import com.samskivert.util.ObserverList;
import static com.threerings.NaryaLog.log;
/**
- * Uses native code to catch Unix signals and invoke callbacks on a
- * separate signal handler thread. If the native library cannot be loaded,
- * signal handlers will be allowed to be registered but will never be
- * called.
+ * Uses native code to catch Unix signals and invoke callbacks on a separate signal handler thread.
+ * If the native library cannot be loaded, signal handlers will be allowed to be registered but
+ * will never be called.
*/
public class SignalManager
{
@@ -139,17 +138,17 @@ public class SignalManager
/**
* Called when the specified signal is received.
*
- * @return true if the signal handler should remain registered,
- * false if it should be removed.
+ * @return true if the signal handler should remain registered, false if it should be
+ * removed.
*/
public boolean signalReceived (int signal);
}
/**
- * Returns true if signal dispatching services are available, false if
- * we could not load our native library.
+ * Returns true if signal dispatching services are available, false if we could not load our
+ * native library.
*/
- public boolean servicesAvailable ()
+ public static boolean servicesAvailable ()
{
return _haveLibrary;
}
@@ -157,8 +156,7 @@ public class SignalManager
/**
* Registers a signal handler for the specified signal.
*/
- public synchronized static void registerSignalHandler (
- int signal, SignalHandler handler)
+ public synchronized static void registerSignalHandler (int signal, SignalHandler handler)
{
ObserverList list = _handlers.get(signal);
if (list == null) {
@@ -186,13 +184,12 @@ public class SignalManager
/**
* Removes the registration for the specified signal handler.
*/
- public synchronized static void removeSignalHandler (
- int signal, SignalHandler handler)
+ public synchronized static void removeSignalHandler (int signal, SignalHandler handler)
{
ObserverList list = _handlers.get(signal);
if (list == null || !list.contains(handler)) {
- log.warning("Requested to remove non-registered handler " +
- "[signal=" + signal + ", handler=" + handler + "].");
+ log.warning("Requested to remove non-registered handler [signal=" + signal +
+ ", handler=" + handler + "].");
return;
}
list.remove(handler);
@@ -204,9 +201,8 @@ public class SignalManager
*/
protected synchronized static void signalReceived (final int signal)
{
- // this is hack, but we seem to get a call to our signal handler
- // for each thread if the user presses ctrl-c in the terminal, so
- // we "collapse" those into one call back
+ // this is hack, but we seem to get a call to our signal handler for each thread if the
+ // user presses ctrl-c in the terminal, so we "collapse" those into one call back
long now = System.currentTimeMillis();
if (signal == SIGINT) {
if (now - _lastINTed < 10) {
@@ -251,8 +247,7 @@ public class SignalManager
protected static native void deactivateHandler (int signal);
/**
- * Consigns a Java thread to the task of dispatching signal handler
- * callbacks.
+ * Consigns a Java thread to the task of dispatching signal handler callbacks.
*/
protected static native void dispatchSignals ();
@@ -266,8 +261,8 @@ public class SignalManager
/** Set to true if we successfully load our native library. */
protected static boolean _haveLibrary;
- /** Used to collapse bogus multiple delivery of SIGINT when the user
- * pressed ctrl-c in the console. */
+ /** Used to collapse bogus multiple delivery of SIGINT when the user pressed ctrl-c in the
+ * console. */
protected static long _lastINTed = 0L;
static {