Files
narya/src/java/com/threerings/presents/server/InvocationManager.java
T
Michael Bayne 804505890f We love to revamp! Created a set of listener interfaces which are used
with distributed objects rather than having a single handleEvent() by
which all subscribers are forced to hear about all events. Now one
subscribes separately and then adds onesself as any of a few types of
listener once they have access to the subscribed object reference.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@439 542714f4-19e9-0310-aa3c-eee0fc999fb1
2001-10-12 00:03:03 +00:00

171 lines
6.1 KiB
Java

//
// $Id: InvocationManager.java,v 1.9 2001/10/12 00:03:03 mdb Exp $
package com.threerings.presents.server;
import java.lang.reflect.Method;
import java.util.HashMap;
import com.threerings.presents.Log;
import com.threerings.presents.dobj.*;
import com.threerings.presents.data.*;
import com.threerings.presents.util.ClassUtil;
/**
* The invocation services provide client to server invocations (service
* requests) and server to client invocations (responses and
* notifications). Via this mechanism, the client can make requests of the
* server, be notified of its response and the server can asynchronously
* 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).
*
* <p> The server invocation manager listens for invocation requests from
* the client and passes them on to the invocation provider registered for
* the requested invocation module. It also provides a mechanism by which
* responses and asynchronous notification invocations can be delivered to
* the client.
*/
public class InvocationManager
implements Subscriber, MessageListener
{
public InvocationManager (DObjectManager omgr)
{
_omgr = omgr;
// create the object on which we'll listen for invocation requests
omgr.createObject(DObject.class, this, true);
}
public int getOid ()
{
return _invoid;
}
/**
* 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.
*
* <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.).
*/
public void sendNotification (
int cloid, String module, String procedure, Object[] args)
{
// 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);
}
// construct a message event and deliver it
MessageEvent nevt = new MessageEvent(
cloid, InvocationObject.NOTIFICATION_NAME, nargs);
PresentsServer.omgr.postEvent(nevt);
}
public void objectAvailable (DObject object)
{
// this must be our invocation object
_invoid = object.getOid();
// add ourselves as a message listener
object.addListener(this);
}
public void requestFailed (int oid, ObjectAccessException cause)
{
// if for some reason we were unable to create our invocation
// object, we'll end up here
Log.warning("Unable to create invocation object " +
"[reason=" + cause + "].");
_invoid = -1;
}
public void messageReceived (MessageEvent event)
{
// make sure the name is proper just for sanity's sake
if (!event.getName().equals(InvocationObject.REQUEST_NAME)) {
return;
}
// 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();
margs[0] = PresentsServer.omgr.getObject(cloid);
// make sure the client is still around
if (margs[0] == null) {
Log.warning("Client no longer around for invocation provider " +
"request [module=" + module +
", proc=" + procedure + ", cloid=" + cloid + "].");
return;
}
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 (Exception e) {
Log.warning("Error invoking invocation procedure " +
"[provider=" + provider +
", method=" + procmeth + "].");
Log.logStackTrace(e);
}
}
protected DObjectManager _omgr;
protected int _invoid;
protected HashMap _providers = new HashMap();
protected HashMap _methcache = new HashMap();
}