Bureaucracy review updates and hello world test
* Fleshed out client-side Agent class, made abstract * Delegate the creation of the bureau director to subclasses of BureauClient * Moved BureauDirector's BureauReceiver and Subscriber implementations to anonymous classes that delegate to protected methods. * Use SafeSubscriber * Added new service method for notiyfing the server when the creation of an agent fails so that the registry is not perpetually waiting * Made BureauCredentials more conformant * Made BureauRegistry respond properly to client logging off * Got rid of superfluous uses of safeToString * Changed Launcher to CommandGenerator and now use ProcessBuilder and copyMergedOutput * Changed bureau process parameters to server name and port instead of URL git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5030 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -26,10 +26,28 @@ import com.threerings.bureau.data.AgentObject;
|
||||
/**
|
||||
* Represents an agent running within a bureau client.
|
||||
*/
|
||||
public class Agent
|
||||
public abstract class Agent
|
||||
{
|
||||
/**
|
||||
* Initializes the Agent with the distributed agent object.
|
||||
*/
|
||||
public void init (AgentObject agentObj)
|
||||
{
|
||||
_agentObj = agentObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the code running in the agent.
|
||||
*/
|
||||
public abstract void start ();
|
||||
|
||||
/**
|
||||
* Stops the code running in the agent.
|
||||
*/
|
||||
public abstract void stop ();
|
||||
|
||||
/**
|
||||
* The shared agent object.
|
||||
*/
|
||||
public AgentObject agentObject;
|
||||
protected AgentObject _agentObj;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.samskivert.util.Config;
|
||||
/**
|
||||
* Represents a client embedded in a bureau.
|
||||
*/
|
||||
public class BureauClient extends Client
|
||||
public abstract class BureauClient extends Client
|
||||
{
|
||||
/**
|
||||
* Creates a new client.
|
||||
@@ -45,14 +45,16 @@ public class BureauClient extends Client
|
||||
|
||||
_bureauId = bureauId;
|
||||
|
||||
BureauCredentials creds = new BureauCredentials();
|
||||
BureauCredentials creds = new BureauCredentials(_bureauId);
|
||||
creds.sessionToken = token;
|
||||
_creds = creds;
|
||||
|
||||
_ctx = createContext();
|
||||
_director = new BureauDirector(_ctx);
|
||||
_director = createDirector();
|
||||
}
|
||||
|
||||
protected abstract BureauDirector createDirector ();
|
||||
|
||||
protected BureauContext createContext ()
|
||||
{
|
||||
return new BureauContext () {
|
||||
|
||||
@@ -3,81 +3,31 @@ package com.threerings.bureau.client;
|
||||
import com.threerings.presents.client.BasicDirector;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.bureau.data.BureauCodes;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import com.threerings.bureau.Log;
|
||||
import com.threerings.bureau.util.BureauContext;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
import com.threerings.presents.util.SafeSubscriber;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
|
||||
/**
|
||||
* Allows the server to create and destroy agents on a client.
|
||||
* @see BureauRegistry
|
||||
*/
|
||||
public class BureauDirector extends BasicDirector
|
||||
implements BureauReceiver, Subscriber<AgentObject>
|
||||
public abstract class BureauDirector extends BasicDirector
|
||||
{
|
||||
/**
|
||||
* Creates a new BureauDirector.
|
||||
*/
|
||||
public BureauDirector (BureauContext ctx)
|
||||
{
|
||||
super(ctx);
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
// from BureauReceiver
|
||||
public synchronized void createAgent (int agentId)
|
||||
{
|
||||
_ctx.getDObjectManager().subscribeToObject(agentId, this);
|
||||
}
|
||||
|
||||
// from BureauReceiver
|
||||
public synchronized void destroyAgent (int agentId)
|
||||
{
|
||||
Agent agent = null;
|
||||
try {
|
||||
agent = _agents.remove(agentId);
|
||||
// TODO: stop the agent somehow
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Log.warning("Could not create agent [id=" + agentId + "]");
|
||||
Log.logStackTrace(t);
|
||||
// TODO: failure notification?
|
||||
return;
|
||||
}
|
||||
|
||||
_ctx.getDObjectManager().unsubscribeFromObject(agentId, this);
|
||||
doStopAgent(agent);
|
||||
_bureauService.agentDestroyed(_ctx.getClient(), agentId);
|
||||
}
|
||||
|
||||
// from Subscriber
|
||||
public synchronized void objectAvailable (AgentObject agentObject)
|
||||
{
|
||||
int oid = agentObject.getOid();
|
||||
try {
|
||||
Agent agent = new Agent();
|
||||
agent.agentObject = agentObject;
|
||||
doStartAgent(agent);
|
||||
_agents.put(oid, agent);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.warning("Could not create agent [obj=" + agentObject + "]");
|
||||
Log.logStackTrace(e);
|
||||
// TODO: failure notification?
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: post to runqueue?
|
||||
_bureauService.agentCreated(_ctx.getClient(), oid);
|
||||
}
|
||||
|
||||
// from Subscriber
|
||||
public synchronized void requestFailed (int oid, ObjectAccessException cause)
|
||||
{
|
||||
Log.warning("Could not subscribe to agent [oid=" + oid + "]");
|
||||
Log.logStackTrace(cause);
|
||||
}
|
||||
|
||||
@Override // from BasicDirector
|
||||
public void clientDidLogon (Client client)
|
||||
{
|
||||
@@ -85,6 +35,79 @@ public class BureauDirector extends BasicDirector
|
||||
_bureauService.bureauInitialized(_ctx.getClient(), _ctx.getBureauId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new agent when the server requests it.
|
||||
*/
|
||||
protected synchronized void createAgent (int agentId)
|
||||
{
|
||||
Subscriber<AgentObject> delegator = new Subscriber<AgentObject>() {
|
||||
public void objectAvailable (AgentObject agentObject) {
|
||||
BureauDirector.this.objectAvailable(agentObject);
|
||||
}
|
||||
public void requestFailed (int oid, ObjectAccessException cause) {
|
||||
BureauDirector.this.requestFailed(oid, cause);
|
||||
}
|
||||
};
|
||||
|
||||
_subscriber = new SafeSubscriber<AgentObject>(agentId, delegator);
|
||||
_subscriber.subscribe(_ctx.getDObjectManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys an agent at the server's request.
|
||||
*/
|
||||
protected synchronized void destroyAgent (int agentId)
|
||||
{
|
||||
Agent agent = null;
|
||||
agent = _agents.remove(agentId);
|
||||
|
||||
if (agent == null) {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
agent.stop();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Log.warning("Stopping an agent caused an exception");
|
||||
Log.logStackTrace(t);
|
||||
}
|
||||
_subscriber.unsubscribe(_ctx.getDObjectManager());
|
||||
_bureauService.agentDestroyed(_ctx.getClient(), agentId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the a request to subscribe to an object finishes and the object is available.
|
||||
*/
|
||||
protected synchronized void objectAvailable (AgentObject agentObject)
|
||||
{
|
||||
int oid = agentObject.getOid();
|
||||
Agent agent;
|
||||
try {
|
||||
agent = createAgent(agentObject);
|
||||
agent.init(agentObject);
|
||||
agent.start();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Log.warning("Could not create agent [obj=" + agentObject + "]");
|
||||
Log.logStackTrace(t);
|
||||
_bureauService.agentCreationFailed(_ctx.getClient(), oid);
|
||||
return;
|
||||
}
|
||||
|
||||
_agents.put(oid, agent);
|
||||
_bureauService.agentCreated(_ctx.getClient(), oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the a request to subscribe to an object fails.
|
||||
*/
|
||||
protected synchronized void requestFailed (int oid, ObjectAccessException cause)
|
||||
{
|
||||
Log.warning("Could not subscribe to agent [oid=" + oid + "]");
|
||||
Log.logStackTrace(cause);
|
||||
}
|
||||
|
||||
@Override // from BasicDirector
|
||||
protected void registerServices (Client client)
|
||||
{
|
||||
@@ -95,8 +118,17 @@ public class BureauDirector extends BasicDirector
|
||||
|
||||
// Set up our decoder so we can receive method calls
|
||||
// from the server
|
||||
BureauReceiver receiver = new BureauReceiver () {
|
||||
public void createAgent (int agentId) {
|
||||
BureauDirector.this.createAgent(agentId);
|
||||
}
|
||||
public void destroyAgent (int agentId) {
|
||||
BureauDirector.this.destroyAgent(agentId);
|
||||
}
|
||||
};
|
||||
|
||||
client.getInvocationDirector().
|
||||
registerReceiver(new BureauDecoder(this));
|
||||
registerReceiver(new BureauDecoder(receiver));
|
||||
}
|
||||
|
||||
@Override // from BasicDirector
|
||||
@@ -108,21 +140,16 @@ public class BureauDirector extends BasicDirector
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the agent object is ready and it is time to run his code.
|
||||
* Called when it is time to create an Agent. Subclasses should read the
|
||||
* <code>agentObject</code>'s type and/or properties to determine what kind of Agent to
|
||||
* create.
|
||||
* @param agentObj the distributed and object
|
||||
* @return a new Agent that will govern the distributed object
|
||||
*/
|
||||
protected void doStartAgent (Agent agent)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the agent object is being destroyed and the client code should stop
|
||||
* processing.
|
||||
*/
|
||||
protected void doStopAgent (Agent agent)
|
||||
{
|
||||
}
|
||||
protected abstract Agent createAgent (AgentObject agentObj);
|
||||
|
||||
protected BureauContext _ctx;
|
||||
protected BureauService _bureauService;
|
||||
protected HashIntMap<Agent> _agents = new HashIntMap<Agent>();
|
||||
protected IntMap<Agent> _agents = IntMaps.newHashIntMap();
|
||||
protected SafeSubscriber<AgentObject> _subscriber;
|
||||
}
|
||||
|
||||
@@ -37,11 +37,17 @@ public interface BureauService extends InvocationService
|
||||
void bureauInitialized (Client client, String bureauId);
|
||||
|
||||
/**
|
||||
* Notify the server that a previosuly requested agent is not created and ready to use.
|
||||
* Notify the server that a previosuly requested agent is now created and ready to use.
|
||||
* @see BureauReceiver#createAgent
|
||||
*/
|
||||
void agentCreated (Client client, int agentId);
|
||||
|
||||
/**
|
||||
* Notify the server that a previosuly requested agent could not be created.
|
||||
* @see BureauReceiver#createAgent
|
||||
*/
|
||||
void agentCreationFailed (Client client, int agentId);
|
||||
|
||||
/**
|
||||
* Notify the server that an agent is no longer running. Normally called in response
|
||||
* to a call to <code>destroyAgent</code>
|
||||
|
||||
@@ -24,15 +24,35 @@ package com.threerings.bureau.data;
|
||||
import com.threerings.presents.net.Credentials;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Extends the basic credentials to provide bureau-specific fields.
|
||||
*/
|
||||
public class BureauCredentials extends Credentials
|
||||
{
|
||||
/**
|
||||
* The token to pass to the server when logging in. This is usually just passed to the bureau
|
||||
* on the command line to guard against outside connections being established. */
|
||||
* on the command line to guard against outside connections being established.
|
||||
*/
|
||||
public String sessionToken;
|
||||
|
||||
/**
|
||||
* Creates an empty credentials for streaming. Should not be used directly.
|
||||
*/
|
||||
public BureauCredentials ()
|
||||
{
|
||||
super(new Name("$$$BUREAU$$$"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new credentials for a specific bureau.
|
||||
*/
|
||||
public BureauCredentials (String bureauId)
|
||||
{
|
||||
super(new Name("@@bureau:" + bureauId + "@@"));
|
||||
}
|
||||
|
||||
// inherit documentation - from Object
|
||||
public String toString ()
|
||||
{
|
||||
return super.toString() + ", token=" + sessionToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,19 @@ public class BureauMarshaller extends InvocationMarshaller
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #agentCreationFailed} requests. */
|
||||
public static final int AGENT_CREATION_FAILED = 2;
|
||||
|
||||
// from interface BureauService
|
||||
public void agentCreationFailed (Client arg1, int arg2)
|
||||
{
|
||||
sendRequest(arg1, AGENT_CREATION_FAILED, new Object[] {
|
||||
Integer.valueOf(arg2)
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #agentDestroyed} requests. */
|
||||
public static final int AGENT_DESTROYED = 2;
|
||||
public static final int AGENT_DESTROYED = 3;
|
||||
|
||||
// from interface BureauService
|
||||
public void agentDestroyed (Client arg1, int arg2)
|
||||
@@ -59,7 +70,7 @@ public class BureauMarshaller extends InvocationMarshaller
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #bureauInitialized} requests. */
|
||||
public static final int BUREAU_INITIALIZED = 3;
|
||||
public static final int BUREAU_INITIALIZED = 4;
|
||||
|
||||
// from interface BureauService
|
||||
public void bureauInitialized (Client arg1, String arg2)
|
||||
|
||||
@@ -63,6 +63,13 @@ public class BureauDispatcher extends InvocationDispatcher
|
||||
);
|
||||
return;
|
||||
|
||||
case BureauMarshaller.AGENT_CREATION_FAILED:
|
||||
((BureauProvider)provider).agentCreationFailed(
|
||||
source,
|
||||
((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
case BureauMarshaller.AGENT_DESTROYED:
|
||||
((BureauProvider)provider).agentDestroyed(
|
||||
source,
|
||||
|
||||
@@ -37,6 +37,11 @@ public interface BureauProvider extends InvocationProvider
|
||||
*/
|
||||
public void agentCreated (ClientObject caller, int arg1);
|
||||
|
||||
/**
|
||||
* Handles a {@link BureauService#agentCreationFailed} request.
|
||||
*/
|
||||
public void agentCreationFailed (ClientObject caller, int arg1);
|
||||
|
||||
/**
|
||||
* Handles a {@link BureauService#agentDestroyed} request.
|
||||
*/
|
||||
|
||||
@@ -30,8 +30,11 @@ import com.threerings.bureau.data.AgentObject;
|
||||
import com.threerings.bureau.data.BureauCodes;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.dobj.ObjectDeathListener;
|
||||
import com.threerings.presents.dobj.ObjectDestroyedEvent;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
import static com.samskivert.util.StringUtil.safeToString;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.ProcessLogger;
|
||||
|
||||
import com.threerings.bureau.Log;
|
||||
|
||||
@@ -42,31 +45,37 @@ import com.threerings.bureau.Log;
|
||||
public class BureauRegistry
|
||||
{
|
||||
/**
|
||||
* Defines a way to launch a bureau. Launchers are set by the server on startup and are
|
||||
* invoked whenever an agent is requested whose bureauType matches the registered type.
|
||||
* @see registerBureau
|
||||
* Defines the commands that are responsible for invoking a bureau. Instances are associated to
|
||||
* bureau types by the server on startup. The instances are used whenever the registry needs to
|
||||
* launch a bureau for an agent with the assocated bureau type.
|
||||
* @see setCommandGenerator
|
||||
*/
|
||||
public static interface Launcher
|
||||
public static interface CommandGenerator
|
||||
{
|
||||
/**
|
||||
* Launches a new bureau using the given server connect-back url and other information.
|
||||
* Called by the registry when it decides a new bureau is needed.
|
||||
* @param serverUrl the url the bureau should use to connect back to the server
|
||||
* @param serverNameAndPort the name and port the bureau should use to connect back to the
|
||||
* server, e.g. server.com:47624
|
||||
* @param bureauId the id of the bureau being launched
|
||||
* @param token the token string to use for the credentials when logging in
|
||||
* @return the builder, ready to launch
|
||||
*/
|
||||
Process launch (String serverUrl, String bureauId, String token);
|
||||
String[] createCommand (
|
||||
String serverNameAndPort,
|
||||
String bureauId,
|
||||
String token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new registry, prepared to provide bureau services.
|
||||
*/
|
||||
public BureauRegistry (
|
||||
String serverUrl,
|
||||
String serverNameAndPort,
|
||||
InvocationManager invmgr,
|
||||
RootDObjectManager omgr)
|
||||
{
|
||||
_serverUrl = serverUrl;
|
||||
_serverNameAndPort = serverNameAndPort;
|
||||
_invmgr = invmgr;
|
||||
_omgr = omgr;
|
||||
|
||||
@@ -74,9 +83,12 @@ public class BureauRegistry
|
||||
public void bureauInitialized (ClientObject client, String bureauId) {
|
||||
BureauRegistry.this.bureauInitialized(client, bureauId);
|
||||
}
|
||||
public void agentCreated (ClientObject client, int agentId) {
|
||||
public void agentCreated (ClientObject client, int agentId) {
|
||||
BureauRegistry.this.agentCreated(client, agentId);
|
||||
}
|
||||
public void agentCreationFailed (ClientObject client, int agentId) {
|
||||
BureauRegistry.this.agentCreationFailed(client, agentId);
|
||||
}
|
||||
public void agentDestroyed (ClientObject client, int agentId) {
|
||||
BureauRegistry.this.agentDestroyed(client, agentId);
|
||||
}
|
||||
@@ -88,20 +100,21 @@ public class BureauRegistry
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a launcher of a given type. When an agent is started and no bureaus are currently
|
||||
* running, the <code>bureauType</code> is used to determine the <code>Launcher</code> to call.
|
||||
* Registers a command generator for a given type. When an agent is started and no bureaus are
|
||||
* running, the <code>bureauType</code> is used to determine the <code>CommandGenerator</code>
|
||||
* instance to call.
|
||||
* @param bureauType the type of bureau that will be launched
|
||||
* @param launcher the launcher to be used when the <code>bureauType</code> is requested
|
||||
* @param cmdGenerator the generator to be used for bureaus of <code>bureauType</code>
|
||||
*/
|
||||
public void setLauncher (String bureauType, Launcher launcher)
|
||||
public void setCommandGenerator (String bureauType, CommandGenerator cmdGenerator)
|
||||
{
|
||||
if (_launchers.get(bureauType) != null) {
|
||||
Log.warning("Launcher for type already exists [type=" +
|
||||
if (_generators.get(bureauType) != null) {
|
||||
Log.warning("Generator for type already exists [type=" +
|
||||
bureauType + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
_launchers.put(bureauType, launcher);
|
||||
_generators.put(bureauType, cmdGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +126,7 @@ public class BureauRegistry
|
||||
if (bureau != null && bureau.ready()) {
|
||||
|
||||
Log.info("Bureau ready, sending createAgent " +
|
||||
safeToString(agent));
|
||||
StringUtil.toString(agent));
|
||||
|
||||
BureauSender.createAgent(bureau.clientObj, agent.getOid());
|
||||
// !TODO: is this the right place to register the object?
|
||||
@@ -127,29 +140,38 @@ public class BureauRegistry
|
||||
|
||||
if (bureau == null) {
|
||||
|
||||
Launcher launcher = _launchers.get(agent.bureauType);
|
||||
if (launcher == null) {
|
||||
Log.warning("Launcher not found for agent's " +
|
||||
"bureau type " + safeToString(agent));
|
||||
CommandGenerator generator = _generators.get(agent.bureauType);
|
||||
if (generator == null) {
|
||||
Log.warning("CommandGenerator not found for agent's " +
|
||||
"bureau type " + StringUtil.toString(agent));
|
||||
return;
|
||||
}
|
||||
|
||||
Log.info("Creating new bureau " +
|
||||
safeToString(agent) + " " +
|
||||
safeToString(launcher));
|
||||
StringUtil.toString(agent) + " " +
|
||||
StringUtil.toString(generator));
|
||||
|
||||
bureau = new Bureau();
|
||||
bureau.bureauId = agent.bureauId;
|
||||
|
||||
try {
|
||||
bureau.process = launcher.launch(_serverUrl, agent.bureauId, "");
|
||||
// kick off the bureau's process
|
||||
ProcessBuilder builder = new ProcessBuilder(
|
||||
generator.createCommand(
|
||||
_serverNameAndPort, agent.bureauId, ""));
|
||||
|
||||
builder.redirectErrorStream(true);
|
||||
bureau.process = builder.start();
|
||||
|
||||
// log the output of the process and prefix with bureau id
|
||||
ProcessLogger.copyMergedOutput(
|
||||
Log.log, bureau.bureauId, bureau.process);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.warning("Could not launch process for bureau " +
|
||||
safeToString(agent));
|
||||
StringUtil.toString(agent));
|
||||
Log.logStackTrace(e);
|
||||
|
||||
// !TODO: how to hook into caller and keep the other clients from hanging
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -173,7 +195,7 @@ public class BureauRegistry
|
||||
return;
|
||||
}
|
||||
|
||||
Log.warning("Destroying agent " + safeToString(agent));
|
||||
Log.warning("Destroying agent " + StringUtil.toString(agent));
|
||||
|
||||
// transition the agent to a new state and perform the effect of the transition
|
||||
switch (found.state) {
|
||||
@@ -197,7 +219,7 @@ public class BureauRegistry
|
||||
case Bureau.STILL_BORN:
|
||||
Log.warning("Acknowledging a request to destory an agent, but agent " +
|
||||
"is in state " + found.state + ", ignoring request " +
|
||||
safeToString(found.agent));
|
||||
StringUtil.toString(found.agent));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -210,16 +232,22 @@ public class BureauRegistry
|
||||
*/
|
||||
protected synchronized void bureauInitialized (ClientObject client, String bureauId)
|
||||
{
|
||||
Bureau bureau = _bureaus.get(bureauId);
|
||||
final Bureau bureau = _bureaus.get(bureauId);
|
||||
if (bureau == null) {
|
||||
Log.warning("Acknowledging initialization of non-existent bureau " +
|
||||
safeToString(bureauId));
|
||||
StringUtil.toString(bureauId));
|
||||
return;
|
||||
}
|
||||
|
||||
bureau.clientObj = client;
|
||||
|
||||
Log.info("Bureau created " + safeToString(bureau) +
|
||||
bureau.clientObj.addListener(new ObjectDeathListener() {
|
||||
public void objectDestroyed (ObjectDestroyedEvent e) {
|
||||
BureauRegistry.this.clientDestroyed(bureau);
|
||||
}
|
||||
});
|
||||
|
||||
Log.info("Bureau created " + StringUtil.toString(bureau) +
|
||||
", launching pending agents");
|
||||
|
||||
// find all pending agents
|
||||
@@ -235,7 +263,7 @@ public class BureauRegistry
|
||||
|
||||
// create them
|
||||
for (AgentObject agent : pending) {
|
||||
Log.info("Creating agent " + safeToString(agent));
|
||||
Log.info("Creating agent " + StringUtil.toString(agent));
|
||||
BureauSender.createAgent(bureau.clientObj, agent.getOid());
|
||||
bureau.agentStates.put(agent, Bureau.STARTED);
|
||||
}
|
||||
@@ -268,7 +296,38 @@ public class BureauRegistry
|
||||
case Bureau.DESTROYED:
|
||||
Log.warning("Received acknowledgement of the creation of an " +
|
||||
"agent in state " + found.state + ", ignoring request " +
|
||||
safeToString(found.agent));
|
||||
StringUtil.toString(found.agent));
|
||||
break;
|
||||
}
|
||||
|
||||
found.bureau.summarize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the bureau client acknowledges the creation of an agent.
|
||||
*/
|
||||
protected synchronized void agentCreationFailed (ClientObject client, int agentId)
|
||||
{
|
||||
FoundAgent found = resolve(client, agentId, "agentCreationFailed");
|
||||
if (found == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (found.state) {
|
||||
case Bureau.STARTED:
|
||||
found.bureau.agentStates.remove(found.agent);
|
||||
break;
|
||||
|
||||
case Bureau.STILL_BORN:
|
||||
found.bureau.agentStates.remove(found.agent);
|
||||
break;
|
||||
|
||||
case Bureau.PENDING:
|
||||
case Bureau.RUNNING:
|
||||
case Bureau.DESTROYED:
|
||||
Log.warning("Received acknowledgement of creation failure for " +
|
||||
"agent in state " + found.state + ", ignoring request " +
|
||||
StringUtil.toString(found.agent));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -296,13 +355,22 @@ public class BureauRegistry
|
||||
case Bureau.STILL_BORN:
|
||||
Log.warning("Acknowledging agent destruction, but state is " +
|
||||
found.state + ", ignoring request " +
|
||||
safeToString(found.agent));
|
||||
StringUtil.toString(found.agent));
|
||||
break;
|
||||
}
|
||||
|
||||
found.bureau.summarize();
|
||||
}
|
||||
|
||||
// TODO: schedule a shutdown event for the bureau if this is the last agent
|
||||
/**
|
||||
* Callback for when a client is destroyed.
|
||||
*/
|
||||
protected synchronized void clientDestroyed (Bureau bureau)
|
||||
{
|
||||
// clean up any agents attached to this bureau
|
||||
for (AgentObject agent : bureau.agentStates.keySet()) {
|
||||
_omgr.destroyObject(agent.getOid());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,7 +387,7 @@ public class BureauRegistry
|
||||
|
||||
if (!(dobj instanceof AgentObject)) {
|
||||
Log.warning("Object not an agent in " + resolver +
|
||||
" " + safeToString(dobj));
|
||||
" " + StringUtil.toString(dobj));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -327,27 +395,27 @@ public class BureauRegistry
|
||||
Bureau bureau = _bureaus.get(agent.bureauId);
|
||||
if (bureau == null) {
|
||||
Log.warning("Bureau not found for agent in " + resolver +
|
||||
" " + safeToString(agent));
|
||||
" " + StringUtil.toString(agent));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!bureau.agentStates.containsKey(agent)) {
|
||||
Log.warning("Bureau does not have agent in " + resolver +
|
||||
" " + safeToString(agent));
|
||||
" " + StringUtil.toString(agent));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bureau.clientObj == null) {
|
||||
Log.warning("Bureau not yet connected in " + resolver +
|
||||
" " + safeToString(agent));
|
||||
" " + StringUtil.toString(agent));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (client != null && bureau.clientObj != client) {
|
||||
Log.warning("Masquerading request in " + resolver +
|
||||
" " + safeToString(agent) +
|
||||
" " + safeToString(bureau.clientObj) +
|
||||
" " + safeToString(client));
|
||||
" " + StringUtil.toString(agent) +
|
||||
" " + StringUtil.toString(bureau.clientObj) +
|
||||
" " + StringUtil.toString(client));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -447,9 +515,16 @@ public class BureauRegistry
|
||||
}
|
||||
}
|
||||
|
||||
protected String _serverUrl;
|
||||
// More readable generic map creation
|
||||
// TODO: add to library or use existing
|
||||
protected static <K, V> HashMap<K, V> hashMap ()
|
||||
{
|
||||
return new HashMap<K, V>();
|
||||
}
|
||||
|
||||
protected String _serverNameAndPort;
|
||||
protected InvocationManager _invmgr;
|
||||
protected RootDObjectManager _omgr;
|
||||
protected Map<String, Launcher> _launchers = new HashMap<String, Launcher>();
|
||||
protected Map<String, Bureau> _bureaus = new HashMap<String, Bureau>();
|
||||
protected Map<String, CommandGenerator> _generators = hashMap();
|
||||
protected Map<String, Bureau> _bureaus = hashMap();
|
||||
}
|
||||
|
||||
@@ -179,4 +179,24 @@
|
||||
</batchtest>
|
||||
</junit>
|
||||
</target>
|
||||
|
||||
<target name="bureau-runserver" depends="compile"
|
||||
description="Run the bureau test server.">
|
||||
<java fork="true" classname="com.threerings.bureau.server.TestServer">
|
||||
<classpath refid="classpath"/>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
<!-- NOTE: this target is launched by the bureau-runserver target, modelling the way bureaus -->
|
||||
<!-- work. As such it is not currently useful on its own -->
|
||||
<target name="bureau-runclient" depends="compile"
|
||||
description="Run the bureau test client.">
|
||||
<java fork="true" classname="com.threerings.bureau.client.TestClient">
|
||||
<classpath refid="classpath"/>
|
||||
<sysproperty key="serverName" value="${serverName}"/>
|
||||
<sysproperty key="serverPort" value="${serverPort}"/>
|
||||
<sysproperty key="token" value="${token}"/>
|
||||
<sysproperty key="bureauId" value="${bureauId}"/>
|
||||
</java>
|
||||
</target>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 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.bureau.client;
|
||||
|
||||
import com.samskivert.util.OneLineLogFormatter;
|
||||
import com.threerings.bureau.Log;
|
||||
import com.samskivert.util.Queue;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.bureau.client.BureauDirector;
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import com.samskivert.util.RunQueue;
|
||||
|
||||
/**
|
||||
* Extends bureau client minimally and provides a static main function to create a client and
|
||||
* connect to a server given by system properties.
|
||||
*/
|
||||
public class TestClient extends BureauClient
|
||||
{
|
||||
public static void main (String args[])
|
||||
throws java.net.MalformedURLException
|
||||
{
|
||||
// make log pretty
|
||||
OneLineLogFormatter.configureDefaultHandler();
|
||||
|
||||
// create the client and log on
|
||||
TestClient client = new TestClient(
|
||||
System.getProperty("token"),
|
||||
System.getProperty("bureauId"));
|
||||
client.setServer(
|
||||
System.getProperty("serverName"),
|
||||
new int[] {Integer.parseInt(System.getProperty("serverPort"))});
|
||||
client.logon();
|
||||
|
||||
// run it
|
||||
client.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements most basic run queue. Required to instantate a client.
|
||||
*/
|
||||
static protected class SimpleRunQueue implements RunQueue
|
||||
{
|
||||
public void postRunnable (Runnable r)
|
||||
{
|
||||
_queue.append(r);
|
||||
}
|
||||
|
||||
public boolean isDispatchThread ()
|
||||
{
|
||||
return _main == Thread.currentThread();
|
||||
}
|
||||
|
||||
public void run ()
|
||||
{
|
||||
_main = Thread.currentThread();
|
||||
|
||||
while (true) {
|
||||
Runnable r = _queue.get();
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected Thread _main;
|
||||
protected Queue<Runnable> _queue = new Queue<Runnable>();
|
||||
}
|
||||
|
||||
/**
|
||||
* The agent class used by our director. Does not actually load any code, just logs the
|
||||
* start/stop requests.
|
||||
*/
|
||||
static protected class TestAgent extends Agent
|
||||
{
|
||||
public void start ()
|
||||
{
|
||||
Log.info("Starting agent " + StringUtil.toString(_agentObj));
|
||||
}
|
||||
|
||||
public void stop ()
|
||||
{
|
||||
Log.info("Stopping agent " + StringUtil.toString(_agentObj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new test client.
|
||||
*/
|
||||
protected TestClient (String token, String bureauId)
|
||||
{
|
||||
super(token, bureauId, new SimpleRunQueue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the event loop.
|
||||
*/
|
||||
protected void run ()
|
||||
{
|
||||
((SimpleRunQueue)_runQueue).run();
|
||||
}
|
||||
|
||||
// overridden - creates a simple director
|
||||
protected BureauDirector createDirector ()
|
||||
{
|
||||
// just use our test agent exclusively - in the real world, the agent created would depend
|
||||
// on the object's type and/or properties
|
||||
return new BureauDirector(_ctx) {
|
||||
public Agent createAgent (AgentObject agentObj) {
|
||||
return new TestAgent();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 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.bureau.server;
|
||||
|
||||
import com.threerings.bureau.Log;
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import com.threerings.presents.server.PresentsServer;
|
||||
import com.samskivert.util.OneLineLogFormatter;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Extends a presents server to include a bureau registry.
|
||||
*/
|
||||
public class TestServer extends PresentsServer
|
||||
{
|
||||
/**
|
||||
* The bureau registry for the server. Will be null until <code>init</code> is called.
|
||||
*/
|
||||
public static BureauRegistry breg;
|
||||
|
||||
/**
|
||||
* Creates a new server and runs it.
|
||||
*/
|
||||
public static void main (String[] args)
|
||||
{
|
||||
// make log pretty
|
||||
OneLineLogFormatter.configureDefaultHandler();
|
||||
|
||||
final TestServer server = new TestServer();
|
||||
try {
|
||||
server.init();
|
||||
|
||||
// request a new agent
|
||||
// TODO: more tests here - create a thread that posts different kinds of requests
|
||||
// and somehow monitors results from client
|
||||
server.omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
server.createTestAgent();
|
||||
}
|
||||
});
|
||||
|
||||
server.run();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Unable to initialize server.");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
// inherit documentation - from PresentsServer
|
||||
public void init ()
|
||||
throws Exception
|
||||
{
|
||||
super.init();
|
||||
breg = new BureauRegistry("localhost:47624", invmgr, omgr);
|
||||
|
||||
breg.setCommandGenerator("test", new BureauRegistry.CommandGenerator() {
|
||||
public String[] createCommand (
|
||||
String serverNameAndPort,
|
||||
String bureauId,
|
||||
String token) {
|
||||
|
||||
int colon = serverNameAndPort.indexOf(':');
|
||||
String [] cmd = {"ant",
|
||||
"-DserverName=" + serverNameAndPort.substring(0, colon),
|
||||
"-DserverPort=" + serverNameAndPort.substring(colon + 1),
|
||||
"-DbureauId=" + bureauId,
|
||||
"-Dtoken=" + token,
|
||||
"bureau-runclient"};
|
||||
|
||||
return cmd;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the bureau client create an agent.
|
||||
*/
|
||||
protected void createTestAgent ()
|
||||
{
|
||||
AgentObject obj = new AgentObject();
|
||||
obj.bureauType = "test";
|
||||
obj.bureauId = "test";
|
||||
breg.startAgent(obj);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user