Convert Narya (most of the way) over to a Maven Ant task based build. The
ActionScript bits remain belligerent, but the Java stuff is mostly shipshape. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6222 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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;
|
||||
|
||||
import com.samskivert.util.Logger;
|
||||
|
||||
/**
|
||||
* Contains a reference to the log object used by the Bureau services.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** We dispatch our log messages through this logger. */
|
||||
public static Logger log = Logger.getLogger("com.threerings.bureau");
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.threerings.bureau.data.AgentObject;
|
||||
|
||||
/**
|
||||
* Represents an agent running within a bureau client.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
protected AgentObject _agentObj;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.Config;
|
||||
import com.samskivert.util.RunQueue;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
|
||||
import com.threerings.bureau.data.BureauCredentials;
|
||||
import com.threerings.bureau.util.BureauContext;
|
||||
|
||||
/**
|
||||
* Represents a client embedded in a bureau.
|
||||
*/
|
||||
public abstract class BureauClient extends Client
|
||||
{
|
||||
/**
|
||||
* Creates a new client.
|
||||
* @param runQueue the place to post tasks required by clients
|
||||
*/
|
||||
public BureauClient (String bureauId, String sharedSecret, RunQueue runQueue)
|
||||
{
|
||||
super(null, runQueue);
|
||||
_bureauId = bureauId;
|
||||
_creds = new BureauCredentials(_bureauId, sharedSecret);
|
||||
_ctx = createContext();
|
||||
_director = createDirector();
|
||||
}
|
||||
|
||||
protected abstract BureauDirector createDirector ();
|
||||
|
||||
protected BureauContext createContext ()
|
||||
{
|
||||
return new BureauContext() {
|
||||
public BureauDirector getBureauDirector () {
|
||||
return _director;
|
||||
}
|
||||
public DObjectManager getDObjectManager () {
|
||||
return _omgr;
|
||||
}
|
||||
public Client getClient () {
|
||||
return BureauClient.this;
|
||||
}
|
||||
public Config getConfig () {
|
||||
return _config;
|
||||
}
|
||||
public String getBureauId () {
|
||||
return _bureauId;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected BureauContext _ctx;
|
||||
protected String _bureauId;
|
||||
protected BureauDirector _director;
|
||||
protected Config _config = new Config("bureau");
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.threerings.presents.client.InvocationDecoder;
|
||||
|
||||
/**
|
||||
* Dispatches calls to a {@link BureauReceiver} instance.
|
||||
*/
|
||||
public class BureauDecoder extends InvocationDecoder
|
||||
{
|
||||
/** The generated hash code used to identify this receiver class. */
|
||||
public static final String RECEIVER_CODE = "3e98f7a30deb5a8e25e05c71c6081bf4";
|
||||
|
||||
/** The method id used to dispatch {@link BureauReceiver#createAgent}
|
||||
* notifications. */
|
||||
public static final int CREATE_AGENT = 1;
|
||||
|
||||
/** The method id used to dispatch {@link BureauReceiver#destroyAgent}
|
||||
* notifications. */
|
||||
public static final int DESTROY_AGENT = 2;
|
||||
|
||||
/**
|
||||
* Creates a decoder that may be registered to dispatch invocation
|
||||
* service notifications to the specified receiver.
|
||||
*/
|
||||
public BureauDecoder (BureauReceiver receiver)
|
||||
{
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReceiverCode ()
|
||||
{
|
||||
return RECEIVER_CODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchNotification (int methodId, Object[] args)
|
||||
{
|
||||
switch (methodId) {
|
||||
case CREATE_AGENT:
|
||||
((BureauReceiver)receiver).createAgent(
|
||||
((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
case DESTROY_AGENT:
|
||||
((BureauReceiver)receiver).destroyAgent(
|
||||
((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchNotification(methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
|
||||
import com.threerings.presents.client.BasicDirector;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
import com.threerings.presents.util.SafeSubscriber;
|
||||
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import com.threerings.bureau.data.BureauCodes;
|
||||
import com.threerings.bureau.server.BureauRegistry;
|
||||
import com.threerings.bureau.util.BureauContext;
|
||||
|
||||
import static com.threerings.bureau.Log.log;
|
||||
|
||||
/**
|
||||
* Allows the server to create and destroy agents on a client.
|
||||
* @see BureauRegistry
|
||||
*/
|
||||
public abstract class BureauDirector extends BasicDirector
|
||||
{
|
||||
/**
|
||||
* Creates a new BureauDirector.
|
||||
*/
|
||||
public BureauDirector (BureauContext ctx)
|
||||
{
|
||||
super(ctx);
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
@Override // from BasicDirector
|
||||
public void clientDidLogon (Client client)
|
||||
{
|
||||
super.clientDidLogon(client);
|
||||
_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);
|
||||
}
|
||||
};
|
||||
|
||||
log.info("Subscribing to object " + agentId);
|
||||
|
||||
SafeSubscriber<AgentObject> subscriber =
|
||||
new SafeSubscriber<AgentObject>(agentId, delegator);
|
||||
_subscribers.put(agentId, subscriber);
|
||||
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) {
|
||||
log.warning("Lost an agent, id " + agentId);
|
||||
} else {
|
||||
try {
|
||||
agent.stop();
|
||||
} catch (Throwable t) {
|
||||
log.warning("Stopping an agent caused an exception", t);
|
||||
}
|
||||
SafeSubscriber<AgentObject> subscriber = _subscribers.remove(agentId);
|
||||
if (subscriber == null) {
|
||||
log.warning("Lost a subscriber for agent " + agent);
|
||||
} else {
|
||||
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();
|
||||
|
||||
log.info("Object " + oid + " now available");
|
||||
|
||||
Agent agent;
|
||||
try {
|
||||
agent = createAgent(agentObject);
|
||||
agent.init(agentObject);
|
||||
agent.start();
|
||||
} catch (Throwable t) {
|
||||
log.warning("Could not create agent", "obj", agentObject, 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, cause);
|
||||
}
|
||||
|
||||
@Override // from BasicDirector
|
||||
protected void registerServices (Client client)
|
||||
{
|
||||
super.registerServices(client);
|
||||
|
||||
// Require the bureau services
|
||||
client.addServiceGroup(BureauCodes.BUREAU_GROUP);
|
||||
|
||||
// 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(receiver));
|
||||
}
|
||||
|
||||
@Override // from BasicDirector
|
||||
protected void fetchServices (Client client)
|
||||
{
|
||||
super.fetchServices(client);
|
||||
|
||||
_bureauService = client.getService(BureauService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 abstract Agent createAgent (AgentObject agentObj);
|
||||
|
||||
protected BureauContext _ctx;
|
||||
protected BureauService _bureauService;
|
||||
protected IntMap<Agent> _agents = IntMaps.newHashIntMap();
|
||||
protected IntMap<SafeSubscriber<AgentObject>> _subscribers =
|
||||
IntMaps.newHashIntMap();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.threerings.presents.client.InvocationReceiver;
|
||||
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
|
||||
/**
|
||||
* Hooks for controlling a previously launched bureau client.
|
||||
*/
|
||||
public interface BureauReceiver extends InvocationReceiver
|
||||
{
|
||||
/**
|
||||
* Creates a new agent. Implementors should create a new {@link Agent} and give it access to
|
||||
* the {@link AgentObject} referred to by the <code>agentId</code> parameter and must notify
|
||||
* the service that the agent has been created using {@link BureauService#agentCreated}.
|
||||
* @param agentId the id of the <code>AgentObject</code> that needs an <code>Agent</code>
|
||||
*/
|
||||
void createAgent (int agentId);
|
||||
|
||||
/**
|
||||
* Destroys a previously created agent. Implementors should destroy the agent that was created
|
||||
* by the call to <code>createAgent</code> with the same agent id and must notify
|
||||
* the service that the agent has been created using {@link BureauService#agentDestroyed}.
|
||||
* @param agentId the id of the <code>AgentObject</code> whose <code>Agent</code>
|
||||
* should be destroyed
|
||||
*/
|
||||
void destroyAgent (int agentId);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* Interface for the bureau to communicate with the server.
|
||||
*/
|
||||
public interface BureauService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Notifies the server that the bureau is up and running and ready to receive
|
||||
* requests via the <code>BureauReceiver</code>.
|
||||
* @see BureauReceiver
|
||||
*/
|
||||
void bureauInitialized (Client client, String bureauId);
|
||||
|
||||
/**
|
||||
* Notifies the server that this bureau has encountered a critical error and needs to be shut
|
||||
* down.
|
||||
*/
|
||||
void bureauError (Client client, String message);
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @see BureauReceiver#destroyAgent
|
||||
*/
|
||||
void agentDestroyed (Client client, int agentId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
|
||||
/**
|
||||
* Contains information for configuring and communicating with an agent.
|
||||
*/
|
||||
public class AgentObject extends DObject
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>bureauId</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String BUREAU_ID = "bureauId";
|
||||
|
||||
/** The field name of the <code>bureauType</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String BUREAU_TYPE = "bureauType";
|
||||
|
||||
/** The field name of the <code>code</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String CODE = "code";
|
||||
|
||||
/** The field name of the <code>className</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String CLASS_NAME = "className";
|
||||
|
||||
/** The field name of the <code>clientOid</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String CLIENT_OID = "clientOid";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** The id of the bureau the agent is running in. This is normally a unique id corresponding
|
||||
* to the game or item that requires some server-side processing. */
|
||||
public String bureauId;
|
||||
|
||||
/** The type of bureau that the agent is running in. This is normally derived from the kind
|
||||
* of media that the game or item has specified for its code and determines the method of
|
||||
* launching the bureau when the first agent is requested. */
|
||||
public String bureauType;
|
||||
|
||||
/** The location of the code for the agent. This could be a URL to an action script file or
|
||||
* some other description that the bureau can use to load and execute the agent's code. */
|
||||
public String code;
|
||||
|
||||
/** The main class within the code to use when launching an agent. Whether this value is
|
||||
* used depends on the type of bureau and will be resolve in the bureau client. */
|
||||
public String className;
|
||||
|
||||
/** The id of the client running this agent (only set after the agent is assigned to a
|
||||
* bureau and run). */
|
||||
public int clientOid;
|
||||
|
||||
/**
|
||||
* Returns a brief string that identifies this agent. Use this instead of
|
||||
* {@link Object#toString} when you wish to report an agent object in a log message.
|
||||
*/
|
||||
@Override
|
||||
public String which ()
|
||||
{
|
||||
return "[bid=" + bureauId + ", type=" + bureauType + "]";
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that the <code>bureauId</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setBureauId (String value)
|
||||
{
|
||||
String ovalue = this.bureauId;
|
||||
requestAttributeChange(
|
||||
BUREAU_ID, value, ovalue);
|
||||
this.bureauId = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>bureauType</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setBureauType (String value)
|
||||
{
|
||||
String ovalue = this.bureauType;
|
||||
requestAttributeChange(
|
||||
BUREAU_TYPE, value, ovalue);
|
||||
this.bureauType = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>code</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setCode (String value)
|
||||
{
|
||||
String ovalue = this.code;
|
||||
requestAttributeChange(
|
||||
CODE, value, ovalue);
|
||||
this.code = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>className</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setClassName (String value)
|
||||
{
|
||||
String ovalue = this.className;
|
||||
requestAttributeChange(
|
||||
CLASS_NAME, value, ovalue);
|
||||
this.className = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>clientOid</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setClientOid (int value)
|
||||
{
|
||||
int ovalue = this.clientOid;
|
||||
requestAttributeChange(
|
||||
CLIENT_OID, Integer.valueOf(value), Integer.valueOf(ovalue));
|
||||
this.clientOid = value;
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.data;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Represents an authenticated bureau client.
|
||||
*/
|
||||
public class BureauAuthName extends Name
|
||||
{
|
||||
public BureauAuthName (String bureauId)
|
||||
{
|
||||
super(bureauId);
|
||||
}
|
||||
|
||||
// used when unserializing
|
||||
public BureauAuthName ()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.data;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
|
||||
/**
|
||||
* An object representing a Bureau connection. This is currently just a marker class.
|
||||
*/
|
||||
public class BureauClientObject extends ClientObject
|
||||
{
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return "BUREAU_CLIENT_OBJECT(" + super.toString() + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.data;
|
||||
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
|
||||
/**
|
||||
* Codes and constants global to the Bureau services.
|
||||
*/
|
||||
public interface BureauCodes extends InvocationCodes
|
||||
{
|
||||
/** Defines our invocation services group. */
|
||||
public static final String BUREAU_GROUP = "bureau";
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.data;
|
||||
|
||||
import com.threerings.presents.net.ServiceCreds;
|
||||
|
||||
/**
|
||||
* Extends the basic credentials to provide bureau-specific fields.
|
||||
*/
|
||||
public class BureauCredentials extends ServiceCreds
|
||||
{
|
||||
/**
|
||||
* Creates new credentials for a specific bureau.
|
||||
*/
|
||||
public BureauCredentials (String bureauId, String sharedSecret)
|
||||
{
|
||||
super(bureauId, sharedSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty credentials for streaming. Should not be used directly.
|
||||
*/
|
||||
public BureauCredentials ()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.bureau.client.BureauService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link BureauService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from BureauService.java.")
|
||||
public class BureauMarshaller extends InvocationMarshaller
|
||||
implements BureauService
|
||||
{
|
||||
/** The method id used to dispatch {@link #agentCreated} requests. */
|
||||
public static final int AGENT_CREATED = 1;
|
||||
|
||||
// from interface BureauService
|
||||
public void agentCreated (Client arg1, int arg2)
|
||||
{
|
||||
sendRequest(arg1, AGENT_CREATED, new Object[] {
|
||||
Integer.valueOf(arg2)
|
||||
});
|
||||
}
|
||||
|
||||
/** 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 = 3;
|
||||
|
||||
// from interface BureauService
|
||||
public void agentDestroyed (Client arg1, int arg2)
|
||||
{
|
||||
sendRequest(arg1, AGENT_DESTROYED, new Object[] {
|
||||
Integer.valueOf(arg2)
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #bureauError} requests. */
|
||||
public static final int BUREAU_ERROR = 4;
|
||||
|
||||
// from interface BureauService
|
||||
public void bureauError (Client arg1, String arg2)
|
||||
{
|
||||
sendRequest(arg1, BUREAU_ERROR, new Object[] {
|
||||
arg2
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #bureauInitialized} requests. */
|
||||
public static final int BUREAU_INITIALIZED = 5;
|
||||
|
||||
// from interface BureauService
|
||||
public void bureauInitialized (Client arg1, String arg2)
|
||||
{
|
||||
sendRequest(arg1, BUREAU_INITIALIZED, new Object[] {
|
||||
arg2
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.ClientResolver;
|
||||
|
||||
import com.threerings.bureau.data.BureauClientObject;
|
||||
|
||||
/**
|
||||
* Used to configure crowd-specific client object data.
|
||||
*/
|
||||
public class BureauClientResolver extends ClientResolver
|
||||
{
|
||||
@Override // from ClientResolver
|
||||
public ClientObject createClientObject ()
|
||||
{
|
||||
return new BureauClientObject();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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 javax.annotation.Generated;
|
||||
|
||||
import com.threerings.bureau.data.BureauMarshaller;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link BureauProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from BureauService.java.")
|
||||
public class BureauDispatcher extends InvocationDispatcher<BureauMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public BureauDispatcher (BureauProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BureauMarshaller createMarshaller ()
|
||||
{
|
||||
return new BureauMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case BureauMarshaller.AGENT_CREATED:
|
||||
((BureauProvider)provider).agentCreated(
|
||||
source, ((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
case BureauMarshaller.AGENT_CREATION_FAILED:
|
||||
((BureauProvider)provider).agentCreationFailed(
|
||||
source, ((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
case BureauMarshaller.AGENT_DESTROYED:
|
||||
((BureauProvider)provider).agentDestroyed(
|
||||
source, ((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
case BureauMarshaller.BUREAU_ERROR:
|
||||
((BureauProvider)provider).bureauError(
|
||||
source, (String)args[0]
|
||||
);
|
||||
return;
|
||||
|
||||
case BureauMarshaller.BUREAU_INITIALIZED:
|
||||
((BureauProvider)provider).bureauInitialized(
|
||||
source, (String)args[0]
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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 javax.annotation.Generated;
|
||||
|
||||
import com.threerings.bureau.client.BureauService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link BureauService}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from BureauService.java.")
|
||||
public interface BureauProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link BureauService#agentCreated} request.
|
||||
*/
|
||||
void agentCreated (ClientObject caller, int arg1);
|
||||
|
||||
/**
|
||||
* Handles a {@link BureauService#agentCreationFailed} request.
|
||||
*/
|
||||
void agentCreationFailed (ClientObject caller, int arg1);
|
||||
|
||||
/**
|
||||
* Handles a {@link BureauService#agentDestroyed} request.
|
||||
*/
|
||||
void agentDestroyed (ClientObject caller, int arg1);
|
||||
|
||||
/**
|
||||
* Handles a {@link BureauService#bureauError} request.
|
||||
*/
|
||||
void bureauError (ClientObject caller, String arg1);
|
||||
|
||||
/**
|
||||
* Handles a {@link BureauService#bureauInitialized} request.
|
||||
*/
|
||||
void bureauInitialized (ClientObject caller, String arg1);
|
||||
}
|
||||
@@ -0,0 +1,804 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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 java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.Invoker;
|
||||
import com.samskivert.util.RunQueue;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.annotation.MainInvoker;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.server.ClientManager;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
import com.threerings.presents.server.PresentsSession;
|
||||
import com.threerings.presents.server.ServiceAuthenticator;
|
||||
import com.threerings.presents.server.SessionFactory;
|
||||
import com.threerings.presents.server.net.ConnectionManager;
|
||||
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import com.threerings.bureau.data.BureauAuthName;
|
||||
import com.threerings.bureau.data.BureauCodes;
|
||||
import com.threerings.bureau.data.BureauCredentials;
|
||||
import com.threerings.bureau.util.BureauLogRedirector;
|
||||
|
||||
import static com.threerings.bureau.Log.log;
|
||||
|
||||
/**
|
||||
* Abstracts the launching and termination of external processes (bureaus) that host instances of
|
||||
* server-side code (agents).
|
||||
*/
|
||||
@Singleton
|
||||
public class BureauRegistry
|
||||
{
|
||||
/**
|
||||
* Defines how a bureau is launched. 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 associated bureau type.
|
||||
*/
|
||||
public static interface Launcher
|
||||
{
|
||||
/**
|
||||
* Kicks off a new bureau. This method will always be called on the unit invocation
|
||||
* thread since it may do extensive I/O.
|
||||
* @param bureauId the id of the bureau being launched
|
||||
* @param token the secret string for the bureau to use in its credentials
|
||||
*/
|
||||
void launchBureau (String bureauId, String token)
|
||||
throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how to generate a command to launch a bureau in a local process.
|
||||
* @see #setCommandGenerator(String,CommandGenerator,int)
|
||||
* @see Launcher
|
||||
*/
|
||||
public static interface CommandGenerator
|
||||
{
|
||||
/**
|
||||
* Creates the command line to launch a new bureau using the given information.
|
||||
* Called by the registry when a new bureau is needed whose type was registered
|
||||
* with <code>setCommandGenerator</code>.
|
||||
* @param bureauId the id of the bureau being launched
|
||||
* @param token the token string to use for the credentials when logging in
|
||||
* @return command line arguments, including executable name
|
||||
*/
|
||||
String[] createCommand (String bureauId, String token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an uninitialized registry.
|
||||
*/
|
||||
@Inject public BureauRegistry (
|
||||
InvocationManager invmgr, ConnectionManager conmgr, ClientManager clmgr)
|
||||
{
|
||||
invmgr.registerDispatcher(new BureauDispatcher(new BureauProvider() {
|
||||
public void bureauInitialized (ClientObject client, String bureauId) {
|
||||
BureauRegistry.this.bureauInitialized(client, bureauId);
|
||||
}
|
||||
public void bureauError (ClientObject caller, String message) {
|
||||
BureauRegistry.this.bureauError(caller, message);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}), BureauCodes.BUREAU_GROUP);
|
||||
conmgr.addChainedAuthenticator(new ServiceAuthenticator<BureauCredentials>(
|
||||
BureauCredentials.class, BureauAuthName.class) {
|
||||
@Override protected boolean areValid (BureauCredentials creds) {
|
||||
return checkToken(creds) == null;
|
||||
}
|
||||
});
|
||||
clmgr.addSessionFactory(
|
||||
SessionFactory.newSessionFactory(BureauCredentials.class, getSessionClass(),
|
||||
BureauAuthName.class, getClientResolverClass()));
|
||||
clmgr.addClientObserver(new ClientManager.ClientObserver() {
|
||||
public void clientSessionDidStart (PresentsSession client) {
|
||||
if (client.getCredentials() instanceof BureauCredentials) {
|
||||
sessionDidStart(client, ((BureauCredentials)client.getCredentials()).clientId);
|
||||
}
|
||||
}
|
||||
public void clientSessionDidEnd (PresentsSession client) {
|
||||
if (client.getCredentials() instanceof BureauCredentials) {
|
||||
sessionDidEnd(client, ((BureauCredentials)client.getCredentials()).clientId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the credentials to make sure this is one of our bureaus.
|
||||
* @return null if all's well, otherwise a string describing the authentication failure
|
||||
*/
|
||||
public String checkToken (BureauCredentials creds)
|
||||
{
|
||||
Bureau bureau = _bureaus.get(creds.clientId);
|
||||
if (bureau == null) {
|
||||
return "Bureau " + creds.clientId + " not found";
|
||||
}
|
||||
if (bureau.clientObj != null) {
|
||||
return "Bureau " + creds.clientId + " already logged in";
|
||||
}
|
||||
if (!creds.areValid(bureau.token)) {
|
||||
return "Bureau " + creds.clientId + " does not match credentials token";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. The registry will wait indefinitely for the bureau to connect back.
|
||||
* @param bureauType the type of bureau that will be launched
|
||||
* @param cmdGenerator the generator to be used for bureaus of <code>bureauType</code>
|
||||
*/
|
||||
public void setCommandGenerator (String bureauType, final CommandGenerator cmdGenerator)
|
||||
{
|
||||
setCommandGenerator(bureauType, cmdGenerator, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. If the launched bureau does not connect within the given number of
|
||||
* milliseconds, it will be logged as an error and future attempts to launch the bureau
|
||||
* will try launching the command again.
|
||||
* @param bureauType the type of bureau that will be launched
|
||||
* @param cmdGenerator the generator to be used for bureaus of <code>bureauType</code>
|
||||
* @param timeout milliseconds to wait for the bureau or 0 to wait forever
|
||||
*/
|
||||
public void setCommandGenerator (
|
||||
String bureauType, final CommandGenerator cmdGenerator, int timeout)
|
||||
{
|
||||
setLauncher(bureauType, new Launcher() {
|
||||
public void launchBureau (String bureauId, String token)
|
||||
throws IOException {
|
||||
ProcessBuilder builder = new ProcessBuilder(
|
||||
cmdGenerator.createCommand(bureauId, token));
|
||||
builder.redirectErrorStream(true);
|
||||
Process process = builder.start();
|
||||
// log the output of the process and prefix with bureau id
|
||||
new BureauLogRedirector(bureauId, process.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "DefaultLauncher for " + cmdGenerator;
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a launcher for a given type. When an agent is started and no bureaus are
|
||||
* running, the <code>bureauType</code> is used to determine the <code>Launcher</code>
|
||||
* instance to call. The registry will wait indefinitely for the launched bureau
|
||||
* to connect back.
|
||||
* @param bureauType the type of bureau that will be launched
|
||||
* @param launcher the launcher to be used for bureaus of <code>bureauType</code>
|
||||
*/
|
||||
public void setLauncher (String bureauType, Launcher launcher)
|
||||
{
|
||||
setLauncher(bureauType, launcher, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a launcher for a given type. When an agent is started and no bureaus are
|
||||
* running, the <code>bureauType</code> is used to determine the <code>Launcher</code>
|
||||
* instance to call. If the launched bureau does not connect within the given number of
|
||||
* milliseconds, it will be logged as an error and future attempts to launch the bureau
|
||||
* will invoke the <code>launch</code> method again.
|
||||
* @param bureauType the type of bureau that will be launched
|
||||
* @param launcher the launcher to be used for bureaus of <code>bureauType</code>
|
||||
* @param timeout milliseconds to wait for the bureau or 0 to wait forever
|
||||
*/
|
||||
public void setLauncher (String bureauType, Launcher launcher, int timeout)
|
||||
{
|
||||
if (_launchers.get(bureauType) != null) {
|
||||
log.warning("Launcher for type already exists", "type", bureauType);
|
||||
return;
|
||||
}
|
||||
|
||||
_launchers.put(bureauType, new LauncherEntry(launcher, timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new agent using the data in the given object, creating a new bureau if necessary.
|
||||
*/
|
||||
public void startAgent (AgentObject agent)
|
||||
{
|
||||
agent.setLocal(AgentData.class, new AgentData());
|
||||
|
||||
Bureau bureau = _bureaus.get(agent.bureauId);
|
||||
if (bureau != null && bureau.ready()) {
|
||||
_omgr.registerObject(agent);
|
||||
|
||||
log.info("Bureau ready, sending createAgent", "agent", agent.which());
|
||||
BureauSender.createAgent(bureau.clientObj, agent.getOid());
|
||||
bureau.agentStates.put(agent, AgentState.STARTED);
|
||||
bureau.summarize();
|
||||
return;
|
||||
}
|
||||
|
||||
if (bureau == null) {
|
||||
LauncherEntry launcherEntry = _launchers.get(agent.bureauType);
|
||||
if (launcherEntry == null) {
|
||||
log.warning("Launcher not found", "agent", agent.which());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Creating new bureau", "bureauId", agent.bureauId, "launcher", launcherEntry);
|
||||
bureau = new Bureau();
|
||||
bureau.bureauId = agent.bureauId;
|
||||
bureau.token = generateToken(bureau.bureauId);
|
||||
bureau.launcherEntry = launcherEntry;
|
||||
_invoker.postUnit(new LauncherUnit(bureau, _omgr));
|
||||
_bureaus.put(agent.bureauId, bureau);
|
||||
}
|
||||
|
||||
_omgr.registerObject(agent);
|
||||
bureau.agentStates.put(agent, AgentState.PENDING);
|
||||
|
||||
log.info("Bureau not ready, pending agent", "agent", agent.which());
|
||||
bureau.summarize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a previously started agent using the data in the given object.
|
||||
*/
|
||||
public void destroyAgent (AgentObject agent)
|
||||
{
|
||||
FoundAgent found = resolve(null, agent.getOid(), "destroyAgent");
|
||||
if (found == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Destroying agent", "agent", agent.which());
|
||||
|
||||
// transition the agent to a new state and perform the effect of the transition
|
||||
if (found.state == AgentState.PENDING) {
|
||||
found.bureau.agentStates.remove(found.agent);
|
||||
_omgr.destroyObject(found.agent.getOid());
|
||||
|
||||
} else if (found.state == AgentState.STARTED) {
|
||||
found.bureau.agentStates.put(found.agent, AgentState.STILL_BORN);
|
||||
|
||||
} else if (found.state == AgentState.RUNNING) {
|
||||
// TODO: have a timeout for this in case the client is misbehaving or hung
|
||||
BureauSender.destroyAgent(found.bureau.clientObj, agent.getOid());
|
||||
found.bureau.agentStates.put(found.agent, AgentState.DESTROYED);
|
||||
|
||||
} else if (found.state == AgentState.DESTROYED ||
|
||||
found.state == AgentState.STILL_BORN) {
|
||||
log.warning("Ignoring request to destroy agent in unexpected state",
|
||||
"state", found.state, "agent", found.agent.which());
|
||||
}
|
||||
|
||||
found.bureau.summarize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active session for a bureau of the given id.
|
||||
*/
|
||||
public PresentsSession lookupClient (String bureauId)
|
||||
{
|
||||
Bureau bureau = _bureaus.get(bureauId);
|
||||
if (bureau == null) {
|
||||
return null;
|
||||
}
|
||||
return bureau.client;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this agent's bureau encountered an error on launch, return it.
|
||||
*/
|
||||
public Exception getLaunchError (AgentObject agentObj)
|
||||
{
|
||||
AgentData data = agentObj.getLocal(AgentData.class);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return data.launchError;
|
||||
}
|
||||
|
||||
protected void sessionDidStart (PresentsSession client, String id)
|
||||
{
|
||||
Bureau bureau = _bureaus.get(id);
|
||||
if (bureau == null) {
|
||||
log.warning("Starting session for unknown bureau", "id", id, "client", client);
|
||||
return;
|
||||
}
|
||||
if (bureau.client != null) {
|
||||
log.warning("Multiple sessions for the same bureau", "id", id, "client", client,
|
||||
"bureau", bureau);
|
||||
}
|
||||
bureau.client = client;
|
||||
}
|
||||
|
||||
protected void sessionDidEnd (PresentsSession client, String id)
|
||||
{
|
||||
Bureau bureau = _bureaus.get(id);
|
||||
if (bureau == null) {
|
||||
log.warning("Ending session for unknown bureau", "id", id, "client", client);
|
||||
return;
|
||||
}
|
||||
if (bureau.client == null) {
|
||||
log.warning("Multiple logouts from the same bureau", "id", id, "client", client,
|
||||
"bureau", bureau);
|
||||
}
|
||||
bureau.client = null;
|
||||
|
||||
clientDestroyed(bureau);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the bureau client acknowledges starting up. Starts all pending agents and
|
||||
* causes subsequent agent start requests to be sent directly to the bureau.
|
||||
*/
|
||||
protected void bureauInitialized (ClientObject client, String bureauId)
|
||||
{
|
||||
final Bureau bureau = _bureaus.get(bureauId);
|
||||
if (bureau == null) {
|
||||
log.warning("Initialization of non-existent bureau", "bureauId", bureauId);
|
||||
return;
|
||||
}
|
||||
|
||||
bureau.clientObj = client;
|
||||
|
||||
log.info("Bureau created, launching pending agents", "bureau", bureau);
|
||||
|
||||
// find all pending agents
|
||||
Set<AgentObject> pending = Sets.newHashSet();
|
||||
|
||||
for (Map.Entry<AgentObject, AgentState> entry :
|
||||
bureau.agentStates.entrySet()) {
|
||||
|
||||
if (entry.getValue() == AgentState.PENDING) {
|
||||
pending.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
// create them
|
||||
for (AgentObject agent : pending) {
|
||||
log.info("Creating agent", "agent", agent.which());
|
||||
BureauSender.createAgent(bureau.clientObj, agent.getOid());
|
||||
bureau.agentStates.put(agent, AgentState.STARTED);
|
||||
}
|
||||
|
||||
bureau.summarize();
|
||||
}
|
||||
|
||||
protected void bureauError (ClientObject caller, String message)
|
||||
{
|
||||
for (Bureau bureau : _bureaus.values()) {
|
||||
if (bureau.clientObj == caller) {
|
||||
log.info(
|
||||
"Bureau error occurred", "caller", caller.who(), "message", message,
|
||||
"bureau", bureau.bureauId);
|
||||
bureau.client.endSession();
|
||||
return;
|
||||
}
|
||||
}
|
||||
log.warning(
|
||||
"Bureau error occurred in unregistered bureau", "caller", caller.who(),
|
||||
"message", message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the bureau client acknowledges the creation of an agent.
|
||||
*/
|
||||
protected void agentCreated (ClientObject client, int agentId)
|
||||
{
|
||||
FoundAgent found = resolve(client, agentId, "agentCreated");
|
||||
if (found == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Agent creation confirmed", "agent", found.agent.which());
|
||||
|
||||
if (found.state == AgentState.STARTED) {
|
||||
found.bureau.agentStates.put(found.agent, AgentState.RUNNING);
|
||||
found.agent.setClientOid(client.getOid());
|
||||
|
||||
} else if (found.state == AgentState.STILL_BORN) {
|
||||
// TODO: have a timeout for this in case the client is misbehaving or hung
|
||||
BureauSender.destroyAgent(found.bureau.clientObj, agentId);
|
||||
found.bureau.agentStates.put(found.agent, AgentState.DESTROYED);
|
||||
|
||||
} else if (found.state == AgentState.PENDING ||
|
||||
found.state == AgentState.RUNNING ||
|
||||
found.state == AgentState.DESTROYED) {
|
||||
log.warning("Ignoring confirmation of creation of an agent in an unexpected state",
|
||||
"state", found.state, "agent", found.agent.which());
|
||||
}
|
||||
|
||||
found.bureau.summarize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the bureau client acknowledges the failure to create an agent.
|
||||
*/
|
||||
protected void agentCreationFailed (ClientObject client, int agentId)
|
||||
{
|
||||
FoundAgent found = resolve(client, agentId, "agentCreationFailed");
|
||||
if (found == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Agent creation failed", "agent", found.agent.which());
|
||||
|
||||
if (found.state == AgentState.STARTED ||
|
||||
found.state == AgentState.STILL_BORN) {
|
||||
found.bureau.agentStates.remove(found.agent);
|
||||
_omgr.destroyObject(found.agent.getOid());
|
||||
|
||||
} else if (found.state == AgentState.PENDING ||
|
||||
found.state == AgentState.RUNNING ||
|
||||
found.state == AgentState.DESTROYED) {
|
||||
log.warning("Ignoring failure of creation of an agent in an unexpected state",
|
||||
"state", found.state, "agent", found.agent.which());
|
||||
}
|
||||
|
||||
found.bureau.summarize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the bureau client acknowledges the destruction of an agent.
|
||||
*/
|
||||
protected void agentDestroyed (ClientObject client, int agentId)
|
||||
{
|
||||
FoundAgent found = resolve(client, agentId, "agentDestroyed");
|
||||
if (found == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Agent destruction confirmed", "agent", found.agent.which());
|
||||
|
||||
if (found.state == AgentState.DESTROYED) {
|
||||
found.bureau.agentStates.remove(found.agent);
|
||||
_omgr.destroyObject(found.agent.getOid());
|
||||
|
||||
} else if (found.state == AgentState.PENDING ||
|
||||
found.state == AgentState.STARTED ||
|
||||
found.state == AgentState.RUNNING ||
|
||||
found.state == AgentState.STILL_BORN) {
|
||||
log.warning("Ignoring confirmation of destruction of agent in unexpected state",
|
||||
"state", found.state, "agent", found.agent.which());
|
||||
}
|
||||
|
||||
found.bureau.summarize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when a client is destroyed.
|
||||
*/
|
||||
protected void clientDestroyed (Bureau bureau)
|
||||
{
|
||||
log.info("Client destroyed, destroying all agents", "bureau", bureau);
|
||||
|
||||
// clean up any agents attached to this bureau
|
||||
for (AgentObject agent : bureau.agentStates.keySet()) {
|
||||
_omgr.destroyObject(agent.getOid());
|
||||
}
|
||||
bureau.agentStates.clear();
|
||||
|
||||
if (_bureaus.remove(bureau.bureauId) == null) {
|
||||
log.info("Bureau not found to remove", "bureau", bureau);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does lots of null checks and lookups and resolves the given information into FoundAgent.
|
||||
*/
|
||||
protected FoundAgent resolve (ClientObject client, int agentId, String resolver)
|
||||
{
|
||||
com.threerings.presents.dobj.DObject dobj = _omgr.getObject(agentId);
|
||||
if (dobj == null) {
|
||||
log.warning("Non-existent agent", "function", resolver, "agentId", agentId);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(dobj instanceof AgentObject)) {
|
||||
log.warning("Object not an agent", "function", resolver, "obj", dobj.getClass());
|
||||
return null;
|
||||
}
|
||||
|
||||
AgentObject agent = (AgentObject)dobj;
|
||||
Bureau bureau = _bureaus.get(agent.bureauId);
|
||||
if (bureau == null) {
|
||||
log.warning("Bureau not found for agent", "function", resolver, "agent", agent.which());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!bureau.agentStates.containsKey(agent)) {
|
||||
log.warning("Bureau does not have agent", "function", resolver, "agent", agent.which());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (client != null && bureau.clientObj != client) {
|
||||
log.warning("Masquerading request", "function", resolver, "agent", agent.which(),
|
||||
"client", bureau.clientObj, "client", client);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FoundAgent(bureau, agent, bureau.agentStates.get(agent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hard-to-guess token that the bureau can use to authenticate itself when it tries
|
||||
* to log in.
|
||||
*/
|
||||
protected String generateToken (String bureauId)
|
||||
{
|
||||
String tokenSource = bureauId + "@" + System.currentTimeMillis() + "r" + Math.random();
|
||||
return StringUtil.md5hex(tokenSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the launcher unit timeout time after launching.
|
||||
* @param bureau bureau whose launch occurred
|
||||
*/
|
||||
protected void launchTimeoutExpired (Bureau bureau)
|
||||
{
|
||||
if (bureau.clientObj != null) {
|
||||
return; // all's well, ignore
|
||||
}
|
||||
|
||||
if (!_bureaus.containsKey(bureau.bureauId)) {
|
||||
// bureau has already managed to get destroyed before the launch timeout, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
handleLaunchError(bureau, null, "timeout");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when something goes wrong with launching a bureau.
|
||||
*/
|
||||
protected void handleLaunchError (Bureau bureau, Exception error, String cause)
|
||||
{
|
||||
if (cause == null && error != null) {
|
||||
cause = error.getMessage();
|
||||
}
|
||||
log.info("Bureau failed to launch", "bureau", bureau, "cause", cause);
|
||||
|
||||
// clean up any agents attached to this bureau
|
||||
for (AgentObject agent : bureau.agentStates.keySet()) {
|
||||
agent.getLocal(AgentData.class).launchError = error;
|
||||
_omgr.destroyObject(agent.getOid());
|
||||
}
|
||||
bureau.agentStates.clear();
|
||||
|
||||
_bureaus.remove(bureau.bureauId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class used to handle bureau sessions.
|
||||
*/
|
||||
protected Class<? extends BureauSession> getSessionClass ()
|
||||
{
|
||||
return BureauSession.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class used to resolve bureau client data.
|
||||
*/
|
||||
protected Class<? extends BureauClientResolver> getClientResolverClass ()
|
||||
{
|
||||
return BureauClientResolver.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoker unit to launch a bureau's process, then assign the result on the main thread.
|
||||
*/
|
||||
protected class LauncherUnit extends Invoker.Unit
|
||||
{
|
||||
LauncherUnit (Bureau bureau, RunQueue runQueue) {
|
||||
super("LauncherUnit for " + bureau + ": " + StringUtil.toString(bureau.launcherEntry));
|
||||
_bureau = bureau;
|
||||
_runQueue = runQueue;
|
||||
}
|
||||
|
||||
@Override public boolean invoke () {
|
||||
try {
|
||||
_bureau.launch();
|
||||
} catch (Exception e) {
|
||||
_error = e;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult () {
|
||||
if (_error == null) {
|
||||
// bureau launched ok, but it may still not connect. wait for timeout
|
||||
int timeout = _bureau.launcherEntry.timeout;
|
||||
if (timeout != 0) {
|
||||
new Interval(_runQueue) {
|
||||
@Override public void expired () {
|
||||
launchTimeoutExpired(_bureau);
|
||||
}
|
||||
}.schedule(timeout);
|
||||
}
|
||||
_bureau.launched = true;
|
||||
_bureau.launcherEntry = null;
|
||||
log.info("Bureau launch requested", "bureau", _bureau);
|
||||
|
||||
} else {
|
||||
handleLaunchError(_bureau, _error, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected Bureau _bureau;
|
||||
protected Exception _error;
|
||||
protected RunQueue _runQueue;
|
||||
}
|
||||
|
||||
protected static class LauncherEntry
|
||||
{
|
||||
public Launcher launcher;
|
||||
public int timeout;
|
||||
|
||||
public LauncherEntry (Launcher launcher, int timeout) {
|
||||
this.launcher = launcher;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected enum AgentState
|
||||
{
|
||||
// Not yet stated, waiting for bureau to ack
|
||||
PENDING,
|
||||
// Bureau acked, agent told to start
|
||||
STARTED,
|
||||
// Agent ack'ed, now live and hosting, ready to tell other clients
|
||||
RUNNING,
|
||||
// Agent destruction requested, waiting for acknowledge (after which the agent is removed
|
||||
// from the Bureau, so has no state)
|
||||
DESTROYED,
|
||||
// Edge case: destroy request prior to RUNNING
|
||||
STILL_BORN
|
||||
}
|
||||
|
||||
/** Models the results of searching for an agent. */
|
||||
protected static class FoundAgent
|
||||
{
|
||||
FoundAgent (Bureau bureau, AgentObject agent, AgentState state) {
|
||||
this.bureau = bureau;
|
||||
this.agent = agent;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
// Bureau containing the agent
|
||||
Bureau bureau;
|
||||
|
||||
// The object
|
||||
AgentObject agent;
|
||||
|
||||
// The state of the agent
|
||||
AgentState state;
|
||||
}
|
||||
|
||||
/** Models a bureau, including the process handle, all running agents and their states. */
|
||||
protected static class Bureau
|
||||
{
|
||||
// non-null once the bureau is scheduled but not yet kicked off
|
||||
LauncherEntry launcherEntry;
|
||||
|
||||
// non-null once the bureau is kicked off
|
||||
boolean launched;
|
||||
|
||||
// The token given to this bureau for authentication
|
||||
String token;
|
||||
|
||||
// The bureau's key in the map of bureaus. All requests for this bureau
|
||||
// with this id should be associated with one instance
|
||||
String bureauId;
|
||||
|
||||
// The client object of the bureau that has opened a dobj connection to
|
||||
// the registry
|
||||
ClientObject clientObj;
|
||||
|
||||
// The client session
|
||||
PresentsSession client;
|
||||
|
||||
// The states of the various agents allocated to this bureau
|
||||
Map<AgentObject, AgentState> agentStates = Maps.newHashMap();
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[Bureau id=").append(bureauId).append(", client=");
|
||||
if (clientObj == null) {
|
||||
builder.append("null");
|
||||
} else {
|
||||
builder.append(clientObj.getOid());
|
||||
}
|
||||
builder.append(", launcherEntry=").append(launcherEntry);
|
||||
builder.append(", launched=").append(launched);
|
||||
builder.append(", totalAgents=").append(agentStates.size());
|
||||
agentSummary(builder.append(", ")).append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
boolean ready () {
|
||||
return clientObj != null;
|
||||
}
|
||||
|
||||
StringBuilder agentSummary (StringBuilder str) {
|
||||
int[] counts = new int[AgentState.values().length];
|
||||
for (Map.Entry<AgentObject, AgentState> me : agentStates.entrySet()) {
|
||||
counts[me.getValue().ordinal()]++;
|
||||
}
|
||||
for (AgentState state : AgentState.values()) {
|
||||
if (state.ordinal() > 0) {
|
||||
str.append(", ");
|
||||
}
|
||||
str.append(counts[state.ordinal()]).append(" ").append(state.name());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void summarize () {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("Bureau ").append(bureauId).append(" [");
|
||||
agentSummary(str).append("]");
|
||||
log.info(str.toString());
|
||||
}
|
||||
|
||||
void launch () throws IOException {
|
||||
launcherEntry.launcher.launchBureau(bureauId, token);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class AgentData
|
||||
{
|
||||
Exception launchError;
|
||||
}
|
||||
|
||||
protected Map<String, LauncherEntry> _launchers = Maps.newHashMap();
|
||||
protected Map<String, Bureau> _bureaus = Maps.newHashMap();
|
||||
|
||||
@Inject protected RootDObjectManager _omgr;
|
||||
@Inject protected @MainInvoker Invoker _invoker;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationSender;
|
||||
|
||||
import com.threerings.bureau.client.BureauDecoder;
|
||||
import com.threerings.bureau.client.BureauReceiver;
|
||||
|
||||
/**
|
||||
* Used to issue notifications to a {@link BureauReceiver} instance on a
|
||||
* client.
|
||||
*/
|
||||
public class BureauSender extends InvocationSender
|
||||
{
|
||||
/**
|
||||
* Issues a notification that will result in a call to {@link
|
||||
* BureauReceiver#createAgent} on a client.
|
||||
*/
|
||||
public static void createAgent (
|
||||
ClientObject target, int arg1)
|
||||
{
|
||||
sendNotification(
|
||||
target, BureauDecoder.RECEIVER_CODE, BureauDecoder.CREATE_AGENT,
|
||||
new Object[] { Integer.valueOf(arg1) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues a notification that will result in a call to {@link
|
||||
* BureauReceiver#destroyAgent} on a client.
|
||||
*/
|
||||
public static void destroyAgent (
|
||||
ClientObject target, int arg1)
|
||||
{
|
||||
sendNotification(
|
||||
target, BureauDecoder.RECEIVER_CODE, BureauDecoder.DESTROY_AGENT,
|
||||
new Object[] { Integer.valueOf(arg1) });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.presents.server.PresentsSession;
|
||||
|
||||
public class BureauSession extends PresentsSession
|
||||
{
|
||||
@Override // from PresentsSession
|
||||
protected void sessionConnectionClosed ()
|
||||
{
|
||||
super.sessionConnectionClosed();
|
||||
|
||||
// end our session when the connection is closed
|
||||
endSession();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.util;
|
||||
|
||||
import com.threerings.presents.util.PresentsContext;
|
||||
|
||||
import com.threerings.bureau.client.BureauDirector;
|
||||
|
||||
/**
|
||||
* Defines the objects held on a bureau client. This includes usual set of objects found on a
|
||||
* standard presents client.
|
||||
*/
|
||||
public interface BureauContext extends PresentsContext
|
||||
{
|
||||
/**
|
||||
* Access the director object.
|
||||
*/
|
||||
BureauDirector getBureauDirector ();
|
||||
|
||||
/**
|
||||
* Access the bureau id.
|
||||
*/
|
||||
String getBureauId ();
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import com.samskivert.util.Logger;
|
||||
|
||||
import static com.threerings.bureau.Log.log;
|
||||
|
||||
/**
|
||||
* Captures the output of a bureau and redirects it into a single logger instance using a thread
|
||||
* name equal to the bureau id. The {@link Logger} instance is the one for this class. The intent
|
||||
* is that log4j will be configured to use %t (thread name) to embed the bureau id.
|
||||
*/
|
||||
public class BureauLogRedirector
|
||||
{
|
||||
/**
|
||||
* Creates a new redirector with no size limit.
|
||||
* @param bureauId the id of the bureau being redirected - this will become the thread name
|
||||
* @param input the stream that is the output of the bureau process
|
||||
*/
|
||||
public BureauLogRedirector (String bureauId, InputStream input)
|
||||
{
|
||||
this(bureauId, input, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new redirector.
|
||||
* @param bureauId the id of the bureau being redirected - this will become the thread name
|
||||
* @param input the stream that is the output of the bureau process
|
||||
* @param limit approximate limit for the total characters written to the logger
|
||||
*/
|
||||
public BureauLogRedirector (String bureauId, InputStream input, int limit)
|
||||
{
|
||||
_bureauId = bureauId;
|
||||
_reader = new BufferedReader(new InputStreamReader(input));
|
||||
_limit = limit;
|
||||
Thread thread = new Thread(bureauId) {
|
||||
@Override public void run () {
|
||||
copyLoop();
|
||||
}};
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bureau id this was created with.
|
||||
*/
|
||||
public String getBureauId ()
|
||||
{
|
||||
return _bureauId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of characters written to the log.
|
||||
*/
|
||||
public int getWritten ()
|
||||
{
|
||||
return _written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character limit associated with the log.
|
||||
*/
|
||||
public int getLimit ()
|
||||
{
|
||||
return _limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the redirector's truncation status and allows additional output up to the given
|
||||
* character limit.
|
||||
*/
|
||||
public synchronized void reset (int limit)
|
||||
{
|
||||
_written = 0;
|
||||
_truncated = false;
|
||||
_limit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this redirector has stopped copying lines due to the size limit being exceeded.
|
||||
*/
|
||||
public boolean isTruncated ()
|
||||
{
|
||||
return _truncated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the redirector is still active. Normally this indicates that the launched
|
||||
* process is still running.
|
||||
*/
|
||||
public boolean isRunning ()
|
||||
{
|
||||
return _reader != null;
|
||||
}
|
||||
|
||||
protected void copyLoop ()
|
||||
{
|
||||
String line;
|
||||
try {
|
||||
while ((line = _reader.readLine()) != null) {
|
||||
int length = line.length();
|
||||
boolean showTrunc = false;
|
||||
|
||||
synchronized (this) {
|
||||
if (_truncated) {
|
||||
line = null;
|
||||
} else if (_limit > 0 && _written + length > _limit) {
|
||||
_truncated = true;
|
||||
showTrunc = true;
|
||||
line = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (line != null) {
|
||||
_target.info(line); // this should get prefixed by the thread name
|
||||
_written += length;
|
||||
|
||||
} else if (showTrunc) {
|
||||
DateFormat format =
|
||||
DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
|
||||
_target.info(
|
||||
format.format(new Date()) +
|
||||
": Size limit reached, suppressing further output");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warning("Failed to read bureau output", "bureauId", _bureauId, e);
|
||||
} finally {
|
||||
StreamUtil.close(_reader);
|
||||
_reader = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String _bureauId;
|
||||
protected BufferedReader _reader;
|
||||
protected int _limit;
|
||||
protected int _written;
|
||||
protected boolean _truncated;
|
||||
|
||||
protected static Logger _target = Logger.getLogger(BureauLogRedirector.class);
|
||||
}
|
||||
Reference in New Issue
Block a user