Holy fruity beverage Batman! I'm taking the plunge and switching to Guice for
dependency resolution and management. Things are currently in a state of transition, but the important patterns have been established and I'll aim to sweep through all of Narya, Nenya and Vilya in the near future and do everything properly. Going through the other million-odd lines of code we have scattered across our various projects that use these libraries is not something I plan to do, so we'll be maintaining backward compatibility with the old static member method, though I hope to strive toward eradication of that usage entirely in MSOY while we still have a fighting chance. Given that some of our projects will continue to use the static member method in perpetuity, I'm not going to @Deprecate those fields because that would fill their logs with so much spam that they'd have to turn off deprecation warnings which would make life worse for them. So instead we'll settle for the big scary comment warning people away from the old bits. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5153 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -3,11 +3,13 @@
|
||||
<project name="library-dependencies">
|
||||
<fileset dir="${libs.dir}" id="narya.libs">
|
||||
<include name="ant.jar"/>
|
||||
<include name="atunit.jar"/>
|
||||
<include name="asc.jar"/>
|
||||
<include name="flexTasks.jar"/>
|
||||
<include name="commons-collections.jar"/>
|
||||
<include name="commons-io.jar"/>
|
||||
<include name="commons-lang.jar"/>
|
||||
<include name="guice.jar"/>
|
||||
<include name="google-collect.jar"/>
|
||||
<include name="javassist.jar"/>
|
||||
<include name="junit4.jar"/>
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
|
||||
package com.threerings.crowd.peer.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.PresentsClient;
|
||||
import com.threerings.presents.server.ShutdownManager;
|
||||
|
||||
import com.threerings.presents.peer.data.ClientInfo;
|
||||
import com.threerings.presents.peer.data.NodeObject;
|
||||
@@ -49,6 +52,14 @@ import com.threerings.crowd.peer.data.CrowdPeerMarshaller;
|
||||
public class CrowdPeerManager extends PeerManager
|
||||
implements CrowdPeerProvider, ChatProvider.ChatForwarder
|
||||
{
|
||||
/**
|
||||
* Creates an uninitialized peer manager.
|
||||
*/
|
||||
@Inject public CrowdPeerManager (ShutdownManager shutmgr)
|
||||
{
|
||||
super(shutmgr);
|
||||
}
|
||||
|
||||
// from interface CrowdPeerProvider
|
||||
public void deliverTell (ClientObject caller, UserMessage message,
|
||||
Name target, ChatService.TellListener listener)
|
||||
|
||||
@@ -23,6 +23,9 @@ package com.threerings.crowd.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
@@ -44,6 +47,15 @@ import static com.threerings.crowd.Log.log;
|
||||
*/
|
||||
public class CrowdServer extends PresentsServer
|
||||
{
|
||||
/** Configures dependencies needed by the Crowd services. */
|
||||
public static class Module extends PresentsServer.Module
|
||||
{
|
||||
@Override protected void configure () {
|
||||
super.configure();
|
||||
// nada
|
||||
}
|
||||
}
|
||||
|
||||
/** The place registry. */
|
||||
public static PlaceRegistry plreg;
|
||||
|
||||
@@ -53,11 +65,10 @@ public class CrowdServer extends PresentsServer
|
||||
/**
|
||||
* Initializes all of the server services and prepares for operation.
|
||||
*/
|
||||
public void init ()
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
// do the presents server initialization
|
||||
super.init();
|
||||
super.init(injector);
|
||||
|
||||
// configure the client manager to use our bits
|
||||
clmgr.setClientFactory(new ClientFactory() {
|
||||
@@ -139,9 +150,10 @@ public class CrowdServer extends PresentsServer
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
CrowdServer server = new CrowdServer();
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
CrowdServer server = injector.getInstance(CrowdServer.class);
|
||||
try {
|
||||
server.init();
|
||||
server.init(injector);
|
||||
server.run();
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to initialize server.", e);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// $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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.google.inject.BindingAnnotation;
|
||||
|
||||
import com.samskivert.util.RunQueue;
|
||||
|
||||
/**
|
||||
* An annotation that identifies the distributed object event dispatcher's {@link RunQueue}. Code
|
||||
* that requires the ability to post runnables for execution on the event dispatcher thread can
|
||||
* inject this queue like so:
|
||||
*
|
||||
* <code>@Inject @EventQueue RunQueue _dobjq;</code>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.PARAMETER })
|
||||
@BindingAnnotation
|
||||
public @interface EventQueue
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// $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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.google.inject.BindingAnnotation;
|
||||
|
||||
import com.samskivert.util.Invoker;
|
||||
|
||||
/**
|
||||
* An annotation that identifies the main Presents {@link Invoker}. Code that requires the ability
|
||||
* to post units for execution on the main invoker thread can inject this queue like so:
|
||||
*
|
||||
* <code>@Inject @MainInvoker Invoker _invoker;</code>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.PARAMETER })
|
||||
@BindingAnnotation
|
||||
public @interface MainInvoker
|
||||
{
|
||||
}
|
||||
@@ -32,6 +32,10 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.jdbc.RepositoryUnit;
|
||||
import com.samskivert.jdbc.WriteOnlyUnit;
|
||||
import com.samskivert.jdbc.depot.PersistenceContext;
|
||||
@@ -49,7 +53,6 @@ import com.samskivert.util.Tuple;
|
||||
import com.threerings.io.Streamable;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.EntryAddedEvent;
|
||||
@@ -58,10 +61,15 @@ import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.SetListener;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.server.ClientManager;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
import com.threerings.presents.server.PresentsClient;
|
||||
import com.threerings.presents.server.PresentsServer;
|
||||
import com.threerings.presents.server.PresentsDObjectMgr;
|
||||
import com.threerings.presents.server.ShutdownManager;
|
||||
import com.threerings.presents.server.net.ConnectionManager;
|
||||
|
||||
import com.threerings.presents.peer.data.ClientInfo;
|
||||
import com.threerings.presents.peer.data.NodeObject;
|
||||
@@ -77,8 +85,9 @@ import static com.threerings.presents.Log.log;
|
||||
* client connection to the other servers and subscribes to the {@link NodeObject} of all peer
|
||||
* servers and uses those objects to communicate cross-node information.
|
||||
*/
|
||||
@Singleton
|
||||
public class PeerManager
|
||||
implements PeerProvider, ClientManager.ClientObserver, PresentsServer.Shutdowner
|
||||
implements PeerProvider, ClientManager.ClientObserver, ShutdownManager.Shutdowner
|
||||
{
|
||||
/**
|
||||
* Used by entities that wish to know when cached data has become stale due to a change on
|
||||
@@ -168,6 +177,14 @@ public class PeerManager
|
||||
protected abstract void execute ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an uninitialized peer manager.
|
||||
*/
|
||||
@Inject public PeerManager (ShutdownManager shutmgr)
|
||||
{
|
||||
shutmgr.registerShutdowner(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distributed object that represents this node to its peers.
|
||||
*/
|
||||
@@ -202,14 +219,11 @@ public class PeerManager
|
||||
_sharedSecret = sharedSecret;
|
||||
|
||||
// wire ourselves into the server
|
||||
PresentsServer.registerShutdowner(this);
|
||||
PresentsServer.conmgr.setAuthenticator(
|
||||
new PeerAuthenticator(this, PresentsServer.conmgr.getAuthenticator()));
|
||||
PresentsServer.clmgr.setClientFactory(
|
||||
new PeerClientFactory(this, PresentsServer.clmgr.getClientFactory()));
|
||||
_conmgr.setAuthenticator(new PeerAuthenticator(this, _conmgr.getAuthenticator()));
|
||||
_clmgr.setClientFactory(new PeerClientFactory(this, _clmgr.getClientFactory()));
|
||||
|
||||
// create our node object
|
||||
_nodeobj = PresentsServer.omgr.registerObject(createNodeObject());
|
||||
_nodeobj = _omgr.registerObject(createNodeObject());
|
||||
_nodeobj.setNodeName(nodeName);
|
||||
|
||||
// register ourselves with the node table
|
||||
@@ -223,10 +237,10 @@ public class PeerManager
|
||||
|
||||
// set the invocation service
|
||||
_nodeobj.setPeerService(
|
||||
(PeerMarshaller)PresentsServer.invmgr.registerDispatcher(new PeerDispatcher(this)));
|
||||
(PeerMarshaller)_invmgr.registerDispatcher(new PeerDispatcher(this)));
|
||||
|
||||
// register ourselves as a client observer
|
||||
PresentsServer.clmgr.addClientObserver(this);
|
||||
_clmgr.addClientObserver(this);
|
||||
|
||||
// and start our peer refresh interval
|
||||
_peerRefresher.schedule(5000L, 60*1000L);
|
||||
@@ -317,8 +331,8 @@ public class PeerManager
|
||||
public void invokeNodeAction (final NodeAction action)
|
||||
{
|
||||
// if we're not on the dobjmgr thread, get there
|
||||
if (!PresentsServer.omgr.isDispatchThread()) {
|
||||
PresentsServer.omgr.postRunnable(new Runnable() {
|
||||
if (!_omgr.isDispatchThread()) {
|
||||
_omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
invokeNodeAction(action);
|
||||
}
|
||||
@@ -384,7 +398,7 @@ public class PeerManager
|
||||
// make a note of this proxy mapping
|
||||
_proxies.put(key, new Tuple<Subscriber<?>,DObject>(this, object));
|
||||
// map the object into our local oid space
|
||||
PresentsServer.omgr.registerProxyObject(object, peer.getDObjectManager());
|
||||
_omgr.registerProxyObject(object, peer.getDObjectManager());
|
||||
// then tell the caller about the (now remapped) oid
|
||||
listener.requestCompleted(object.getOid());
|
||||
}
|
||||
@@ -408,7 +422,7 @@ public class PeerManager
|
||||
}
|
||||
|
||||
// clear out the local object manager's proxy mapping
|
||||
PresentsServer.omgr.clearProxyObject(remoteOid, bits.right);
|
||||
_omgr.clearProxyObject(remoteOid, bits.right);
|
||||
|
||||
final Client peer = getPeerClient(nodeName);
|
||||
if (peer == null) {
|
||||
@@ -681,19 +695,19 @@ public class PeerManager
|
||||
_nodeobj.setCacheData(new NodeObject.CacheData(cache, data));
|
||||
}
|
||||
|
||||
// from interface PresentsServer.Shutdowner
|
||||
// from interface ShutdownManager.Shutdowner
|
||||
public void shutdown ()
|
||||
{
|
||||
// clear out our invocation service
|
||||
if (_nodeobj != null) {
|
||||
PresentsServer.invmgr.clearDispatcher(_nodeobj.peerService);
|
||||
_invmgr.clearDispatcher(_nodeobj.peerService);
|
||||
}
|
||||
|
||||
// stop our peer refresher interval
|
||||
_peerRefresher.cancel();
|
||||
|
||||
// clear out our client observer registration
|
||||
PresentsServer.clmgr.removeClientObserver(this);
|
||||
_clmgr.removeClientObserver(this);
|
||||
|
||||
// clear our record from the node table
|
||||
_invoker.postUnit(new WriteOnlyUnit("deleteNode(" + _nodeName + ")") {
|
||||
@@ -1004,7 +1018,7 @@ public class PeerManager
|
||||
_remoids = (ArrayIntSet)_suboids.clone();
|
||||
|
||||
// schedule a timeout to act if something goes wrong
|
||||
(_timeout = new Interval(PresentsServer.omgr) {
|
||||
(_timeout = new Interval(_omgr) {
|
||||
public void expired () {
|
||||
log.warning("Lock handler timed out, acting anyway [lock=" + _lock +
|
||||
", acquire=" + _acquire + "].");
|
||||
@@ -1198,19 +1212,23 @@ public class PeerManager
|
||||
|
||||
/** Contains a mapping of proxied objects to subscriber instances. */
|
||||
protected HashMap<Tuple<String,Integer>,Tuple<Subscriber<?>,DObject>> _proxies =
|
||||
new HashMap<Tuple<String,Integer>,Tuple<Subscriber<?>,DObject>>();
|
||||
Maps.newHashMap();
|
||||
|
||||
/** Our stale cache observers. */
|
||||
protected HashMap<String, ObserverList<StaleCacheObserver>> _cacheobs =
|
||||
new HashMap<String, ObserverList<StaleCacheObserver>>();
|
||||
protected HashMap<String, ObserverList<StaleCacheObserver>> _cacheobs = Maps.newHashMap();
|
||||
|
||||
/** Listeners for dropped locks. */
|
||||
protected ObserverList<DroppedLockObserver> _dropobs =
|
||||
new ObserverList<DroppedLockObserver>(ObserverList.FAST_UNSAFE_NOTIFY);
|
||||
|
||||
/** Locks in the process of resolution. */
|
||||
protected HashMap<NodeObject.Lock, LockHandler> _locks =
|
||||
new HashMap<NodeObject.Lock, LockHandler>();
|
||||
protected HashMap<NodeObject.Lock, LockHandler> _locks = Maps.newHashMap();
|
||||
|
||||
// our service dependencies
|
||||
@Inject protected ConnectionManager _conmgr;
|
||||
@Inject protected ClientManager _clmgr;
|
||||
@Inject protected PresentsDObjectMgr _omgr;
|
||||
@Inject protected InvocationManager _invmgr;
|
||||
|
||||
/** We wait this long for peer ratification to complete before acquiring/releasing the lock. */
|
||||
protected static final long LOCK_TIMEOUT = 5000L;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
@@ -28,9 +30,11 @@ import static com.threerings.presents.Log.log;
|
||||
*/
|
||||
public abstract class AbstractSignalHandler
|
||||
{
|
||||
public boolean init (PresentsServer server)
|
||||
/**
|
||||
* Initializes this signal handler.
|
||||
*/
|
||||
public boolean init ()
|
||||
{
|
||||
_server = server;
|
||||
return registerHandlers();
|
||||
}
|
||||
|
||||
@@ -65,8 +69,9 @@ public abstract class AbstractSignalHandler
|
||||
*/
|
||||
protected void hupReceived ()
|
||||
{
|
||||
log.info(PresentsServer.generateReport());
|
||||
log.info(_repmgr.generateReport());
|
||||
}
|
||||
|
||||
protected PresentsServer _server;
|
||||
@Inject protected PresentsServer _server;
|
||||
@Inject protected ReportManager _repmgr;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,14 @@
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.ObserverList;
|
||||
@@ -49,9 +54,10 @@ import static com.threerings.presents.Log.log;
|
||||
* thread (to notify of connections showing up or going away) and from the dobjmgr thread (when
|
||||
* clients are given the boot for application-defined reasons).
|
||||
*/
|
||||
@Singleton
|
||||
public class ClientManager
|
||||
implements ConnectionObserver, ClientResolutionListener,
|
||||
PresentsServer.Reporter, PresentsServer.Shutdowner
|
||||
ReportManager.Reporter, ShutdownManager.Shutdowner
|
||||
{
|
||||
/**
|
||||
* Used by {@link #applyToClient}.
|
||||
@@ -89,25 +95,26 @@ public class ClientManager
|
||||
/**
|
||||
* Constructs a client manager that will interact with the supplied connection manager.
|
||||
*/
|
||||
public ClientManager (ConnectionManager conmgr)
|
||||
@Inject public ClientManager (ConnectionManager conmgr, ReportManager repmgr,
|
||||
ShutdownManager shutmgr)
|
||||
{
|
||||
// register ourselves as a connection observer
|
||||
conmgr.addConnectionObserver(this);
|
||||
|
||||
// start up an interval that will check for expired clients and flush them from the bowels
|
||||
// of the server
|
||||
new Interval(PresentsServer.omgr) {
|
||||
new Interval(_omgr) {
|
||||
public void expired () {
|
||||
flushClients();
|
||||
}
|
||||
}.schedule(CLIENT_FLUSH_INTERVAL, true);
|
||||
|
||||
// register as a "state of server" reporter and a shutdowner
|
||||
PresentsServer.registerReporter(this);
|
||||
PresentsServer.registerShutdowner(this);
|
||||
repmgr.registerReporter(this);
|
||||
shutmgr.registerShutdowner(this);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
// from interface ShutdownManager.Shutdowner
|
||||
public void shutdown ()
|
||||
{
|
||||
log.info("Client manager shutting down [ccount=" + _usermap.size() + "].");
|
||||
@@ -237,7 +244,7 @@ public class ClientManager
|
||||
// report that the client is resolved on the dobjmgr thread to provide equivalent
|
||||
// behavior to the case where we actually have to do the resolution
|
||||
clobj.reference();
|
||||
PresentsServer.omgr.postRunnable(new Runnable() {
|
||||
_omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
listener.clientResolved(username, clobj);
|
||||
}
|
||||
@@ -265,11 +272,11 @@ public class ClientManager
|
||||
// create and register our client object and give it back to the client resolver; we
|
||||
// need to do this on the dobjmgr thread since we're registering an object
|
||||
final ClientResolver fclr = clr;
|
||||
PresentsServer.omgr.postRunnable(new Runnable() {
|
||||
_omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
ClientObject clobj = fclr.createClientObject();
|
||||
clobj.setPermissionPolicy(fclr.createPermissionPolicy());
|
||||
fclr.objectAvailable(PresentsServer.omgr.registerObject(clobj));
|
||||
fclr.objectAvailable(_omgr.registerObject(clobj));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -303,7 +310,7 @@ public class ClientManager
|
||||
_objmap.remove(username);
|
||||
|
||||
// and destroy the object itself
|
||||
PresentsServer.omgr.destroyObject(clobj.getOid());
|
||||
_omgr.destroyObject(clobj.getOid());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,7 +418,7 @@ public class ClientManager
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface PresentsServer.Reporter
|
||||
// documentation inherited from interface ReportManager.Reporter
|
||||
public void appendReport (StringBuilder report, long now, long sinceLast, boolean reset)
|
||||
{
|
||||
report.append("* presents.ClientManager:\n");
|
||||
@@ -477,7 +484,7 @@ public class ClientManager
|
||||
*/
|
||||
protected void flushClients ()
|
||||
{
|
||||
ArrayList<PresentsClient> victims = new ArrayList<PresentsClient>();
|
||||
List<PresentsClient> victims = Lists.newArrayList();
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
// first build a list of our victims
|
||||
@@ -530,23 +537,25 @@ public class ClientManager
|
||||
}
|
||||
|
||||
/** A mapping from auth username to client instances. */
|
||||
protected HashMap<Name,PresentsClient> _usermap = new HashMap<Name,PresentsClient>();
|
||||
protected Map<Name,PresentsClient> _usermap = Maps.newHashMap();
|
||||
|
||||
/** A mapping from connections to client instances. */
|
||||
protected HashMap<Connection,PresentsClient> _conmap = new HashMap<Connection,PresentsClient>();
|
||||
protected Map<Connection,PresentsClient> _conmap = Maps.newHashMap();
|
||||
|
||||
/** A mapping from usernames to client object instances. */
|
||||
protected HashMap<Name,ClientObject> _objmap = new HashMap<Name,ClientObject>();
|
||||
protected Map<Name,ClientObject> _objmap = Maps.newHashMap();
|
||||
|
||||
/** A mapping of pending client resolvers. */
|
||||
protected HashMap<Name,ClientResolver> _penders = new HashMap<Name,ClientResolver>();
|
||||
protected Map<Name,ClientResolver> _penders = Maps.newHashMap();
|
||||
|
||||
/** The client class in use. */
|
||||
protected ClientFactory _factory = ClientFactory.DEFAULT;
|
||||
|
||||
/** Tracks registered {@link ClientObserver}s. */
|
||||
protected ObserverList<ClientObserver> _clobservers =
|
||||
new ObserverList<ClientObserver>(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
protected ObserverList<ClientObserver> _clobservers = ObserverList.newSafeInOrder();
|
||||
|
||||
// our injected dependencies
|
||||
@Inject protected PresentsDObjectMgr _omgr;
|
||||
|
||||
/** The frequency with which we check for expired clients. */
|
||||
protected static final long CLIENT_FLUSH_INTERVAL = 60 * 1000L;
|
||||
|
||||
@@ -21,9 +21,14 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
@@ -39,7 +44,6 @@ import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.EventListener;
|
||||
import com.threerings.presents.dobj.InvocationRequestEvent;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
|
||||
import com.threerings.presents.net.Transport;
|
||||
|
||||
@@ -62,6 +66,7 @@ import static com.threerings.presents.Log.log;
|
||||
* provides a mechanism by which responses and asynchronous notification invocations can be
|
||||
* delivered to the client.
|
||||
*/
|
||||
@Singleton
|
||||
public class InvocationManager
|
||||
implements EventListener
|
||||
{
|
||||
@@ -70,16 +75,16 @@ public class InvocationManager
|
||||
* operate its invocation services. Generally only one invocation manager should be operational
|
||||
* in a particular system.
|
||||
*/
|
||||
public InvocationManager (RootDObjectManager omgr)
|
||||
@Inject public InvocationManager (PresentsDObjectMgr omgr)
|
||||
{
|
||||
_omgr = omgr;
|
||||
|
||||
// create the object on which we'll listen for invocation requests
|
||||
DObject invobj = omgr.registerObject(new DObject());
|
||||
DObject invobj = _omgr.registerObject(new DObject());
|
||||
invobj.addListener(this);
|
||||
_invoid = invobj.getOid();
|
||||
|
||||
// Log.info("Created invocation service object [oid=" + _invoid + "].");
|
||||
// log.info("Created invocation service object [oid=" + _invoid + "].");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,7 +148,7 @@ public class InvocationManager
|
||||
|
||||
_recentRegServices.put(Integer.valueOf(invCode), marsh.getClass().getName());
|
||||
|
||||
// Log.info("Registered service [marsh=" + marsh + "].");
|
||||
// log.info("Registered service [marsh=" + marsh + "].");
|
||||
return marsh;
|
||||
}
|
||||
|
||||
@@ -199,7 +204,7 @@ public class InvocationManager
|
||||
// documentation inherited from interface
|
||||
public void eventReceived (DEvent event)
|
||||
{
|
||||
// Log.info("Event received " + event + ".");
|
||||
// log.info("Event received " + event + ".");
|
||||
|
||||
if (event instanceof InvocationRequestEvent) {
|
||||
InvocationRequestEvent ire = (InvocationRequestEvent)event;
|
||||
@@ -255,7 +260,7 @@ public class InvocationManager
|
||||
}
|
||||
}
|
||||
|
||||
// Log.debug("Dispatching invreq [caller=" + source.who() +
|
||||
// log.debug("Dispatching invreq [caller=" + source.who() +
|
||||
// ", disp=" + disp + ", methId=" + methodId +
|
||||
// ", args=" + StringUtil.toString(args) + "].");
|
||||
|
||||
@@ -301,29 +306,24 @@ public class InvocationManager
|
||||
return _invCode++;
|
||||
}
|
||||
|
||||
// debugging action...
|
||||
protected static final LRUHashMap<Integer,String> _recentRegServices =
|
||||
new LRUHashMap<Integer,String>(10000);
|
||||
|
||||
/** The distributed object manager with which we're working. */
|
||||
protected RootDObjectManager _omgr;
|
||||
|
||||
/** The object id of the object on which we receive invocation service requests. */
|
||||
protected int _invoid = -1;
|
||||
|
||||
/** Used to generate monotonically increasing provider ids. */
|
||||
protected int _invCode;
|
||||
|
||||
/** The distribted object manager we're working with. */
|
||||
protected PresentsDObjectMgr _omgr;
|
||||
|
||||
/** A table of invocation dispatchers each mapped by a unique code. */
|
||||
protected HashIntMap<InvocationDispatcher> _dispatchers =
|
||||
new HashIntMap<InvocationDispatcher>();
|
||||
protected IntMap<InvocationDispatcher> _dispatchers = IntMaps.newHashIntMap();
|
||||
|
||||
/** A mapping from bootstrap group to lists of services that are to be provided to clients at
|
||||
* boot time. */
|
||||
protected HashMap<String,StreamableArrayList<InvocationMarshaller>> _bootlists =
|
||||
new HashMap<String,StreamableArrayList<InvocationMarshaller>>();
|
||||
/** Maps bootstrap group to lists of services to be provided to clients at boot time. */
|
||||
protected Map<String,StreamableArrayList<InvocationMarshaller>> _bootlists = Maps.newHashMap();
|
||||
|
||||
/** The text that is appended to the procedure name when automatically generating a failure
|
||||
* response. */
|
||||
// debugging action...
|
||||
protected final Map<Integer,String> _recentRegServices = new LRUHashMap<Integer,String>(10000);
|
||||
|
||||
/** The text appended to the procedure name when generating a failure response. */
|
||||
protected static final String FAILED_SUFFIX = "Failed";
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.threerings.presents.server;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.threerings.presents.dobj.DEvent;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
@@ -35,6 +36,14 @@ import com.threerings.presents.dobj.Subscriber;
|
||||
*/
|
||||
public class LocalDObjectMgr extends PresentsDObjectMgr
|
||||
{
|
||||
/**
|
||||
* Creates the dobjmgr and prepares it for operation.
|
||||
*/
|
||||
@Inject public LocalDObjectMgr (ReportManager repmgr)
|
||||
{
|
||||
super(repmgr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DObjectManager} that posts directly to this local object manager, but first
|
||||
* sets the source oid of all events to properly identify them with the supplied client oid.
|
||||
|
||||
@@ -22,14 +22,17 @@
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.AuditLogger;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.Histogram;
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.Queue;
|
||||
@@ -51,11 +54,12 @@ import static com.threerings.presents.Log.log;
|
||||
* thus provides a method to be invoked by the application main thread which won't return until the
|
||||
* manager has been requested to shut down.
|
||||
*/
|
||||
@Singleton
|
||||
public class PresentsDObjectMgr
|
||||
implements RootDObjectManager, RunQueue, PresentsServer.Reporter
|
||||
implements RootDObjectManager, RunQueue, ReportManager.Reporter
|
||||
{
|
||||
/** Contains operational statistics that are tracked by the distributed object manager between
|
||||
* {@link PresentsServer#Reporter} intervals. The snapshot for the most recently completed
|
||||
* {@link ReportManager#Reporter} intervals. The snapshot for the most recently completed
|
||||
* period can be requested via {@link #getStats()}. . */
|
||||
public static class Stats
|
||||
{
|
||||
@@ -77,17 +81,21 @@ public class PresentsDObjectMgr
|
||||
/**
|
||||
* Creates the dobjmgr and prepares it for operation.
|
||||
*/
|
||||
public PresentsDObjectMgr ()
|
||||
@Inject public PresentsDObjectMgr (ReportManager repmgr)
|
||||
{
|
||||
// we create a dummy object to live as oid zero and we'll use that for some internal event
|
||||
// trickery
|
||||
// create a dummy object to live as oid zero and use that for some internal event trickery
|
||||
DObject dummy = new DObject();
|
||||
dummy.setOid(0);
|
||||
dummy.setManager(this);
|
||||
_objects.put(0, new DObject());
|
||||
|
||||
// register ourselves as a state of server reporter
|
||||
PresentsServer.registerReporter(this);
|
||||
// register ourselves as a state of server reporter and also tell the report manager that
|
||||
// we're providing the runqueue for it to do its business
|
||||
repmgr.registerReporter(this);
|
||||
repmgr.init(this);
|
||||
|
||||
// register our event helpers
|
||||
registerEventHelpers();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,8 +120,7 @@ public class PresentsDObjectMgr
|
||||
_defaultController = controller;
|
||||
|
||||
// switch all objects from the old default (null, usually) to the new default.
|
||||
for (Iterator itr = _objects.elements(); itr.hasNext(); ) {
|
||||
DObject obj = (DObject) itr.next();
|
||||
for (DObject obj : _objects.values()) {
|
||||
if (oldDefault == obj.getAccessController()) {
|
||||
obj.setAccessController(controller);
|
||||
}
|
||||
@@ -289,6 +296,235 @@ public class PresentsDObjectMgr
|
||||
log.info("DOMGR exited.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the dobjmgr shut itself down soon- you may want to try using {@link
|
||||
* Invoker#shutdown} which will make sure that both the Invoker and DObjectMgr are empty and
|
||||
* then shut them both down.
|
||||
*/
|
||||
public void harshShutdown ()
|
||||
{
|
||||
postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
_running = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps collected profiling information to the system log.
|
||||
*/
|
||||
public void dumpUnitProfiles ()
|
||||
{
|
||||
for (Map.Entry<String,UnitProfile> entry : _profiles.entrySet()) {
|
||||
log.info("P: " + entry.getKey() + " => " + entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as a helper for <code>ObjectDestroyedEvent</code> events. It removes the object from
|
||||
* the object table.
|
||||
*
|
||||
* @return true if the event should be dispatched, false if it should be aborted.
|
||||
*/
|
||||
public boolean objectDestroyed (DEvent event, DObject target)
|
||||
{
|
||||
int oid = target.getOid();
|
||||
|
||||
// log.info("Removing destroyed object from table [oid=" + oid + "].");
|
||||
|
||||
// remove the object from the table
|
||||
_objects.remove(oid);
|
||||
|
||||
// inactivate the object
|
||||
target.setManager(null);
|
||||
|
||||
// deal with any remaining oid lists that reference this object
|
||||
Reference[] refs = _refs.remove(oid);
|
||||
if (refs != null) {
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
// skip empty spots
|
||||
if (refs[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference ref = refs[i];
|
||||
DObject reffer = _objects.get(ref.reffingOid);
|
||||
|
||||
// ensure that the referencing object is still around
|
||||
if (reffer != null) {
|
||||
// post an object removed event to clear the reference
|
||||
postEvent(new ObjectRemovedEvent(ref.reffingOid, ref.field, oid));
|
||||
// log.info("Forcing removal " + ref + ".");
|
||||
|
||||
} else {
|
||||
log.info("Dangling reference from inactive object " + ref + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if this object has any oid list fields that are still referencing other objects, we need
|
||||
// to clear out those references
|
||||
Class oclass = target.getClass();
|
||||
Field[] fields = oclass.getFields();
|
||||
for (int f = 0; f < fields.length; f++) {
|
||||
Field field = fields[f];
|
||||
|
||||
// ignore static and non-public fields
|
||||
int mods = field.getModifiers();
|
||||
if ((mods & Modifier.STATIC) != 0 || (mods & Modifier.PUBLIC) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore non-oidlist fields
|
||||
if (!OidList.class.isAssignableFrom(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
OidList list = (OidList)field.get(target);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
clearReference(target, field.getName(), list.get(i));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to clean up after oid list field [target=" + target +
|
||||
", field=" + field + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as a helper for <code>ObjectAddedEvent</code> events. It updates the object/oid list
|
||||
* tracking structures.
|
||||
*
|
||||
* @return true if the event should be dispatched, false if it should be aborted.
|
||||
*/
|
||||
public boolean objectAdded (DEvent event, DObject target)
|
||||
{
|
||||
ObjectAddedEvent oae = (ObjectAddedEvent)event;
|
||||
int oid = oae.getOid();
|
||||
|
||||
// ensure that the target object exists
|
||||
if (!_objects.containsKey(oid)) {
|
||||
log.info("Rejecting object added event of non-existent object " +
|
||||
"[refferOid=" + target.getOid() + ", reffedOid=" + oid + "].");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the reference vector for the referenced object. we use bare arrays rather than
|
||||
// something like an array list to conserve memory. there will be many objects and
|
||||
// references
|
||||
Reference[] refs = _refs.get(oid);
|
||||
if (refs == null) {
|
||||
refs = new Reference[DEFREFVEC_SIZE];
|
||||
_refs.put(oid, refs);
|
||||
}
|
||||
|
||||
// determine where to add the reference
|
||||
Reference ref = new Reference(target.getOid(), oae.getName(), oid);
|
||||
int rpos = -1;
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (ref.equals(refs[i])) {
|
||||
log.warning("Ignoring request to track existing reference " + ref + ".");
|
||||
return true;
|
||||
} else if (refs[i] == null && rpos == -1) {
|
||||
rpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
// expand the refvec if necessary
|
||||
if (rpos == -1) {
|
||||
Reference[] nrefs = new Reference[refs.length*2];
|
||||
System.arraycopy(refs, 0, nrefs, 0, refs.length);
|
||||
rpos = refs.length;
|
||||
_refs.put(oid, refs = nrefs);
|
||||
}
|
||||
|
||||
// finally add the reference
|
||||
refs[rpos] = ref;
|
||||
|
||||
// log.info("Tracked reference " + ref + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as a helper for <code>ObjectRemovedEvent</code> events. It updates the object/oid
|
||||
* list tracking structures.
|
||||
*
|
||||
* @return true if the event should be dispatched, false if it should be aborted.
|
||||
*/
|
||||
public boolean objectRemoved (DEvent event, DObject target)
|
||||
{
|
||||
ObjectRemovedEvent ore = (ObjectRemovedEvent)event;
|
||||
String field = ore.getName();
|
||||
int toid = target.getOid();
|
||||
int oid = ore.getOid();
|
||||
|
||||
// log.info("Processing object removed [from=" + toid + ", roid=" + toid + "].");
|
||||
|
||||
// get the reference vector for the referenced object
|
||||
Reference[] refs = _refs.get(oid);
|
||||
if (refs == null) {
|
||||
// this can happen normally when an object is destroyed. it will remove itself from the
|
||||
// reference system and then generate object removed events for all of its referencees.
|
||||
// so we opt not to log anything in this case
|
||||
|
||||
// log.info("Object removed without reference to track it [toid=" + toid +
|
||||
// ", field=" + field + ", oid=" + oid + "].");
|
||||
return true;
|
||||
}
|
||||
|
||||
// look for the matching reference
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
Reference ref = refs[i];
|
||||
if (ref != null && ref.equals(toid, field)) {
|
||||
// log.info("Removed reference " + refs[i] + ".");
|
||||
refs[i] = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
log.warning("Unable to locate reference for removal [reffingOid=" + toid +
|
||||
", field=" + field + ", reffedOid=" + oid + "].");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should not need to be called except by the invoker during shutdown to ensure that things are
|
||||
* proceeding smoothly.
|
||||
*/
|
||||
public boolean queueIsEmpty ()
|
||||
{
|
||||
return !_evqueue.hasElements();
|
||||
}
|
||||
|
||||
// from interface ReportManager.Reporter
|
||||
public void appendReport (StringBuilder report, long now, long sinceLast, boolean reset)
|
||||
{
|
||||
report.append("* presents.PresentsDObjectMgr:\n");
|
||||
int queueSize = _evqueue.size();
|
||||
report.append("- Queue size: ").append(queueSize).append("\n");
|
||||
report.append("- Max queue size: ").append(_current.maxQueueSize).append("\n");
|
||||
report.append("- Units executed: ").append(_current.eventCount).append("\n");
|
||||
|
||||
if (UNIT_PROF_ENABLED) {
|
||||
report.append("- Unit profiles: ").append(_profiles.size()).append("\n");
|
||||
for (Map.Entry<String,UnitProfile> entry : _profiles.entrySet()) {
|
||||
report.append(" ").append(entry.getKey());
|
||||
report.append(" ").append(entry.getValue()).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// roll over stats
|
||||
if (reset) {
|
||||
_recent = _current;
|
||||
_current = new Stats();
|
||||
_current.maxQueueSize = queueSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single unit from the queue.
|
||||
*/
|
||||
@@ -352,7 +588,7 @@ public class PresentsDObjectMgr
|
||||
cname = StringUtil.shortClassName(ival);
|
||||
} else if (unit instanceof InvocationRequestEvent) {
|
||||
InvocationRequestEvent ire = (InvocationRequestEvent)unit;
|
||||
Class c = PresentsServer.invmgr.getDispatcherClass(ire.getInvCode());
|
||||
Class c = _invmgr.getDispatcherClass(ire.getInvCode());
|
||||
cname = (c == null) ? "dobj.InvocationRequestEvent:(no longer registered)" :
|
||||
StringUtil.shortClassName(c) + ":" + ire.getMethodId();
|
||||
} else {
|
||||
@@ -481,105 +717,6 @@ public class PresentsDObjectMgr
|
||||
log.warning("Fatal error caused by '" + causer + "': " + error, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the dobjmgr shut itself down soon- you may want to try using {@link
|
||||
* Invoker#shutdown} which will make sure that both the Invoker and DObjectMgr are empty and
|
||||
* then shut them both down.
|
||||
*/
|
||||
public void harshShutdown ()
|
||||
{
|
||||
postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
_running = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps collected profiling information to the system log.
|
||||
*/
|
||||
public void dumpUnitProfiles ()
|
||||
{
|
||||
for (Map.Entry<String,UnitProfile> entry : _profiles.entrySet()) {
|
||||
log.info("P: " + entry.getKey() + " => " + entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as a helper for <code>ObjectDestroyedEvent</code> events. It removes the object from
|
||||
* the object table.
|
||||
*
|
||||
* @return true if the event should be dispatched, false if it should be aborted.
|
||||
*/
|
||||
public boolean objectDestroyed (DEvent event, DObject target)
|
||||
{
|
||||
int oid = target.getOid();
|
||||
|
||||
// log.info("Removing destroyed object from table [oid=" + oid + "].");
|
||||
|
||||
// remove the object from the table
|
||||
_objects.remove(oid);
|
||||
|
||||
// inactivate the object
|
||||
target.setManager(null);
|
||||
|
||||
// deal with any remaining oid lists that reference this object
|
||||
Reference[] refs = _refs.remove(oid);
|
||||
if (refs != null) {
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
// skip empty spots
|
||||
if (refs[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference ref = refs[i];
|
||||
DObject reffer = _objects.get(ref.reffingOid);
|
||||
|
||||
// ensure that the referencing object is still around
|
||||
if (reffer != null) {
|
||||
// post an object removed event to clear the reference
|
||||
postEvent(new ObjectRemovedEvent(ref.reffingOid, ref.field, oid));
|
||||
// log.info("Forcing removal " + ref + ".");
|
||||
|
||||
} else {
|
||||
log.info("Dangling reference from inactive object " + ref + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if this object has any oid list fields that are still referencing other objects, we need
|
||||
// to clear out those references
|
||||
Class oclass = target.getClass();
|
||||
Field[] fields = oclass.getFields();
|
||||
for (int f = 0; f < fields.length; f++) {
|
||||
Field field = fields[f];
|
||||
|
||||
// ignore static and non-public fields
|
||||
int mods = field.getModifiers();
|
||||
if ((mods & Modifier.STATIC) != 0 || (mods & Modifier.PUBLIC) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore non-oidlist fields
|
||||
if (!OidList.class.isAssignableFrom(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
OidList list = (OidList)field.get(target);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
clearReference(target, field.getName(), list.get(i));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to clean up after oid list field [target=" + target +
|
||||
", field=" + field + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by <code>objectDestroyed</code>; clears out the tracking info for a reference by the
|
||||
* supplied object to the specified oid via the specified field.
|
||||
@@ -615,111 +752,6 @@ public class PresentsDObjectMgr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as a helper for <code>ObjectAddedEvent</code> events. It updates the object/oid list
|
||||
* tracking structures.
|
||||
*
|
||||
* @return true if the event should be dispatched, false if it should be aborted.
|
||||
*/
|
||||
public boolean objectAdded (DEvent event, DObject target)
|
||||
{
|
||||
ObjectAddedEvent oae = (ObjectAddedEvent)event;
|
||||
int oid = oae.getOid();
|
||||
|
||||
// ensure that the target object exists
|
||||
if (!_objects.containsKey(oid)) {
|
||||
log.info("Rejecting object added event of non-existent object " +
|
||||
"[refferOid=" + target.getOid() + ", reffedOid=" + oid + "].");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the reference vector for the referenced object. we use bare arrays rather than
|
||||
// something like an array list to conserve memory. there will be many objects and
|
||||
// references
|
||||
Reference[] refs = _refs.get(oid);
|
||||
if (refs == null) {
|
||||
refs = new Reference[DEFREFVEC_SIZE];
|
||||
_refs.put(oid, refs);
|
||||
}
|
||||
|
||||
// determine where to add the reference
|
||||
Reference ref = new Reference(target.getOid(), oae.getName(), oid);
|
||||
int rpos = -1;
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (ref.equals(refs[i])) {
|
||||
log.warning("Ignoring request to track existing reference " + ref + ".");
|
||||
return true;
|
||||
} else if (refs[i] == null && rpos == -1) {
|
||||
rpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
// expand the refvec if necessary
|
||||
if (rpos == -1) {
|
||||
Reference[] nrefs = new Reference[refs.length*2];
|
||||
System.arraycopy(refs, 0, nrefs, 0, refs.length);
|
||||
rpos = refs.length;
|
||||
_refs.put(oid, refs = nrefs);
|
||||
}
|
||||
|
||||
// finally add the reference
|
||||
refs[rpos] = ref;
|
||||
|
||||
// log.info("Tracked reference " + ref + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called as a helper for <code>ObjectRemovedEvent</code> events. It updates the object/oid
|
||||
* list tracking structures.
|
||||
*
|
||||
* @return true if the event should be dispatched, false if it should be aborted.
|
||||
*/
|
||||
public boolean objectRemoved (DEvent event, DObject target)
|
||||
{
|
||||
ObjectRemovedEvent ore = (ObjectRemovedEvent)event;
|
||||
String field = ore.getName();
|
||||
int toid = target.getOid();
|
||||
int oid = ore.getOid();
|
||||
|
||||
// log.info("Processing object removed [from=" + toid + ", roid=" + toid + "].");
|
||||
|
||||
// get the reference vector for the referenced object
|
||||
Reference[] refs = _refs.get(oid);
|
||||
if (refs == null) {
|
||||
// this can happen normally when an object is destroyed. it will remove itself from the
|
||||
// reference system and then generate object removed events for all of its referencees.
|
||||
// so we opt not to log anything in this case
|
||||
|
||||
// log.info("Object removed without reference to track it [toid=" + toid +
|
||||
// ", field=" + field + ", oid=" + oid + "].");
|
||||
return true;
|
||||
}
|
||||
|
||||
// look for the matching reference
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
Reference ref = refs[i];
|
||||
if (ref != null && ref.equals(toid, field)) {
|
||||
// log.info("Removed reference " + refs[i] + ".");
|
||||
refs[i] = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
log.warning("Unable to locate reference for removal [reffingOid=" + toid +
|
||||
", field=" + field + ", reffedOid=" + oid + "].");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should not need to be called except by the invoker during shutdown to ensure that things are
|
||||
* proceeding smoothly.
|
||||
*/
|
||||
public boolean queueIsEmpty ()
|
||||
{
|
||||
return !_evqueue.hasElements();
|
||||
}
|
||||
|
||||
protected synchronized boolean isRunning ()
|
||||
{
|
||||
return _running;
|
||||
@@ -735,28 +767,26 @@ public class PresentsDObjectMgr
|
||||
return _nextOid;
|
||||
}
|
||||
|
||||
// from interface PresentsServer.Reporter
|
||||
public void appendReport (StringBuilder report, long now, long sinceLast, boolean reset)
|
||||
/**
|
||||
* Registers our event helper methods.
|
||||
*/
|
||||
protected void registerEventHelpers ()
|
||||
{
|
||||
report.append("* presents.PresentsDObjectMgr:\n");
|
||||
int queueSize = _evqueue.size();
|
||||
report.append("- Queue size: ").append(queueSize).append("\n");
|
||||
report.append("- Max queue size: ").append(_current.maxQueueSize).append("\n");
|
||||
report.append("- Units executed: ").append(_current.eventCount).append("\n");
|
||||
Class[] ptypes = new Class[] { DEvent.class, DObject.class };
|
||||
Method method;
|
||||
|
||||
if (UNIT_PROF_ENABLED) {
|
||||
report.append("- Unit profiles: ").append(_profiles.size()).append("\n");
|
||||
for (Map.Entry<String,UnitProfile> entry : _profiles.entrySet()) {
|
||||
report.append(" ").append(entry.getKey());
|
||||
report.append(" ").append(entry.getValue()).append("\n");
|
||||
}
|
||||
}
|
||||
try {
|
||||
method = PresentsDObjectMgr.class.getMethod("objectDestroyed", ptypes);
|
||||
_helpers.put(ObjectDestroyedEvent.class, method);
|
||||
|
||||
// roll over stats
|
||||
if (reset) {
|
||||
_recent = _current;
|
||||
_current = new Stats();
|
||||
_current.maxQueueSize = queueSize;
|
||||
method = PresentsDObjectMgr.class.getMethod("objectAdded", ptypes);
|
||||
_helpers.put(ObjectAddedEvent.class, method);
|
||||
|
||||
method = PresentsDObjectMgr.class.getMethod("objectRemoved", ptypes);
|
||||
_helpers.put(ObjectRemovedEvent.class, method);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to register event helpers [error=" + e + "].");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,29 +876,6 @@ public class PresentsDObjectMgr
|
||||
protected int _action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers our event helper methods.
|
||||
*/
|
||||
protected static void registerEventHelpers ()
|
||||
{
|
||||
Class[] ptypes = new Class[] { DEvent.class, DObject.class };
|
||||
Method method;
|
||||
|
||||
try {
|
||||
method = PresentsDObjectMgr.class.getMethod("objectDestroyed", ptypes);
|
||||
_helpers.put(ObjectDestroyedEvent.class, method);
|
||||
|
||||
method = PresentsDObjectMgr.class.getMethod("objectAdded", ptypes);
|
||||
_helpers.put(ObjectAddedEvent.class, method);
|
||||
|
||||
method = PresentsDObjectMgr.class.getMethod("objectRemoved", ptypes);
|
||||
_helpers.put(ObjectRemovedEvent.class, method);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to register event helpers [error=" + e + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to track references of objects in oid lists.
|
||||
*/
|
||||
@@ -947,7 +954,7 @@ public class PresentsDObjectMgr
|
||||
protected Queue<Object> _evqueue = new Queue<Object>();
|
||||
|
||||
/** The managed distributed objects table. */
|
||||
protected HashIntMap<DObject> _objects = new HashIntMap<DObject>();
|
||||
protected IntMap<DObject> _objects = IntMaps.newHashIntMap();
|
||||
|
||||
/** Used to assign a unique oid to each distributed object. */
|
||||
protected int _nextOid = 0;
|
||||
@@ -960,28 +967,33 @@ public class PresentsDObjectMgr
|
||||
protected Throttle _fatalThrottle = new Throttle(30, 60*1000L);
|
||||
|
||||
/** Used to track oid list references of distributed objects. */
|
||||
protected HashIntMap<Reference[]> _refs = new HashIntMap<Reference[]>();
|
||||
protected IntMap<Reference[]> _refs = IntMaps.newHashIntMap();
|
||||
|
||||
/** The default access controller to use when creating distributed objects. */
|
||||
protected AccessController _defaultController;
|
||||
|
||||
/** Maintains proxy information for any proxied distributed objects. */
|
||||
protected HashIntMap<ProxyReference> _proxies = new HashIntMap<ProxyReference>();
|
||||
protected IntMap<ProxyReference> _proxies = IntMaps.newHashIntMap();
|
||||
|
||||
/** We keep track of which thread is executing the event loop so that other services can
|
||||
* enforce restrictions on code that should or should not be called from the event dispatch
|
||||
* thread. */
|
||||
/** keeps Track of which thread is executing the event loop so that other services can enforce
|
||||
* restrictions on code that should or should not be called from the event dispatch thread. */
|
||||
protected Thread _dobjThread;
|
||||
|
||||
/** A monotonically increasing counter used to assign an id to all dispatched events. */
|
||||
protected long _nextEventId = 1;
|
||||
|
||||
/** Used to profile our events and runnable units. */
|
||||
protected HashMap<String,UnitProfile> _profiles = new HashMap<String,UnitProfile>();
|
||||
protected Map<String,UnitProfile> _profiles = Maps.newHashMap();
|
||||
|
||||
/** Used to track runtime statistics. */
|
||||
protected Stats _recent = new Stats(), _current = _recent;
|
||||
|
||||
/** Maps event classes to helpers that perform additional processing for particular events. */
|
||||
protected Map<Class,Method> _helpers = Maps.newHashMap();
|
||||
|
||||
// injected dependencies
|
||||
@Inject protected InvocationManager _invmgr;
|
||||
|
||||
/** Whether or not unit profiling is enabled. */
|
||||
protected static final boolean UNIT_PROF_ENABLED = false;
|
||||
|
||||
@@ -990,9 +1002,4 @@ public class PresentsDObjectMgr
|
||||
|
||||
/** The default size of an oid list refs vector. */
|
||||
protected static final int DEFREFVEC_SIZE = 4;
|
||||
|
||||
/** This table maps event classes to helper methods that perform some additional processing for
|
||||
* particular events. */
|
||||
protected static HashMap<Class,Method> _helpers = new HashMap<Class,Method>();
|
||||
static { registerEventHelpers(); }
|
||||
}
|
||||
|
||||
@@ -23,43 +23,41 @@ package com.threerings.presents.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Extends the generic {@link Invoker} and integrates it a bit more into
|
||||
* the Presents system.
|
||||
* Extends the generic {@link Invoker} and integrates it a bit more into the Presents system.
|
||||
*/
|
||||
public class PresentsInvoker extends Invoker
|
||||
implements PresentsServer.Reporter
|
||||
implements ReportManager.Reporter
|
||||
{
|
||||
/**
|
||||
* Creates an invoker that will post results to the supplied
|
||||
* distributed object manager.
|
||||
*/
|
||||
public PresentsInvoker (PresentsDObjectMgr omgr)
|
||||
@Inject public PresentsInvoker (PresentsDObjectMgr omgr, ReportManager repmgr)
|
||||
{
|
||||
super("presents.Invoker", omgr);
|
||||
_omgr = omgr;
|
||||
if (PERF_TRACK) {
|
||||
PresentsServer.registerReporter(this);
|
||||
repmgr.registerReporter(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will do a sophisticated shutdown of both itself and the DObjectManager
|
||||
* thread.
|
||||
*/
|
||||
@Override // from Invoker
|
||||
public void shutdown ()
|
||||
{
|
||||
// this will do a sophisticated shutdown of both ourself and the dobjmgr
|
||||
_queue.append(new ShutdownUnit());
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void appendReport (
|
||||
StringBuilder buf, long now, long sinceLast, boolean reset)
|
||||
// from interface ReportManager.Reporter
|
||||
public void appendReport (StringBuilder buf, long now, long sinceLast, boolean reset)
|
||||
{
|
||||
buf.append("* presents.util.Invoker:\n");
|
||||
int qsize = _queue.size();
|
||||
@@ -93,7 +91,7 @@ public class PresentsInvoker extends Invoker
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
@Override // from Invoker
|
||||
protected void willInvokeUnit (Unit unit, long start)
|
||||
{
|
||||
super.willInvokeUnit(unit, start);
|
||||
@@ -111,7 +109,7 @@ public class PresentsInvoker extends Invoker
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
@Override // from Invoker
|
||||
protected void didInvokeUnit (Unit unit, long start)
|
||||
{
|
||||
super.didInvokeUnit(unit, start);
|
||||
@@ -124,9 +122,8 @@ public class PresentsInvoker extends Invoker
|
||||
}
|
||||
|
||||
/**
|
||||
* This unit gets posted back and forth between the invoker and DObjectMgr
|
||||
* until both of their queues are empty and they can both be safely
|
||||
* shutdown.
|
||||
* This gets posted back and forth between the invoker and DObjectMgr until both of their
|
||||
* queues are empty and they can both be safely shutdown.
|
||||
*/
|
||||
protected class ShutdownUnit extends Unit
|
||||
{
|
||||
|
||||
@@ -21,13 +21,17 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.RunQueue;
|
||||
import com.samskivert.util.SystemInfo;
|
||||
|
||||
import com.threerings.presents.annotation.EventQueue;
|
||||
import com.threerings.presents.annotation.MainInvoker;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.AccessController;
|
||||
import com.threerings.presents.server.net.ConnectionManager;
|
||||
@@ -44,74 +48,40 @@ import static com.threerings.presents.Log.log;
|
||||
*/
|
||||
public class PresentsServer
|
||||
{
|
||||
/** Used to generate "state of the server" reports. See {@link #registerReporter}. */
|
||||
public static interface Reporter
|
||||
/** Configures dependencies needed by the Presents services. */
|
||||
public static class Module extends AbstractModule
|
||||
{
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void appendReport (StringBuilder buffer, long now, long sinceLast, boolean reset);
|
||||
@Override protected void configure () {
|
||||
bind(Invoker.class).annotatedWith(MainInvoker.class).to(ServerInvoker.class);
|
||||
bind(RunQueue.class).annotatedWith(EventQueue.class).to(PresentsDObjectMgr.class);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementers of this interface will be notified when the server is shutting down. */
|
||||
public static interface Shutdowner
|
||||
{
|
||||
/**
|
||||
* Called when the server is shutting down.
|
||||
*/
|
||||
public void shutdown ();
|
||||
}
|
||||
|
||||
/** The manager of network connections. */
|
||||
/** OBSOLETE! Don't use me. */
|
||||
public static ConnectionManager conmgr;
|
||||
|
||||
/** The manager of clients. */
|
||||
/** OBSOLETE! Don't use me. */
|
||||
public static ClientManager clmgr;
|
||||
|
||||
/** The distributed object manager. */
|
||||
/** OBSOLETE! Don't use me. */
|
||||
public static PresentsDObjectMgr omgr;
|
||||
|
||||
/** The invocation manager. */
|
||||
/** OBSOLETE! Don't use me. */
|
||||
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. */
|
||||
public static PresentsInvoker invoker;
|
||||
|
||||
/**
|
||||
* Registers an entity that will be notified when the server is shutting down.
|
||||
*/
|
||||
public static void registerShutdowner (Shutdowner downer)
|
||||
{
|
||||
_downers.add(downer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the shutdowner from hearing when the server is shutdown.
|
||||
*/
|
||||
public static void unregisterShutdowner (Shutdowner downer)
|
||||
{
|
||||
_downers.remove(downer);
|
||||
}
|
||||
/** OBSOLETE! Don't use me. */
|
||||
public static Invoker invoker;
|
||||
|
||||
/**
|
||||
* The default entry point for the server.
|
||||
*/
|
||||
public static void main (String[] args)
|
||||
{
|
||||
log.info("Presents server starting...");
|
||||
|
||||
PresentsServer server = new PresentsServer();
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
PresentsServer server = injector.getInstance(PresentsServer.class);
|
||||
try {
|
||||
// initialize the server
|
||||
server.init();
|
||||
server.init(injector);
|
||||
|
||||
// check to see if we should load and invoke a test module before running the server
|
||||
String testmod = System.getProperty("test_module");
|
||||
@@ -139,9 +109,16 @@ public class PresentsServer
|
||||
/**
|
||||
* Initializes all of the server services and prepares for operation.
|
||||
*/
|
||||
public void init ()
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
// populate our legacy statics
|
||||
conmgr = _conmgr;
|
||||
clmgr = _clmgr;
|
||||
omgr = _omgr;
|
||||
invmgr = _invmgr;
|
||||
invoker = _invoker;
|
||||
|
||||
// output general system information
|
||||
SystemInfo si = new SystemInfo();
|
||||
log.info("Starting up server [os=" + si.osToString() + ", jvm=" + si.jvmToString() +
|
||||
@@ -150,48 +127,26 @@ public class PresentsServer
|
||||
// register SIGTERM, SIGINT (ctrl-c) and a SIGHUP handlers
|
||||
boolean registered = false;
|
||||
try {
|
||||
registered = new SunSignalHandler().init(this);
|
||||
registered = injector.getInstance(SunSignalHandler.class).init();
|
||||
} catch (Throwable t) {
|
||||
log.warning("Unable to register Sun signal handlers [error=" + t + "].");
|
||||
}
|
||||
if (!registered) {
|
||||
new NativeSignalHandler().init(this);
|
||||
injector.getInstance(NativeSignalHandler.class).init();
|
||||
}
|
||||
|
||||
// create our distributed object manager
|
||||
omgr = createDObjectManager();
|
||||
|
||||
// configure the dobject manager with our access controller
|
||||
omgr.setDefaultAccessController(createDefaultObjectAccessController());
|
||||
_omgr.setDefaultAccessController(createDefaultObjectAccessController());
|
||||
|
||||
// create and start up our invoker
|
||||
invoker = new PresentsInvoker(omgr) {
|
||||
protected void didShutdown () {
|
||||
invokerDidShutdown();
|
||||
}
|
||||
};
|
||||
invoker.start();
|
||||
// start the main invoker thread
|
||||
_invoker.start();
|
||||
|
||||
// create our connection manager
|
||||
conmgr = new ConnectionManager(getListenPorts(), getDatagramPorts());
|
||||
conmgr.setAuthenticator(createAuthenticator());
|
||||
|
||||
// create our client manager
|
||||
clmgr = createClientManager(conmgr);
|
||||
|
||||
// create our invocation manager
|
||||
invmgr = new InvocationManager(omgr);
|
||||
// configure our connection manager
|
||||
_conmgr.init(getListenPorts(), getDatagramPorts());
|
||||
_conmgr.setAuthenticator(createAuthenticator());
|
||||
|
||||
// initialize the time base services
|
||||
TimeBaseProvider.init(invmgr, omgr);
|
||||
|
||||
// queue up an interval which will generate reports
|
||||
_reportInterval = new Interval(omgr) {
|
||||
public void expired () {
|
||||
logReport(generateReport(System.currentTimeMillis(), true));
|
||||
}
|
||||
};
|
||||
_reportInterval.schedule(REPORT_INTERVAL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,21 +157,21 @@ public class PresentsServer
|
||||
return new DummyAuthenticator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the client manager to be used on this server.
|
||||
*/
|
||||
protected ClientManager createClientManager (ConnectionManager conmgr)
|
||||
{
|
||||
return new ClientManager(conmgr);
|
||||
}
|
||||
// /**
|
||||
// * Creates the client manager to be used on this server.
|
||||
// */
|
||||
// protected ClientManager createClientManager (ConnectionManager conmgr)
|
||||
// {
|
||||
// return new ClientManager(conmgr);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates the distributed object manager to be used on this server.
|
||||
*/
|
||||
protected PresentsDObjectMgr createDObjectManager ()
|
||||
{
|
||||
return new PresentsDObjectMgr();
|
||||
}
|
||||
// /**
|
||||
// * Creates the distributed object manager to be used on this server.
|
||||
// */
|
||||
// protected PresentsDObjectMgr createDObjectManager ()
|
||||
// {
|
||||
// return new PresentsDObjectMgr();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Defines the default object access policy for all {@link DObject} instances. The default
|
||||
@@ -253,119 +208,26 @@ public class PresentsServer
|
||||
omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
// start up the connection manager
|
||||
conmgr.start();
|
||||
_conmgr.start();
|
||||
}
|
||||
});
|
||||
// invoke the dobjmgr event loop
|
||||
omgr.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
_reporters.add(reporter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a report for all system services registered as a {@link Reporter}.
|
||||
*/
|
||||
public static String generateReport ()
|
||||
{
|
||||
return generateReport(System.currentTimeMillis(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and logs a "state of server" report.
|
||||
*/
|
||||
protected static String generateReport (long now, boolean reset)
|
||||
{
|
||||
long sinceLast = now - _lastReportStamp;
|
||||
long uptime = now - _serverStartTime;
|
||||
StringBuilder report = new StringBuilder("State of server report:\n");
|
||||
|
||||
report.append("- Uptime: ");
|
||||
report.append(StringUtil.intervalToString(uptime)).append("\n");
|
||||
report.append("- Report period: ");
|
||||
report.append(StringUtil.intervalToString(sinceLast)).append("\n");
|
||||
|
||||
// report on the state of memory
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
long total = rt.totalMemory(), max = rt.maxMemory();
|
||||
long used = (total - rt.freeMemory());
|
||||
report.append("- Memory: ").append(used/1024).append("k used, ");
|
||||
report.append(total/1024).append("k total, ");
|
||||
report.append(max/1024).append("k max\n");
|
||||
|
||||
for (int ii = 0; ii < _reporters.size(); ii++) {
|
||||
Reporter rptr = _reporters.get(ii);
|
||||
try {
|
||||
rptr.appendReport(report, now, sinceLast, reset);
|
||||
} catch (Throwable t) {
|
||||
log.warning("Reporter choked [rptr=" + rptr + "].", t);
|
||||
}
|
||||
}
|
||||
|
||||
/* The following Interval debug methods are no longer supported,
|
||||
* but they could be added back easily if needed.
|
||||
report.append("* samskivert.Interval:\n");
|
||||
report.append("- Registered intervals: ");
|
||||
report.append(Interval.registeredIntervalCount());
|
||||
report.append("\n- Fired since last report: ");
|
||||
report.append(Interval.getAndClearFiredIntervals());
|
||||
report.append("\n");
|
||||
*/
|
||||
|
||||
// strip off the final newline
|
||||
int blen = report.length();
|
||||
if (report.charAt(blen-1) == '\n') {
|
||||
report.delete(blen-1, blen);
|
||||
}
|
||||
|
||||
// only reset the last report time if this is a periodic report
|
||||
if (reset) {
|
||||
_lastReportStamp = now;
|
||||
}
|
||||
|
||||
return report.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
log.info(report);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ()
|
||||
{
|
||||
ObserverList<Shutdowner> downers = _downers;
|
||||
if (downers == null) {
|
||||
log.warning("Refusing repeat shutdown request.");
|
||||
return;
|
||||
}
|
||||
_downers = null;
|
||||
|
||||
// shut down all shutdown participants
|
||||
downers.apply(new ObserverList.ObserverOp<Shutdowner>() {
|
||||
public boolean apply (Shutdowner downer) {
|
||||
downer.shutdown();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// shutdown all registered shutdowners
|
||||
_shutmgr.shutdown();
|
||||
|
||||
// shut down the connection manager (this will cease all network activity but not actually
|
||||
// close the connections)
|
||||
if (conmgr.isRunning()) {
|
||||
conmgr.shutdown();
|
||||
if (_conmgr.isRunning()) {
|
||||
_conmgr.shutdown();
|
||||
}
|
||||
|
||||
// finally shut down the invoker and dobj manager (The invoker does both for us.)
|
||||
@@ -395,22 +257,39 @@ public class PresentsServer
|
||||
{
|
||||
}
|
||||
|
||||
/** Our interval that generates "state of server" reports. */
|
||||
protected Interval _reportInterval;
|
||||
/** Integrates the main invoker thread with the distributed object thread so that they can
|
||||
* coordinate the shutdown process to ensure that all (barring infinite loops) invoker units
|
||||
* and distributed object events are processed before the server shuts down. */
|
||||
protected static class ServerInvoker extends PresentsInvoker
|
||||
{
|
||||
@Inject public ServerInvoker (PresentsDObjectMgr omgr, ReportManager repmgr) {
|
||||
super(omgr, repmgr);
|
||||
}
|
||||
@Override protected void didShutdown () {
|
||||
_server.invokerDidShutdown();
|
||||
}
|
||||
@Inject protected PresentsServer _server;
|
||||
}
|
||||
|
||||
/** The time at which the server was started. */
|
||||
protected static long _serverStartTime = System.currentTimeMillis();
|
||||
/** The manager of distributed objects. */
|
||||
@Inject protected PresentsDObjectMgr _omgr;
|
||||
|
||||
/** The last time at which {@link #generateReport} was run. */
|
||||
protected static long _lastReportStamp = _serverStartTime;
|
||||
/** The manager of network connections. */
|
||||
@Inject protected ConnectionManager _conmgr;
|
||||
|
||||
/** Used to generate "state of server" reports. */
|
||||
protected static ArrayList<Reporter> _reporters = new ArrayList<Reporter>();
|
||||
/** The manager of clients. */
|
||||
@Inject protected ClientManager _clmgr;
|
||||
|
||||
/** A list of shutdown participants. */
|
||||
protected static ObserverList<Shutdowner> _downers =
|
||||
new ObserverList<Shutdowner>(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
/** The manager of invocation services. */
|
||||
@Inject protected InvocationManager _invmgr;
|
||||
|
||||
/** The frequency with which we generate "state of server" reports. */
|
||||
protected static final long REPORT_INTERVAL = 15 * 60 * 1000L;
|
||||
/** Handles orderly shutdown of our managers, etc. */
|
||||
@Inject protected ShutdownManager _shutmgr;
|
||||
|
||||
/** Handles generation of state of the server reports. */
|
||||
@Inject protected ReportManager _repmgr;
|
||||
|
||||
/** Used to invoke background tasks that should not be allowed to tie up the distributed object
|
||||
* manager thread (generally talking to databases and other relatively slow entities). */
|
||||
@Inject @MainInvoker protected Invoker _invoker;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
|
||||
import com.samskivert.util.Invoker;
|
||||
@@ -39,12 +42,12 @@ import static com.threerings.presents.Log.log;
|
||||
*/
|
||||
public class Rejector extends PresentsServer
|
||||
{
|
||||
// documentation inherited
|
||||
public void init ()
|
||||
@Override // from PresentsServer
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
super.init();
|
||||
conmgr.setAuthenticator(new RejectingAuthenticator());
|
||||
super.init(injector);
|
||||
_conmgr.setAuthenticator(new RejectingAuthenticator());
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
@@ -68,9 +71,10 @@ public class Rejector extends PresentsServer
|
||||
_errmsg = MessageBundle.tcompose(_errmsg, eargs);
|
||||
}
|
||||
|
||||
Rejector server = new Rejector();
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
Rejector server = injector.getInstance(Rejector.class);
|
||||
try {
|
||||
server.init();
|
||||
server.init(injector);
|
||||
server.run();
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to initialize server.", e);
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// $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 java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.RunQueue;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.annotation.EventQueue;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Handles the generation of server status reports.
|
||||
*/
|
||||
@Singleton
|
||||
public class ReportManager
|
||||
{
|
||||
/** 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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up our periodic report generation task on the supplied run queue.
|
||||
*/
|
||||
public void init (RunQueue rqueue)
|
||||
{
|
||||
// queue up an interval which will generate reports
|
||||
_reportInterval = new Interval(rqueue) {
|
||||
public void expired () {
|
||||
logReport(generateReport(System.currentTimeMillis(), true));
|
||||
}
|
||||
};
|
||||
_reportInterval.schedule(REPORT_INTERVAL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A report is generated by the presents server periodically in which server entities can
|
||||
* participate by registering a {@link Reporter} with this method.
|
||||
*/
|
||||
public void registerReporter (Reporter reporter)
|
||||
{
|
||||
_reporters.add(reporter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a report for all system services registered as a {@link Reporter}.
|
||||
*/
|
||||
public String generateReport ()
|
||||
{
|
||||
return generateReport(System.currentTimeMillis(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and logs a "state of server" report.
|
||||
*/
|
||||
protected String generateReport (long now, boolean reset)
|
||||
{
|
||||
long sinceLast = now - _lastReportStamp;
|
||||
long uptime = now - _serverStartTime;
|
||||
StringBuilder report = new StringBuilder("State of server report:\n");
|
||||
|
||||
report.append("- Uptime: ");
|
||||
report.append(StringUtil.intervalToString(uptime)).append("\n");
|
||||
report.append("- Report period: ");
|
||||
report.append(StringUtil.intervalToString(sinceLast)).append("\n");
|
||||
|
||||
// report on the state of memory
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
long total = rt.totalMemory(), max = rt.maxMemory();
|
||||
long used = (total - rt.freeMemory());
|
||||
report.append("- Memory: ").append(used/1024).append("k used, ");
|
||||
report.append(total/1024).append("k total, ");
|
||||
report.append(max/1024).append("k max\n");
|
||||
|
||||
for (int ii = 0; ii < _reporters.size(); ii++) {
|
||||
Reporter rptr = _reporters.get(ii);
|
||||
try {
|
||||
rptr.appendReport(report, now, sinceLast, reset);
|
||||
} catch (Throwable t) {
|
||||
log.warning("Reporter choked [rptr=" + rptr + "].", t);
|
||||
}
|
||||
}
|
||||
|
||||
/* The following Interval debug methods are no longer supported,
|
||||
* but they could be added back easily if needed.
|
||||
report.append("* samskivert.Interval:\n");
|
||||
report.append("- Registered intervals: ");
|
||||
report.append(Interval.registeredIntervalCount());
|
||||
report.append("\n- Fired since last report: ");
|
||||
report.append(Interval.getAndClearFiredIntervals());
|
||||
report.append("\n");
|
||||
*/
|
||||
|
||||
// strip off the final newline
|
||||
int blen = report.length();
|
||||
if (report.charAt(blen-1) == '\n') {
|
||||
report.delete(blen-1, blen);
|
||||
}
|
||||
|
||||
// only reset the last report time if this is a periodic report
|
||||
if (reset) {
|
||||
_lastReportStamp = now;
|
||||
}
|
||||
|
||||
return report.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
log.info(report);
|
||||
}
|
||||
|
||||
/** Our interval that generates "state of server" reports. */
|
||||
protected Interval _reportInterval;
|
||||
|
||||
/** The time at which the server was started. */
|
||||
protected long _serverStartTime = System.currentTimeMillis();
|
||||
|
||||
/** The last time at which {@link #generateReport} was run. */
|
||||
protected long _lastReportStamp = _serverStartTime;
|
||||
|
||||
/** Used to generate "state of server" reports. */
|
||||
protected List<Reporter> _reporters = Lists.newArrayList();
|
||||
|
||||
/** The frequency with which we generate "state of server" reports. */
|
||||
protected static final long REPORT_INTERVAL = 15 * 60 * 1000L;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// $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.Singleton;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Handles the orderly shutdown of all server services.
|
||||
*/
|
||||
@Singleton
|
||||
public class ShutdownManager
|
||||
{
|
||||
/** Implementers of this interface will be notified when the server is shutting down. */
|
||||
public static interface Shutdowner
|
||||
{
|
||||
/**
|
||||
* Called when the server is shutting down.
|
||||
*/
|
||||
public void shutdown ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an entity that will be notified when the server is shutting down.
|
||||
*/
|
||||
public void registerShutdowner (Shutdowner downer)
|
||||
{
|
||||
_downers.add(downer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the shutdowner from hearing when the server is shutdown.
|
||||
*/
|
||||
public void unregisterShutdowner (Shutdowner downer)
|
||||
{
|
||||
_downers.remove(downer);
|
||||
}
|
||||
|
||||
public void shutdown ()
|
||||
{
|
||||
ObserverList<Shutdowner> downers = _downers;
|
||||
if (downers == null) {
|
||||
log.warning("Refusing repeat shutdown request.");
|
||||
return;
|
||||
}
|
||||
_downers = null;
|
||||
|
||||
// shut down all shutdown participants
|
||||
downers.apply(new ObserverList.ObserverOp<Shutdowner>() {
|
||||
public boolean apply (Shutdowner downer) {
|
||||
downer.shutdown();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** A list of shutdown participants. */
|
||||
protected static ObserverList<Shutdowner> _downers = ObserverList.newSafeInOrder();
|
||||
}
|
||||
@@ -32,15 +32,27 @@ import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.samskivert.util.*;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.samskivert.util.LoopingThread;
|
||||
import com.samskivert.util.Queue;
|
||||
import com.samskivert.util.ResultListener;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import com.threerings.io.FramingOutputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
@@ -53,7 +65,8 @@ import com.threerings.presents.net.DownstreamMessage;
|
||||
import com.threerings.presents.util.DatagramSequencer;
|
||||
|
||||
import com.threerings.presents.server.Authenticator;
|
||||
import com.threerings.presents.server.PresentsServer;
|
||||
import com.threerings.presents.server.PresentsDObjectMgr;
|
||||
import com.threerings.presents.server.ReportManager;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
@@ -63,38 +76,36 @@ import static com.threerings.presents.Log.log;
|
||||
* closely with the connection manager because network I/O is done via a poll()-like mechanism
|
||||
* rather than via threads.
|
||||
*/
|
||||
@Singleton
|
||||
public class ConnectionManager extends LoopingThread
|
||||
implements PresentsServer.Reporter
|
||||
implements ReportManager.Reporter
|
||||
{
|
||||
/**
|
||||
* Constructs and initialized a connection manager (binding the socket on which it will listen
|
||||
* for client connections).
|
||||
* Creates a connection manager instance. Don't call this, Guice will do it for you.
|
||||
*/
|
||||
public ConnectionManager (int port)
|
||||
throws IOException
|
||||
{
|
||||
this(new int[] { port });
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and initialized a connection manager (binding socket on which it will listen for
|
||||
* client connections to each of the specified ports).
|
||||
*/
|
||||
public ConnectionManager (int[] ports)
|
||||
throws IOException
|
||||
{
|
||||
this(ports, new int[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and initialized a connection manager (binding socket on which it will listen for
|
||||
* client connections to each of the specified ports).
|
||||
*/
|
||||
public ConnectionManager (int[] ports, int[] datagramPorts)
|
||||
throws IOException
|
||||
@Inject public ConnectionManager (ReportManager repmgr)
|
||||
{
|
||||
super("ConnectionManager");
|
||||
repmgr.registerReporter(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and initialized a connection manager (binding socket on which it will listen for
|
||||
* client connections to each of the specified ports).
|
||||
*/
|
||||
public void init (int[] ports)
|
||||
throws IOException
|
||||
{
|
||||
init(ports, new int[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and initialized a connection manager (binding socket on which it will listen for
|
||||
* client connections to each of the specified ports).
|
||||
*/
|
||||
public void init (int[] ports, int[] datagramPorts)
|
||||
throws IOException
|
||||
{
|
||||
_ports = ports;
|
||||
_datagramPorts = datagramPorts;
|
||||
_selector = SelectorProvider.provider().openSelector();
|
||||
@@ -102,9 +113,6 @@ public class ConnectionManager extends LoopingThread
|
||||
// create our stats record
|
||||
_stats = new ConMgrStats();
|
||||
_lastStats = new ConMgrStats();
|
||||
|
||||
// register as a "state of server" reporter
|
||||
PresentsServer.registerReporter(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +215,7 @@ public class ConnectionManager extends LoopingThread
|
||||
_authq.append(conn);
|
||||
}
|
||||
|
||||
// documentation inherited from interface PresentsServer.Reporter
|
||||
// documentation inherited from interface ReportManager.Reporter
|
||||
public void appendReport (
|
||||
StringBuilder report, long now, long sinceLast, boolean reset)
|
||||
{
|
||||
@@ -842,7 +850,7 @@ public class ConnectionManager extends LoopingThread
|
||||
}
|
||||
|
||||
// more sanity check; messages must only be posted from the dobjmgr thread
|
||||
if (!PresentsServer.omgr.isDispatchThread()) {
|
||||
if (!_omgr.isDispatchThread()) {
|
||||
log.warning("Message posted on non-distributed object thread [conn=" + conn +
|
||||
", msg=" + msg + ", thread=" + Thread.currentThread() + "].");
|
||||
Thread.dumpStack();
|
||||
@@ -1029,6 +1037,15 @@ public class ConnectionManager extends LoopingThread
|
||||
protected int _msgs, _partials;
|
||||
}
|
||||
|
||||
/** Used to create an overflow queue on the first partial write. */
|
||||
protected PartialWriteHandler _oflowHandler = new PartialWriteHandler() {
|
||||
public void handlePartialWrite (Connection conn, ByteBuffer msgbuf) {
|
||||
// if we couldn't write all the data for this message, we'll need to establish an
|
||||
// overflow queue
|
||||
_oflowqs.put(conn, new OverflowQueue(conn, msgbuf));
|
||||
}
|
||||
};
|
||||
|
||||
protected int[] _ports, _datagramPorts;
|
||||
protected Authenticator _author;
|
||||
protected Selector _selector;
|
||||
@@ -1040,11 +1057,10 @@ public class ConnectionManager extends LoopingThread
|
||||
protected int _runtimeExceptionCount;
|
||||
|
||||
/** Maps selection keys to network event handlers. */
|
||||
protected HashMap<SelectionKey,NetEventHandler> _handlers =
|
||||
new HashMap<SelectionKey,NetEventHandler>();
|
||||
protected Map<SelectionKey,NetEventHandler> _handlers = Maps.newHashMap();
|
||||
|
||||
/** Connections mapped by identifier. */
|
||||
protected HashIntMap<Connection> _connections = new HashIntMap<Connection>();
|
||||
protected IntMap<Connection> _connections = IntMaps.newHashIntMap();
|
||||
|
||||
protected Queue<Connection> _deathq = new Queue<Connection>();
|
||||
protected Queue<AuthingConnection> _authq = new Queue<AuthingConnection>();
|
||||
@@ -1056,9 +1072,9 @@ public class ConnectionManager extends LoopingThread
|
||||
protected ByteBuffer _outbuf = ByteBuffer.allocateDirect(64 * 1024);
|
||||
protected ByteBuffer _databuf = ByteBuffer.allocateDirect(Client.MAX_DATAGRAM_SIZE);
|
||||
|
||||
protected HashMap<Connection,OverflowQueue> _oflowqs = new HashMap<Connection,OverflowQueue>();
|
||||
protected Map<Connection,OverflowQueue> _oflowqs = Maps.newHashMap();
|
||||
|
||||
protected ArrayList<ConnectionObserver> _observers = new ArrayList<ConnectionObserver>();
|
||||
protected List<ConnectionObserver> _observers = Lists.newArrayList();
|
||||
|
||||
/** Bytes in and out in the last reporting period. */
|
||||
protected long _bytesIn, _bytesOut;
|
||||
@@ -1075,14 +1091,8 @@ public class ConnectionManager extends LoopingThread
|
||||
/** A runnable to execute when the connection manager thread exits. */
|
||||
protected volatile Runnable _onExit;
|
||||
|
||||
/** Used to create an overflow queue on the first partial write. */
|
||||
protected PartialWriteHandler _oflowHandler = new PartialWriteHandler() {
|
||||
public void handlePartialWrite (Connection conn, ByteBuffer msgbuf) {
|
||||
// if we couldn't write all the data for this message, we'll need to establish an
|
||||
// overflow queue
|
||||
_oflowqs.put(conn, new OverflowQueue(conn, msgbuf));
|
||||
}
|
||||
};
|
||||
// injected dependencies
|
||||
@Inject protected PresentsDObjectMgr _omgr;
|
||||
|
||||
/** 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
|
||||
|
||||
+3
-68
@@ -129,73 +129,8 @@
|
||||
<move overwrite="true" file="../etc/empty.abc" tofile="${deploy.dir}/testslib.abc"/>
|
||||
</target>
|
||||
|
||||
<!-- test the component metadata bundling process -->
|
||||
<target name="cbundles" description="Build component bundles.">
|
||||
<!-- define our tasks -->
|
||||
<taskdef name="metabundle"
|
||||
classname="com.threerings.cast.bundle.tools.MetadataBundlerTask"
|
||||
classpathref="classpath"/>
|
||||
<taskdef name="cbundle"
|
||||
classname="com.threerings.cast.bundle.tools.ComponentBundlerTask"
|
||||
classpathref="classpath"/>
|
||||
|
||||
<!-- build the metadata bundles -->
|
||||
<metabundle actiondef="${cbundle.dir}/actions.xml"
|
||||
classdef="${cbundle.dir}/classes.xml"
|
||||
target="${cbundle.dir}/metadata.jar"/>
|
||||
|
||||
<!-- blow away the components map file so that we get a consistent -->
|
||||
<!-- mapping every time-->
|
||||
<delete file="${cbundle.dir}/components.map"/>
|
||||
|
||||
<!-- build the component bundles -->
|
||||
<cbundle actiondef="${cbundle.dir}/actions.xml"
|
||||
target="${cbundle.dir}/pirate/components.jar"
|
||||
mapfile="${cbundle.dir}/components.map"
|
||||
root="${cbundle.dir}/pirate">
|
||||
<fileset dir="${cbundle.dir}/pirate" includes="**/*.png"
|
||||
excludes="components/**"/>
|
||||
</cbundle>
|
||||
<cbundle actiondef="${cbundle.dir}/actions.xml"
|
||||
target="${cbundle.dir}/vessel/components.jar"
|
||||
mapfile="${cbundle.dir}/components.map"
|
||||
root="${cbundle.dir}/vessel">
|
||||
<fileset dir="${cbundle.dir}/vessel" includes="**/*.png"
|
||||
excludes="components/**"/>
|
||||
</cbundle>
|
||||
</target>
|
||||
|
||||
<!-- test the tileset bundling process -->
|
||||
<target name="tsbundles" description="Build tileset bundles.">
|
||||
<!-- blow away the tilesetid map file so that we get a consistent -->
|
||||
<!-- mapping every time-->
|
||||
<delete file="${tbundle.dir}/tilesets.map"/>
|
||||
|
||||
<!-- build the tileset bundles -->
|
||||
<taskdef name="tilebundle"
|
||||
classname="com.threerings.media.tile.bundle.tools.TileSetBundlerTask"
|
||||
classpathref="classpath"/>
|
||||
<tilebundle config="${tbundle.dir}/bundler-config.xml"
|
||||
mapfile="${tbundle.dir}/tilesets.map">
|
||||
<fileset dir="${tbundle.dir}/ground" includes="**/*.xml"/>
|
||||
</tilebundle>
|
||||
<tilebundle config="${tbundle.dir}/bundler-config.xml"
|
||||
mapfile="${tbundle.dir}/tilesets.map">
|
||||
<fileset dir="${tbundle.dir}/objects" includes="**/*.xml"/>
|
||||
</tilebundle>
|
||||
|
||||
<!-- build the fringe configurations -->
|
||||
<taskdef name="conffringe"
|
||||
classname="com.threerings.miso.tile.tools.CompileFringeConfigurationTask"
|
||||
classpathref="classpath"/>
|
||||
<conffringe
|
||||
tilesetmap="${tbundle.dir}/tilesets.map"
|
||||
fringedef="rsrc/config/miso/tile/fringeconf.xml"
|
||||
target="rsrc/config/miso/tile/fringeconf.dat"/>
|
||||
</target>
|
||||
|
||||
<!-- run the tests -->
|
||||
<target name="test" depends="compile,cbundles,tsbundles"
|
||||
<target name="test" depends="compile"
|
||||
description="Run the tests.">
|
||||
<junit printsummary="no" haltonfailure="yes" fork="${junit.fork}">
|
||||
<classpath refid="classpath"/>
|
||||
@@ -233,7 +168,8 @@
|
||||
</import>
|
||||
<filespec dir="src/as" includes="com/threerings/bureau/client/TestClientMain.as"/>
|
||||
</asc>
|
||||
<move overwrite="true" file="src/as/com/threerings/bureau/client/TestClientMain.abc" todir="dist"/>
|
||||
<move overwrite="true" todir="dist"
|
||||
file="src/as/com/threerings/bureau/client/TestClientMain.abc"/>
|
||||
</target>
|
||||
|
||||
<target name="bureau-runserver" depends="compile"
|
||||
@@ -243,7 +179,6 @@
|
||||
</java>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="bureau-testregistry" depends="compile"
|
||||
description="Run the bureau test server and tests the registry.">
|
||||
<java fork="true" classname="com.threerings.bureau.server.RegistryTester">
|
||||
|
||||
@@ -25,9 +25,14 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
|
||||
import com.threerings.presents.server.ShutdownManager;
|
||||
|
||||
import static com.threerings.bureau.Log.log;
|
||||
|
||||
/**
|
||||
@@ -59,11 +64,12 @@ public class RegistryTester
|
||||
*/
|
||||
public static void main (String[] args)
|
||||
{
|
||||
TestServer server = new TestServer();
|
||||
RegistryTester tester = new RegistryTester(server);
|
||||
Injector injector = Guice.createInjector(new TestServer.Module());
|
||||
TestServer server = injector.getInstance(TestServer.class);
|
||||
RegistryTester tester = injector.getInstance(RegistryTester.class);
|
||||
|
||||
try {
|
||||
server.init();
|
||||
server.init(injector);
|
||||
tester.start();
|
||||
server.run();
|
||||
|
||||
@@ -75,7 +81,7 @@ public class RegistryTester
|
||||
/**
|
||||
* Creates a new registry tester.
|
||||
*/
|
||||
public RegistryTester (TestServer server)
|
||||
@Inject public RegistryTester (TestServer server, ShutdownManager shutmgr)
|
||||
{
|
||||
_server = server;
|
||||
|
||||
@@ -92,7 +98,7 @@ public class RegistryTester
|
||||
|
||||
// stop the tests when the server shuts down
|
||||
// TODO: this is not called on Ctrl-C, need a way to shut down gracefully
|
||||
TestServer.registerShutdowner(new TestServer.Shutdowner() {
|
||||
shutmgr.registerShutdowner(new ShutdownManager.Shutdowner() {
|
||||
public void shutdown () {
|
||||
log.info("Shutting down tests");
|
||||
_stop = true;
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
package com.threerings.bureau.server;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.threerings.presents.server.PresentsServer;
|
||||
|
||||
import static com.threerings.bureau.Log.log;
|
||||
@@ -40,9 +43,10 @@ public class TestServer extends PresentsServer
|
||||
*/
|
||||
public static void main (String[] args)
|
||||
{
|
||||
final TestServer server = new TestServer();
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
TestServer server = injector.getInstance(TestServer.class);
|
||||
try {
|
||||
server.init();
|
||||
server.init(injector);
|
||||
setClientTarget("bureau-runclient");
|
||||
server.run();
|
||||
|
||||
@@ -51,20 +55,20 @@ public class TestServer extends PresentsServer
|
||||
}
|
||||
}
|
||||
|
||||
// inherit documentation - from PresentsServer
|
||||
public void init ()
|
||||
@Override // from PresentsServer
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
super.init();
|
||||
super.init(injector);
|
||||
breg = new BureauRegistry("localhost:47624", invmgr, omgr, invoker);
|
||||
}
|
||||
|
||||
static public void setClientTarget (String target)
|
||||
public static void setClientTarget (String target)
|
||||
{
|
||||
breg.setCommandGenerator("test", antCommandGenerator(target));
|
||||
}
|
||||
|
||||
static public BureauRegistry.CommandGenerator antCommandGenerator (
|
||||
public static BureauRegistry.CommandGenerator antCommandGenerator (
|
||||
final String target)
|
||||
{
|
||||
return new BureauRegistry.CommandGenerator() {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.threerings.crowd.data.JabberConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
@@ -15,10 +18,10 @@ import static com.threerings.crowd.Log.log;
|
||||
public class JabberServer extends CrowdServer
|
||||
{
|
||||
// documentation inherited
|
||||
public void init ()
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
super.init();
|
||||
super.init(injector);
|
||||
|
||||
// create a single location
|
||||
_pmgr = plreg.createPlace(new JabberConfig());
|
||||
@@ -26,9 +29,11 @@ public class JabberServer extends CrowdServer
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
JabberServer server = new JabberServer();
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
JabberServer server = injector.getInstance(JabberServer.class);
|
||||
|
||||
try {
|
||||
server.init();
|
||||
server.init(injector);
|
||||
server.run();
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to initialize server.", e);
|
||||
|
||||
@@ -21,43 +21,29 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import atunit.AtUnit;
|
||||
import atunit.Container;
|
||||
import atunit.Unit;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.threerings.presents.data.TestObject;
|
||||
import com.threerings.presents.dobj.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* A simple test case for the dobjmgr.
|
||||
*/
|
||||
public class DOMTest extends TestCase
|
||||
@RunWith(AtUnit.class)
|
||||
@Container(Container.Option.GUICE)
|
||||
public class DOMTest
|
||||
implements AttributeChangeListener, ElementUpdateListener
|
||||
{
|
||||
public DOMTest ()
|
||||
{
|
||||
super(DOMTest.class.getName());
|
||||
}
|
||||
|
||||
public void attributeChanged (AttributeChangedEvent event)
|
||||
{
|
||||
assertTrue(fields[_fcount] + " == " + values[_fcount],
|
||||
event.getName().equals(fields[_fcount]) &&
|
||||
event.getValue().equals(values[_fcount]));
|
||||
|
||||
// shutdown once we receive our last update
|
||||
if (++_fcount == fields.length) {
|
||||
_omgr.harshShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void elementUpdated (ElementUpdatedEvent event)
|
||||
{
|
||||
// Log.info("Element updated " + event);
|
||||
// Log.info(StringUtil.toString(_test.ints));
|
||||
// Log.info(StringUtil.toString(_test.strings));
|
||||
}
|
||||
|
||||
public void runTest ()
|
||||
@Test public void runTest ()
|
||||
{
|
||||
// request that a new TestObject be registered
|
||||
_test = _omgr.registerObject(new TestObject());
|
||||
@@ -87,27 +73,35 @@ public class DOMTest extends TestCase
|
||||
_omgr.run();
|
||||
}
|
||||
|
||||
public static Test suite ()
|
||||
// from interface AttributeChangeListener
|
||||
public void attributeChanged (AttributeChangedEvent event)
|
||||
{
|
||||
return new DOMTest();
|
||||
assertTrue(fields[_fcount] + " == " + values[_fcount],
|
||||
event.getName().equals(fields[_fcount]) &&
|
||||
event.getValue().equals(values[_fcount]));
|
||||
|
||||
// shutdown once we receive our last update
|
||||
if (++_fcount == fields.length) {
|
||||
_omgr.harshShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
// from interface ElementUpdateListener
|
||||
public void elementUpdated (ElementUpdatedEvent event)
|
||||
{
|
||||
DOMTest test = new DOMTest();
|
||||
test.runTest();
|
||||
// Log.info("Element updated " + event);
|
||||
// Log.info(StringUtil.toString(_test.ints));
|
||||
// Log.info(StringUtil.toString(_test.strings));
|
||||
}
|
||||
|
||||
protected int _fcount = 0;
|
||||
protected TestObject _test;
|
||||
|
||||
// the fields that will change in attribute changed events
|
||||
protected Object[] fields = {
|
||||
TestObject.FOO, TestObject.BAR, TestObject.FOO, TestObject.BAR };
|
||||
protected Object[] fields = { TestObject.FOO, TestObject.BAR, TestObject.FOO, TestObject.BAR };
|
||||
|
||||
// the values we'll receive via attribute changed events
|
||||
protected Object[] values = {
|
||||
new Integer(99), "hoopie", new Integer(25), "howdy" };
|
||||
protected Object[] values = { new Integer(99), "hoopie", new Integer(25), "howdy" };
|
||||
|
||||
protected static PresentsDObjectMgr _omgr = new PresentsDObjectMgr();
|
||||
@Inject @Unit protected PresentsDObjectMgr _omgr;
|
||||
}
|
||||
|
||||
@@ -21,60 +21,37 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import atunit.AtUnit;
|
||||
import atunit.Container;
|
||||
import atunit.Unit;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.threerings.presents.data.TestObject;
|
||||
import com.threerings.presents.dobj.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Tests that the dobjmgr will not allow a destroyed object to be added to
|
||||
* an oid list.
|
||||
*/
|
||||
public class DestroyedRefTest extends TestCase
|
||||
implements EventListener
|
||||
@RunWith(AtUnit.class)
|
||||
@Container(Container.Option.GUICE)
|
||||
public class DestroyedRefTest
|
||||
{
|
||||
public static Test suite ()
|
||||
{
|
||||
return new DestroyedRefTest();
|
||||
}
|
||||
|
||||
public DestroyedRefTest ()
|
||||
{
|
||||
super(DestroyedRefTest.class.getName());
|
||||
}
|
||||
|
||||
public void eventReceived (DEvent event)
|
||||
{
|
||||
int toid = event.getTargetOid();
|
||||
|
||||
// when we get the attribute change, we can exit
|
||||
if (event instanceof ObjectDestroyedEvent) {
|
||||
log.info("The upcoming object added event should be rejected.");
|
||||
|
||||
} else if (event instanceof ObjectAddedEvent &&
|
||||
toid == _objtwo.getOid()) {
|
||||
assertTrue("list should contain only one oid",
|
||||
_objtwo.list.size() == 1);
|
||||
|
||||
} else if (event instanceof AttributeChangedEvent) {
|
||||
// go bye bye
|
||||
_omgr.harshShutdown();
|
||||
|
||||
} else {
|
||||
fail("Got unexpected event: " + event);
|
||||
}
|
||||
}
|
||||
|
||||
public void runTest ()
|
||||
@Test public void runTest ()
|
||||
{
|
||||
// create two test objects
|
||||
_objone = _omgr.registerObject(new TestObject());
|
||||
_objone.addListener(this);
|
||||
_objone.addListener(_listener);
|
||||
_objtwo = _omgr.registerObject(new TestObject());
|
||||
_objtwo.addListener(this);
|
||||
_objtwo.addListener(_listener);
|
||||
|
||||
// add object one to object two twice in a row to make sure repeated
|
||||
// adds don't result in the object being listed twice
|
||||
@@ -95,7 +72,30 @@ public class DestroyedRefTest extends TestCase
|
||||
_omgr.run();
|
||||
}
|
||||
|
||||
protected EventListener _listener = new EventListener() {
|
||||
public void eventReceived (DEvent event) {
|
||||
int toid = event.getTargetOid();
|
||||
|
||||
// when we get the attribute change, we can exit
|
||||
if (event instanceof ObjectDestroyedEvent) {
|
||||
log.info("The upcoming object added event should be rejected.");
|
||||
|
||||
} else if (event instanceof ObjectAddedEvent &&
|
||||
toid == _objtwo.getOid()) {
|
||||
assertTrue("list should contain only one oid",
|
||||
_objtwo.list.size() == 1);
|
||||
|
||||
} else if (event instanceof AttributeChangedEvent) {
|
||||
// go bye bye
|
||||
_omgr.harshShutdown();
|
||||
|
||||
} else {
|
||||
fail("Got unexpected event: " + event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected TestObject _objone, _objtwo;
|
||||
|
||||
protected static PresentsDObjectMgr _omgr = new PresentsDObjectMgr();
|
||||
@Inject @Unit protected PresentsDObjectMgr _omgr;
|
||||
}
|
||||
|
||||
@@ -21,65 +21,34 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import atunit.AtUnit;
|
||||
import atunit.Container;
|
||||
import atunit.Unit;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.threerings.presents.data.TestObject;
|
||||
import com.threerings.presents.dobj.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests the oid list reference tracking code.
|
||||
*/
|
||||
public class RefTest extends TestCase
|
||||
implements EventListener
|
||||
@RunWith(AtUnit.class)
|
||||
@Container(Container.Option.GUICE)
|
||||
public class RefTest
|
||||
{
|
||||
public static Test suite ()
|
||||
{
|
||||
return new RefTest();
|
||||
}
|
||||
|
||||
public RefTest ()
|
||||
{
|
||||
super(RefTest.class.getName());
|
||||
}
|
||||
|
||||
public void eventReceived (DEvent event)
|
||||
{
|
||||
// Log.info("Got event: " + event);
|
||||
int toid = event.getTargetOid();
|
||||
|
||||
// once we receive the second object added we can destroy the
|
||||
// target object to see if the reference is cleaned up
|
||||
if (event instanceof ObjectAddedEvent &&
|
||||
toid == _objtwo.getOid()) {
|
||||
// Log.info("Destroying object two " + _objtwo + ".");
|
||||
_objtwo.destroy();
|
||||
|
||||
} else if (event instanceof ObjectDestroyedEvent) {
|
||||
if (toid == _objtwo.getOid()) {
|
||||
// Log.info("List won't yet be empty: " + _objone.list);
|
||||
assertTrue("List not empty", _objone.list.size() > 0);
|
||||
} else {
|
||||
// Log.info("Other object destroyed.");
|
||||
// go bye bye
|
||||
_omgr.harshShutdown();
|
||||
}
|
||||
|
||||
} else if (event instanceof ObjectRemovedEvent) {
|
||||
// Log.info("List should be empty: " + _objone.list);
|
||||
assertTrue("List empty", _objone.list.size() == 0);
|
||||
// finally destroy the other object to complete the circle
|
||||
_objone.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public void runTest ()
|
||||
@Test public void runTest ()
|
||||
{
|
||||
// create two test objects
|
||||
_objone = _omgr.registerObject(new TestObject());
|
||||
_objone.addListener(this);
|
||||
_objone.addListener(_listener);
|
||||
_objtwo = _omgr.registerObject(new TestObject());
|
||||
_objtwo.addListener(this);
|
||||
_objtwo.addListener(_listener);
|
||||
|
||||
// now that we have both objects, set up the references
|
||||
_objone.addToList(_objtwo.getOid());
|
||||
@@ -89,8 +58,39 @@ public class RefTest extends TestCase
|
||||
_omgr.run();
|
||||
}
|
||||
|
||||
protected EventListener _listener = new EventListener() {
|
||||
public void eventReceived (DEvent event) {
|
||||
// Log.info("Got event: " + event);
|
||||
int toid = event.getTargetOid();
|
||||
|
||||
// once we receive the second object added we can destroy the
|
||||
// target object to see if the reference is cleaned up
|
||||
if (event instanceof ObjectAddedEvent &&
|
||||
toid == _objtwo.getOid()) {
|
||||
// Log.info("Destroying object two " + _objtwo + ".");
|
||||
_objtwo.destroy();
|
||||
|
||||
} else if (event instanceof ObjectDestroyedEvent) {
|
||||
if (toid == _objtwo.getOid()) {
|
||||
// Log.info("List won't yet be empty: " + _objone.list);
|
||||
assertTrue("List not empty", _objone.list.size() > 0);
|
||||
} else {
|
||||
// Log.info("Other object destroyed.");
|
||||
// go bye bye
|
||||
_omgr.harshShutdown();
|
||||
}
|
||||
|
||||
} else if (event instanceof ObjectRemovedEvent) {
|
||||
// Log.info("List should be empty: " + _objone.list);
|
||||
assertTrue("List empty", _objone.list.size() == 0);
|
||||
// finally destroy the other object to complete the circle
|
||||
_objone.destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected TestObject _objone;
|
||||
protected TestObject _objtwo;
|
||||
|
||||
protected static PresentsDObjectMgr _omgr = new PresentsDObjectMgr();
|
||||
@Inject @Unit protected PresentsDObjectMgr _omgr;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
package com.threerings.presents.server;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.threerings.presents.data.TestObject;
|
||||
import com.threerings.presents.dobj.*;
|
||||
|
||||
@@ -30,13 +33,13 @@ public class TestServer extends PresentsServer
|
||||
{
|
||||
public static TestObject testobj;
|
||||
|
||||
public void init ()
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
super.init();
|
||||
super.init(injector);
|
||||
|
||||
// register our test provider
|
||||
invmgr.registerDispatcher(new TestDispatcher(new TestManager()), "test");
|
||||
_invmgr.registerDispatcher(new TestDispatcher(new TestManager()), "test");
|
||||
|
||||
// create a test object
|
||||
testobj = omgr.registerObject(new TestObject());
|
||||
@@ -48,9 +51,10 @@ public class TestServer extends PresentsServer
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
TestServer server = new TestServer();
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
TestServer server = injector.getInstance(TestServer.class);
|
||||
try {
|
||||
server.init();
|
||||
server.init(injector);
|
||||
server.run();
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to initialize server.", e);
|
||||
|
||||
Reference in New Issue
Block a user