The great invocation services rethink of 2002! Rearchitected the remote

method invocation services and converted everything to the new style.
Could this be my biggest checkin ever?


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1642 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2002-08-14 19:08:01 +00:00
parent 4481c5f835
commit e54a4d41f4
161 changed files with 6083 additions and 2805 deletions
@@ -0,0 +1,40 @@
//
// $Id: InvocationDispatcher.java,v 1.1 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
import com.samskivert.util.StringUtil;
import com.threerings.presents.Log;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
/**
* Provides the base class via which invocation service requests are
* dispatched.
*/
public abstract class InvocationDispatcher
{
/** The invocation provider for whom we're dispatching. */
public InvocationProvider provider;
/**
* Creates an instance of the appropriate {@link InvocationMarshaller}
* derived class for use with this dispatcher.
*/
public abstract InvocationMarshaller createMarshaller ();
/**
* Dispatches the specified method to our provider.
*/
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
Log.warning("Requested to dispatch unknown method " +
"[provider=" + provider +
", sourceOid=" + source.getOid() +
", methodId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
}
}
@@ -0,0 +1,19 @@
//
// $Id: InvocationException.java,v 1.1 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
/**
* Used to report failures when executing service requests.
*/
public class InvocationException extends Exception
{
/**
* Constructs an invocation exception with the supplied cause code
* string.
*/
public InvocationException (String cause)
{
super(cause);
}
}
@@ -1,19 +1,30 @@
//
// $Id: InvocationManager.java,v 1.11 2002/04/17 18:20:04 mdb Exp $
// $Id: InvocationManager.java,v 1.12 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.ArrayList;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil;
import com.threerings.io.Streamable;
import com.threerings.util.StreamableArrayList;
import com.threerings.presents.Log;
import com.threerings.presents.dobj.*;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller.ListenerMarshaller;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.data.InvocationObject;
import com.threerings.presents.util.ClassUtil;
import com.threerings.presents.dobj.DEvent;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.EventListener;
import com.threerings.presents.dobj.InvocationRequestEvent;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.dobj.Subscriber;
import com.threerings.presents.dobj.Subscriber;
/**
* The invocation services provide client to server invocations (service
@@ -23,11 +34,12 @@ import com.threerings.presents.util.ClassUtil;
* invoke code on the client.
*
* <p> Invocations are like remote procedure calls in that they are named
* and take arguments. They are simple in that the arguments can only be
* of a small set of supported types (the set of distributed object field
* types) and there is no special facility provided for referencing
* non-local objects (it is assumed that the distributed object facility
* will already be in use for any objects that should be shared).
* and take arguments. All arguments must be {@link Streamable} objects,
* primitive types, or String objects. All arguments are passed by value
* (by serializing and unserializing the arguments); there is no special
* facility provided for referencing non-local objects (it is assumed that
* the distributed object facility will already be in use for any objects
* that should be shared).
*
* <p> The server invocation manager listens for invocation requests from
* the client and passes them on to the invocation provider registered for
@@ -36,9 +48,19 @@ import com.threerings.presents.util.ClassUtil;
* the client.
*/
public class InvocationManager
implements Subscriber, MessageListener
implements Subscriber, EventListener
{
public InvocationManager (DObjectManager omgr)
/** The list of services that are to be provided to clients at boot
* time. Don't mess with this list! */
public StreamableArrayList bootlist = new StreamableArrayList();
/**
* Constructs an invocation manager which will use the supplied
* distributed object manager to operate its invocation
* services. Generally only one invocation manager should be
* operational in a particular system.
*/
public InvocationManager (RootDObjectManager omgr)
{
_omgr = omgr;
@@ -52,45 +74,42 @@ public class InvocationManager
}
/**
* Registers the supplied invocation provider instance as the handler
* for all invocation requests for the specified module.
*/
public void registerProvider (String module, InvocationProvider provider)
{
_providers.put(module, provider);
}
/**
* Delivers an invocation notification to the specified client. The
* <code>module</code> argument selects which
* <code>InvocationReceiver</code> will be invoked and the
* <code>procedure</code> argument indicates which method will be
* invoked on that receiver.
* Registers the supplied invocation dispatcher, returning a
* marshaller that can be used to send requests to the provider for
* whom the dispatcher is proxying.
*
* <p> The method is constructed as follows: a procedure name of
* <code>Tell</code> will result in a method call to
* <code>handleTellNotification</code>. The arguments provided with
* the notification define the necessary signature of that method,
* according to the argument conversion rules defined by the
* reflection services (<code>Integer</code> is converted to
* <code>int</code>, etc.).
* @param dispatcher the dispatcher to be registered.
* @param bootstrap if true, the service instance will be added to the
* list of invocation service objects provided to the client in the
* bootstrap data.
*/
public void sendNotification (
int cloid, String module, String procedure, Object[] args)
public InvocationMarshaller registerDispatcher (
InvocationDispatcher dispatcher, boolean bootstrap)
{
// package up the arguments
int alength = (args != null) ? args.length : 0;
Object[] nargs = new Object[alength + 2];
nargs[0] = module;
nargs[1] = procedure;
if (args != null) {
System.arraycopy(args, 0, nargs, 2, alength);
// get the next invocation code
int invCode = nextInvCode();
// create the marshaller and initialize it
InvocationMarshaller marsh = dispatcher.createMarshaller();
marsh.init(_invoid, invCode);
// if we haven't yet finished our own initialization, we need to
// throw this dispatcher on a queue so that we can fill in its
// invocation oid when we know what it should be
if (_invoid == -1) {
_lateInitQueue.add(marsh);
}
// construct a message event and deliver it
MessageEvent nevt = new MessageEvent(
cloid, InvocationObject.NOTIFICATION_NAME, nargs);
PresentsServer.omgr.postEvent(nevt);
// register the dispatcher
_dispatchers.put(invCode, dispatcher);
// if it's a bootstrap service, slap it in the list
if (bootstrap) {
bootlist.add(marsh);
}
// Log.info("Registered service [marsh=" + marsh + "].");
return marsh;
}
public void objectAvailable (DObject object)
@@ -100,6 +119,15 @@ public class InvocationManager
// add ourselves as a message listener
object.addListener(this);
// let any early registered marshallers know about our invoid
while (_lateInitQueue.size() > 0) {
InvocationMarshaller marsh = (InvocationMarshaller)
_lateInitQueue.remove(0);
marsh.setInvocationOid(_invoid);
}
// Log.info("Created invocation service object [oid=" + _invoid + "].");
}
public void requestFailed (int oid, ObjectAccessException cause)
@@ -111,85 +139,118 @@ public class InvocationManager
_invoid = -1;
}
public void messageReceived (MessageEvent event)
// documentation inherited from interface
public void eventReceived (DEvent event)
{
// make sure the name is proper just for sanity's sake
if (!event.getName().equals(InvocationObject.REQUEST_NAME)) {
return;
}
// Log.info("Event received " + event + ".");
// we've got an invocation request, so we process it
Object[] args = event.getArgs();
String module = (String)args[0];
String procedure = (String)args[1];
Integer invid = (Integer)args[2];
// locate a provider for this module
InvocationProvider provider =
(InvocationProvider)_providers.get(module);
if (provider == null) {
Log.warning("No provider registered for invocation request " +
"[evt=" + event + "].");
return;
}
// prune the method arguments from the full message arguments
Object[] margs = new Object[args.length-1];
int cloid = event.getSourceOid();
ClientObject source = (ClientObject)
PresentsServer.omgr.getObject(cloid);
// make sure the client is still around
if (source == null) {
Log.warning("Client no longer around for invocation provider " +
"request [module=" + module +
", proc=" + procedure + ", cloid=" + cloid + "].");
return;
}
margs[0] = source;
System.arraycopy(args, 2, margs, 1, args.length-2);
// look up the method that will handle this procedure
String mname = "handle" + procedure + "Request";
Method procmeth = ClassUtil.getMethod(mname, provider, _methcache);
if (procmeth == null) {
Log.warning("Unable to resolve provider procedure " +
"[provider=" + provider.getClass().getName() +
", method=" + mname + "].");
return;
}
// and invoke it
try {
procmeth.invoke(provider, margs);
} catch (InvocationTargetException ite) {
Throwable te = ite.getTargetException();
if (te instanceof ServiceFailedException) {
// automatically generate a <foo>Failed response
provider.sendResponse(source, invid.intValue(),
procedure + FAILED_SUFFIX,
te.getMessage());
} else {
Log.warning("Invocation procedure failed " +
"[provider=" + provider +
", method=" + procmeth +
", args=" + StringUtil.toString(margs) + "].");
Log.logStackTrace(te);
}
} catch (Exception e) {
Log.warning("Error invoking invocation procedure " +
"[provider=" + provider +
", method=" + procmeth + "].");
Log.logStackTrace(e);
if (event instanceof InvocationRequestEvent) {
InvocationRequestEvent ire = (InvocationRequestEvent)event;
dispatchRequest(ire.getSourceOid(), ire.getInvCode(),
ire.getMethodId(), ire.getArgs());
}
}
protected DObjectManager _omgr;
protected int _invoid;
protected HashMap _providers = new HashMap();
protected HashMap _methcache = new HashMap();
/**
* Called when we receive an invocation request message. Dispatches
* the request to the appropriate invocation provider via the
* registered invocation dispatcher.
*/
protected void dispatchRequest (
int clientOid, int invCode, int methodId, Object[] args)
{
// make sure the client is still around
ClientObject source = (ClientObject)_omgr.getObject(clientOid);
if (source == null) {
Log.warning("Client no longer around for invocation " +
"request [clientOid=" + clientOid +
", code=" + invCode + ", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
return;
}
// look up the dispatcher
InvocationDispatcher disp = (InvocationDispatcher)
_dispatchers.get(invCode);
if (disp == null) {
Log.warning("Received invocation request for which we have " +
"no registered dispatcher [code=" + invCode +
", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
return;
}
// scan the args, initializing any listeners and keeping track of
// the "primary" listener
ListenerMarshaller rlist = null;
int acount = args.length;
for (int ii = 0; ii < acount; ii++) {
Object arg = args[ii];
if (arg instanceof ListenerMarshaller) {
ListenerMarshaller list = (ListenerMarshaller)arg;
list.omgr = _omgr;
// keep track of the listener we'll inform if anything
// goes horribly awry
if (rlist == null) {
rlist = list;
}
}
}
// Log.info("Dispatching invreq [caller=" + source.who() +
// ", methId=" + methodId +
// ", args=" + StringUtil.toString(args) + "].");
// dispatch the request
try {
disp.dispatchRequest(source, methodId, args);
} catch (InvocationException ie) {
if (rlist != null) {
rlist.requestFailed(ie.getMessage());
} else {
Log.warning("Service request failed but we've got no " +
"listener to inform of the failure " +
"[clientOid=" + clientOid + ", code=" + invCode +
", dispatcher=" + disp + ", methodId=" + methodId +
", args=" + StringUtil.toString(args) +
", error=" + ie + "].");
}
} catch (Throwable t) {
Log.warning("Dispatcher choked [disp=" + disp +
", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
Log.logStackTrace(t);
}
}
/**
* Used to generate monotonically increasing provider ids.
*/
protected synchronized int nextInvCode ()
{
return _invCode++;
}
/** The distributed object manager with which we're working. */
protected RootDObjectManager _omgr;
/** The object id of the object on which we receive invocation service
* requests. */
protected int _invoid = -1;
/** Used to generate monotonically increasing provider ids. */
protected int _invCode;
/** A table of invocation dispatchers each mapped by a unique code. */
protected HashIntMap _dispatchers = new HashIntMap();
/** Used to keep track of marshallers registered before we had our
* invocation object so that we can fill their invocation id in
* belatedly. */
protected ArrayList _lateInitQueue = new ArrayList();
/** The text that is appended to the procedure name when automatically
* generating a failure response. */
@@ -1,151 +1,11 @@
//
// $Id: InvocationProvider.java,v 1.7 2002/04/17 18:20:04 mdb Exp $
// $Id: InvocationProvider.java,v 1.8 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
import com.samskivert.util.StringUtil;
import com.threerings.presents.Log;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationObject;
import com.threerings.presents.dobj.MessageEvent;
/**
* Invocation providers should extend this class when implementing
* invocation services. Because the service procedures are identified by
* strings and the methods that are invoked are looked up via reflection,
* the derived class doesn't override or implement any particular method.
* However, the procedure names are still restricted. For example, a
* procedure identified by the name <code>Tell</code> would result in the
* invocation of a method named <code>handleTellRequest</code>. The
* arguments to that method would be defined by the arguments that
* accompanied the <code>Tell</code> invocation request along with the
* client object of the client that made the request. Specifically:
*
* <pre>
* // client makes request
* Object[] args = new Object[] { "one", new Integer(2) };
* invmgr.invoke(MODULE, "Test", args, rsptarget);
*
* // provider registered for MODULE should look like:
* public class TestProvider extends InvocationProvider
* {
* public void handleTestRequest (ClientObject source, int invid,
* String one, int two)
* {
* // ...
* }
* }
* </pre>
*
* If the arguments do not match, a reflection error will happen when
* trying to invoke the method and the whole request will fail.
*
* <p> Invocation procedures must also package up their response in a
* particular way which is through the use of the
* <code>sendResponse</code> methods. These take a response identifier
* (which determines the name of the method that will be invoked on the
* response target object provided in the client) and a variable number of
* arguments. If a response was created with the identifier
* <code>TellFailed</code>, that would result in the method
* <code>handleTellFailed</code> being invoked on the response target
* object in the client. Again the arguments much match exactly and follow
* the reflection rules for automatic conversion of primitive types
* (supply an <code>Integer</code> object for <code>int</code> params,
* etc.).
*
* <p> Note that if an invocation service method throws a {@link
* ServiceFailedException}, the invocation manager will automatically
* issue a response to the client with the string <code>Failed</code>
* appended to the request method. For example, if the client issues a
* request for <code>Foo</code> which results in a call to
* <code>handleFooRequest</code>, which throws a service failed exception,
* the server will automatically issue a failure response named
* <code>FooFailed</code> with the single argument being the
* <code>reason</code> string provided to the constructor of the service
* failed exception. The caller would then implement:
*
* <pre>
* public void handleFooFailed (int invid, String reason)
* </pre>
*
* to handle the failure.
* All invocation providers must implement this placeholder interface.
*/
public class InvocationProvider
public interface InvocationProvider
{
/**
* Delivers an invocation response properly configured with the
* supplied name and no arguments.
*/
protected void sendResponse (ClientObject source, int invid, String name)
{
deliverResponse(source, new Object[] { name, new Integer(invid) });
}
/**
* Delivers an invocation response properly configured with the
* supplied name and single argument.
*/
protected void sendResponse (ClientObject source, int invid,
String name, Object arg)
{
Object[] args = new Object[] { name, new Integer(invid), arg };
deliverResponse(source, args);
}
/**
* Delivers an invocation response properly configured with the
* supplied name and two arguments.
*/
protected void sendResponse (ClientObject source, int invid,
String name, Object arg1, Object arg2)
{
Object[] args = new Object[] {
name, new Integer(invid), arg1, arg2 };
deliverResponse(source, args);
}
/**
* Delivers an invocation response properly configured with the
* supplied name and three arguments.
*/
protected void sendResponse (ClientObject source, int invid,
String name, Object arg1, Object arg2,
Object arg3)
{
Object[] args = new Object[] {
name, new Integer(invid), arg1, arg2, arg3 };
deliverResponse(source, args);
}
/**
* Delivers an invocation response properly configured with the
* supplied name and varying number of arguments.
*/
protected void sendResponse (ClientObject source, int invid,
String name, Object[] args)
{
Object[] rargs = new Object[args.length+2];
rargs[0] = name;
rargs[1] = new Integer(invid);
System.arraycopy(args, 0, rargs, 2, args.length);
deliverResponse(source, rargs);
}
protected void deliverResponse (ClientObject source, Object[] args)
{
// make sure they didn't go away in the meanwhile
if (source.isActive()) {
// create the response event
MessageEvent mevt = new MessageEvent(
source.getOid(), InvocationObject.RESPONSE_NAME, args);
// and ship it off
PresentsServer.omgr.postEvent(mevt);
} else {
Log.warning("Dropping invrsp due to disappearing client " +
"[cloid=" + source.getOid() +
", args=" + StringUtil.toString(args) + "].");
}
}
}
@@ -0,0 +1,45 @@
//
// $Id: InvocationSender.java,v 1.1 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
import com.samskivert.util.StringUtil;
import com.threerings.presents.Log;
import com.threerings.presents.client.InvocationReceiver.Registration;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.InvocationNotificationEvent;
/**
* Provides basic functionality used by all invocation sender classes.
*/
public abstract class InvocationSender
{
/**
* Requests that the specified invocation notification be packaged up
* and sent to the supplied target client.
*/
public static void sendNotification (
ClientObject target, String receiverCode, int methodId, Object[] args)
{
// convert the receiver hash id into the code used on this
// specific client
Registration rreg = (Registration)target.receivers.get(receiverCode);
if (rreg == null) {
Log.warning("Unable to locate registered receiver for " +
"invocation service notification [target=" + target +
", code=" + receiverCode + ", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
} else {
// Log.info("Sending notification [target=" + target +
// ", code=" + receiverCode + ", methodId=" + methodId +
// ", args=" + StringUtil.toString(args) + "].");
// create and dispatch an invocation notification event
target.postEvent(
new InvocationNotificationEvent(
target.getOid(), rreg.receiverId, methodId, args));
}
}
}
@@ -1,5 +1,5 @@
//
// $Id: PresentsClient.java,v 1.34 2002/07/23 05:52:49 mdb Exp $
// $Id: PresentsClient.java,v 1.35 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
@@ -324,8 +324,8 @@ public class PresentsClient
// give them the client object id
data.clientOid = _clobj.getOid();
// give them the invocation oid
data.invOid = PresentsServer.invmgr.getOid();
// fill in the list of bootstrap services
data.services = PresentsServer.invmgr.bootlist;
}
/**
@@ -1,5 +1,5 @@
//
// $Id: PresentsServer.java,v 1.23 2002/07/25 20:24:02 mdb Exp $
// $Id: PresentsServer.java,v 1.24 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
@@ -66,57 +66,57 @@ public class PresentsServer
// initialize the time base services
TimeBaseProvider.init(invmgr, omgr);
// register our invocation service providers
registerProviders(PresentsConfig.getProviders());
// // register our invocation service providers
// registerProviders(PresentsConfig.getProviders());
}
/**
* Registers invocation service providers as parsed from a
* configuration file. Each string in the array should contain an
* expression of the form:
*
* <pre>
* module = provider fully-qualified class name
* </pre>
*
* A comma separated list of these can be specified in the
* configuration file and loaded into a string array easily. These
* providers will be instantiated and registered with the invocation
* manager.
*/
protected void registerProviders (String[] providers)
{
// ignore null arrays to make life easier for the caller
if (providers == null) {
return;
}
// /**
// * Registers invocation service providers as parsed from a
// * configuration file. Each string in the array should contain an
// * expression of the form:
// *
// * <pre>
// * module = provider fully-qualified class name
// * </pre>
// *
// * A comma separated list of these can be specified in the
// * configuration file and loaded into a string array easily. These
// * providers will be instantiated and registered with the invocation
// * manager.
// */
// protected void registerProviders (String[] providers)
// {
// // ignore null arrays to make life easier for the caller
// if (providers == null) {
// return;
// }
for (int i = 0; i < providers.length; i++) {
int eidx = providers[i].indexOf("=");
if (eidx == -1) {
Log.warning("Ignoring bogus provider declaration " +
"[decl=" + providers[i] + "].");
continue;
}
// for (int i = 0; i < providers.length; i++) {
// int eidx = providers[i].indexOf("=");
// if (eidx == -1) {
// Log.warning("Ignoring bogus provider declaration " +
// "[decl=" + providers[i] + "].");
// continue;
// }
String module = providers[i].substring(0, eidx).trim();
String pname = providers[i].substring(eidx+1).trim();
// String module = providers[i].substring(0, eidx).trim();
// String pname = providers[i].substring(eidx+1).trim();
// instantiate the provider class and register it
try {
Class pclass = Class.forName(pname);
InvocationProvider provider = (InvocationProvider)
pclass.newInstance();
invmgr.registerProvider(module, provider);
Log.info("Registered provider [module=" + module +
", provider=" + pname + "].");
// // instantiate the provider class and register it
// try {
// Class pclass = Class.forName(pname);
// InvocationProvider provider = (InvocationProvider)
// pclass.newInstance();
// invmgr.registerProvider(module, provider);
// Log.info("Registered provider [module=" + module +
// ", provider=" + pname + "].");
} catch (Exception e) {
Log.warning("Unable to register provider [module=" + module +
", provider=" + pname + ", error=" + e + "].");
}
}
}
// } catch (Exception e) {
// Log.warning("Unable to register provider [module=" + module +
// ", provider=" + pname + ", error=" + e + "].");
// }
// }
// }
/**
* Starts up all of the server services and enters the main server
@@ -1,45 +0,0 @@
//
// $Id: ServiceFailedException.java,v 1.2 2001/10/11 04:07:53 mdb Exp $
package com.threerings.presents.server;
/**
* An exception class for use in concert with invocation services when
* they need to communicate a failure of some kind and can't use the
* return value.
*
* <p> For example, consider an invitation service:
*
* <pre>
* public class fooManager
* {
* // returns invitation id, throws ServiceFailedException if
* // invitation couldn't be processed
* public int invite (...)
* throws ServiceFailedException
* {
* }
* }
*
* public class fooProvider
* {
* public void handleInviteRequest (...)
* {
* try {
* int inviteId = _mgr.invite(...);
* sendResponse(..., INVITE_RECEIVED, new Integer(inviteId));
*
* } catch (ServiceFailedException sfe) {
* sendResponse(..., INVITE_FAILED, sfe.getMessage());
* }
* }
* }
* </pre>
*/
public class ServiceFailedException extends Exception
{
public ServiceFailedException (String message)
{
super(message);
}
}
@@ -0,0 +1,52 @@
//
// $Id: TimeBaseDispatcher.java,v 1.1 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.TimeBaseService;
import com.threerings.presents.client.TimeBaseService.GotTimeBaseListener;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.data.TimeBaseMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
/**
* Dispatches requests to the {@link TimeBaseProvider}.
*/
public class TimeBaseDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public TimeBaseDispatcher (TimeBaseProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new TimeBaseMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case TimeBaseMarshaller.GET_TIME_OID:
((TimeBaseProvider)provider).getTimeOid(
source,
(String)args[0], (GotTimeBaseListener)args[1]
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,5 +1,5 @@
//
// $Id: TimeBaseProvider.java,v 1.1 2002/05/28 23:14:06 mdb Exp $
// $Id: TimeBaseProvider.java,v 1.2 2002/08/14 19:07:56 mdb Exp $
package com.threerings.presents.server;
@@ -7,6 +7,8 @@ import java.util.HashMap;
import com.samskivert.util.ResultListener;
import com.threerings.presents.Log;
import com.threerings.presents.client.TimeBaseService.GotTimeBaseListener;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.TimeBaseCodes;
import com.threerings.presents.data.TimeBaseObject;
@@ -22,8 +24,8 @@ import com.threerings.presents.dobj.Subscriber;
* network which are expanded based on a shared base time into full time
* stamps.
*/
public class TimeBaseProvider extends InvocationProvider
implements TimeBaseCodes
public class TimeBaseProvider
implements InvocationProvider, TimeBaseCodes
{
/**
* Registers the time provider with the appropriate managers. Called
@@ -35,8 +37,9 @@ public class TimeBaseProvider extends InvocationProvider
_invmgr = invmgr;
_omgr = omgr;
// register an invocation provider instance
_invmgr.registerProvider(MODULE_NAME, new TimeBaseProvider());
// register a provider instance
invmgr.registerDispatcher(
new TimeBaseDispatcher(new TimeBaseProvider()), true);
}
/**
@@ -81,18 +84,17 @@ public class TimeBaseProvider extends InvocationProvider
* Processes a request from a client to fetch the oid of the specified
* time object.
*/
public void handleGetTimeOidRequest (
ClientObject source, int invid, String timeBase)
throws ServiceFailedException
public void getTimeOid (
ClientObject source, String timeBase, GotTimeBaseListener listener)
throws InvocationException
{
// look up the time base object in question
TimeBaseObject time = getTimeBase(timeBase);
if (time == null) {
throw new ServiceFailedException(NO_SUCH_TIME_BASE);
throw new InvocationException(NO_SUCH_TIME_BASE);
}
// and send the response
sendResponse(source, invid, TIME_OID_RESPONSE,
new Integer(time.getOid()));
listener.gotTimeOid(time.getOid());
}
/** Used to keep track of our time base objects. */