Modified invocation request procedure to provide a reference to the client
object that initiated the request. Implemented the invocation notification side of things. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@76 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
//
|
||||
// $Id: InvocationDirector.java,v 1.3 2001/07/19 18:08:20 mdb Exp $
|
||||
// $Id: InvocationDirector.java,v 1.4 2001/07/19 19:18:06 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.client;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.cocktail.cher.Log;
|
||||
import com.threerings.cocktail.cher.data.*;
|
||||
import com.threerings.cocktail.cher.dobj.*;
|
||||
@@ -84,7 +86,7 @@ public class InvocationManager
|
||||
|
||||
// create a message event on the invocation manager object
|
||||
MessageEvent event = new MessageEvent(
|
||||
_imoid, InvocationObject.MESSAGE_NAME, iargs);
|
||||
_imoid, InvocationObject.REQUEST_NAME, iargs);
|
||||
|
||||
// if we have a response target, register that for later receipt
|
||||
// of the response
|
||||
@@ -96,6 +98,15 @@ public class InvocationManager
|
||||
_omgr.postEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the supplied invocation receiver instance as the handler
|
||||
* for all invocation notifications for the specified module.
|
||||
*/
|
||||
public void registerReceiver (String module, InvocationReceiver receiver)
|
||||
{
|
||||
_receivers.put(module, receiver);
|
||||
}
|
||||
|
||||
public void objectAvailable (DObject object)
|
||||
{
|
||||
// let the client know that we're ready to go now that we've got
|
||||
@@ -123,21 +134,29 @@ public class InvocationManager
|
||||
|
||||
// and only those of proper name
|
||||
MessageEvent mevt = (MessageEvent)event;
|
||||
if (!mevt.getName().equals(InvocationObject.MESSAGE_NAME)) {
|
||||
return true;
|
||||
String name = mevt.getName();
|
||||
if (name.equals(InvocationObject.RESPONSE_NAME)) {
|
||||
handleInvocationResponse(mevt.getArgs());
|
||||
} else if (name.equals(InvocationObject.NOTIFICATION_NAME)) {
|
||||
handleInvocationNotification(mevt.getArgs());
|
||||
}
|
||||
|
||||
// we've got an invocation response, so we extract the args and
|
||||
// process it
|
||||
Object[] args = mevt.getArgs();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an invocation response message.
|
||||
*/
|
||||
protected void handleInvocationResponse (Object[] args)
|
||||
{
|
||||
String name = (String)args[0];
|
||||
int invid = ((Integer)args[1]).intValue();
|
||||
|
||||
Object rsptarg = _targets.get(invid);
|
||||
if (rsptarg == null) {
|
||||
Log.warning("No target for invocation response " +
|
||||
"[rsp=" + mevt + "].");
|
||||
return true;
|
||||
"[args=" + StringUtil.toString(args) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// prune the method arguments from the full message arguments
|
||||
@@ -151,7 +170,7 @@ public class InvocationManager
|
||||
Log.warning("Unable to resolve response method " +
|
||||
"[target=" + rsptarg.getClass().getName() +
|
||||
", method=" + mname + "].");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// and invoke it
|
||||
@@ -162,8 +181,46 @@ public class InvocationManager
|
||||
"[target=" + rsptarg + ", method=" + rspmeth +
|
||||
", error=" + e + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
/**
|
||||
* Processes an invocation notification message.
|
||||
*/
|
||||
protected void handleInvocationNotification (Object[] args)
|
||||
{
|
||||
String module = (String)args[0];
|
||||
String proc = (String)args[1];
|
||||
|
||||
InvocationReceiver receiver =
|
||||
(InvocationReceiver)_receivers.get(module);
|
||||
if (receiver == null) {
|
||||
Log.warning("No receiver registered for notification " +
|
||||
"[args=" + StringUtil.toString(args) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// prune the method arguments from the full message arguments
|
||||
Object[] nargs = new Object[args.length-2];
|
||||
System.arraycopy(args, 2, nargs, 0, nargs.length);
|
||||
|
||||
// and invoke the receiver method
|
||||
String mname = "handle" + proc + "Notification";
|
||||
Method rspmeth = ClassUtil.getMethod(mname, receiver, _methcache);
|
||||
if (rspmeth == null) {
|
||||
Log.warning("Unable to resolve receiver method " +
|
||||
"[target=" + receiver.getClass().getName() +
|
||||
", method=" + mname + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// and invoke it
|
||||
try {
|
||||
rspmeth.invoke(receiver, nargs);
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error invoking receiver method " +
|
||||
"[receiver=" + receiver + ", method=" + rspmeth +
|
||||
", error=" + e + "].");
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized int nextInvocationId ()
|
||||
@@ -189,5 +246,6 @@ public class InvocationManager
|
||||
|
||||
protected int _invocationId;
|
||||
protected IntMap _targets = new IntMap();
|
||||
protected HashMap _receivers = new HashMap();
|
||||
protected HashMap _methcache = new HashMap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// $Id: InvocationReceiver.java,v 1.1 2001/07/19 19:18:06 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.client;
|
||||
|
||||
/**
|
||||
* Classes registered to process invocation notifications should extend
|
||||
* the invocation receiver class and register themselves with the
|
||||
* invocation manager. Because the invocation notification procedures are
|
||||
* looked up using reflection, there are no methods to override in the
|
||||
* receiver class, but it serves as a useful point for documentation and
|
||||
* as a useful indicator that the derived class in question is actually
|
||||
* serving as an invocation receiver.
|
||||
*
|
||||
* <p> Invocation notifications are identified by a module name and a
|
||||
* procedure name. The module name identifies which invocation receiver
|
||||
* instance will receive the notification. Receivers are registered with
|
||||
* the invocation manager as handling all notification procedures for a
|
||||
* particular module. The notification procedure name is used to construct
|
||||
* a method name which is then reflected and invoked. The name
|
||||
* construction is as follows: a notification message requesting the
|
||||
* invocation of a procedure named <code>Tell</code> will result in a
|
||||
* method named <code>handleTellNotification</code> being invoked on the
|
||||
* invocation receiver instance. The signature of that method is defined
|
||||
* by the arguments supplied with the invocation notification message.
|
||||
* These arguments must always be of the same type and must exactly match
|
||||
* the signature of the implementing method (with the standard reflection
|
||||
* argument type conversion process taken into account).
|
||||
*
|
||||
* @see InvocationManager#registerReceiver
|
||||
*/
|
||||
public class InvocationReceiver
|
||||
{
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: InvocationObject.java,v 1.1 2001/07/19 05:56:20 mdb Exp $
|
||||
// $Id: InvocationObject.java,v 1.2 2001/07/19 19:18:06 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.data;
|
||||
|
||||
@@ -14,8 +14,20 @@ import com.threerings.cocktail.cher.dobj.DObject;
|
||||
public class InvocationObject extends DObject
|
||||
{
|
||||
/**
|
||||
* This constant is used to identify messages on both ends of the
|
||||
* invocation services.
|
||||
* This constant is used to identify invocation requests sent to the
|
||||
* server.
|
||||
*/
|
||||
public static final String MESSAGE_NAME = "invoke";
|
||||
public static final String REQUEST_NAME = "invreq";
|
||||
|
||||
/**
|
||||
* This constant is used to identify invocation responses sent to the
|
||||
* client.
|
||||
*/
|
||||
public static final String RESPONSE_NAME = "invrsp";
|
||||
|
||||
/**
|
||||
* This constant is used to identify invocation notifications sent to
|
||||
* the client.
|
||||
*/
|
||||
public static final String NOTIFICATION_NAME = "invnot";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: InvocationManager.java,v 1.4 2001/07/19 18:08:20 mdb Exp $
|
||||
// $Id: InvocationManager.java,v 1.5 2001/07/19 19:18:07 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.server;
|
||||
|
||||
@@ -56,6 +56,39 @@ public class InvocationManager
|
||||
_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);
|
||||
CherServer.omgr.postEvent(nevt);
|
||||
}
|
||||
|
||||
public void objectAvailable (DObject object)
|
||||
{
|
||||
// this must be our invocation object
|
||||
@@ -82,7 +115,7 @@ public class InvocationManager
|
||||
|
||||
// make sure the name is proper just for sanity's sake
|
||||
MessageEvent mevt = (MessageEvent)event;
|
||||
if (!mevt.getName().equals(InvocationObject.MESSAGE_NAME)) {
|
||||
if (!mevt.getName().equals(InvocationObject.REQUEST_NAME)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,8 +135,17 @@ public class InvocationManager
|
||||
}
|
||||
|
||||
// prune the method arguments from the full message arguments
|
||||
Object[] margs = new Object[args.length-3];
|
||||
System.arraycopy(args, 3, margs, 0, margs.length);
|
||||
Object[] margs = new Object[args.length-2];
|
||||
int cloid = mevt.getSourceOid();
|
||||
margs[0] = CherServer.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 true;
|
||||
}
|
||||
System.arraycopy(args, 3, margs, 1, args.length-3);
|
||||
|
||||
// look up the method that will handle this procedure
|
||||
String mname = "handle" + procedure + "Request";
|
||||
@@ -131,7 +173,7 @@ public class InvocationManager
|
||||
rargs[1] = invid;
|
||||
// and create a message event for delivery to the client
|
||||
MessageEvent revt = new MessageEvent(
|
||||
mevt.getSourceOid(), InvocationObject.MESSAGE_NAME, rargs);
|
||||
mevt.getSourceOid(), InvocationObject.RESPONSE_NAME, rargs);
|
||||
// and ship it off
|
||||
CherServer.omgr.postEvent(revt);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: InvocationProvider.java,v 1.1 2001/07/19 18:08:20 mdb Exp $
|
||||
// $Id: InvocationProvider.java,v 1.2 2001/07/19 19:18:07 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.server;
|
||||
|
||||
@@ -12,9 +12,27 @@ package com.threerings.cocktail.cher.server;
|
||||
* 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. If the arguments
|
||||
* do not match, a reflection error will happen when trying to invoke the
|
||||
* method and the whole request will fail.
|
||||
* 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 Object[] handleTestRequest (ClientObject source,
|
||||
* 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: PresentsDObjectMgr.java,v 1.6 2001/06/13 05:17:55 mdb Exp $
|
||||
// $Id: PresentsDObjectMgr.java,v 1.7 2001/07/19 19:18:07 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.server;
|
||||
|
||||
@@ -74,6 +74,20 @@ public class CherDObjectMgr implements DObjectManager
|
||||
// nothing to do here, our objects live forever!
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object in the object table with the specified oid or
|
||||
* null if no object has that oid. Be sure only to call this function
|
||||
* from the dobjmgr thread and not to do anything funny with the
|
||||
* object. If subscription is desired, use
|
||||
* <code>subscribeToObject()</code>.
|
||||
*
|
||||
* @see #subscribeToObject
|
||||
*/
|
||||
public DObject getObject (int oid)
|
||||
{
|
||||
return (DObject)_objects.get(oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the dobjmgr event loop until it is requested to exit. This
|
||||
* should be called from the main application thread.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: PresentsServer.java,v 1.7 2001/07/19 07:48:25 mdb Exp $
|
||||
// $Id: PresentsServer.java,v 1.8 2001/07/19 19:18:07 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.server;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class CherServer
|
||||
public static ClientManager clmgr;
|
||||
|
||||
/** The distributed object manager. */
|
||||
public static DObjectManager omgr;
|
||||
public static CherDObjectMgr omgr;
|
||||
|
||||
/** The invocation manager. */
|
||||
public static InvocationManager invmgr;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: TestClient.java,v 1.6 2001/07/19 18:08:20 mdb Exp $
|
||||
// $Id: TestClient.java,v 1.7 2001/07/19 19:18:06 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.client.test;
|
||||
|
||||
@@ -38,6 +38,9 @@ public class TestClient
|
||||
Log.info("Client did logon [client=" + client + "].");
|
||||
// try subscribing to a test object
|
||||
client.getDObjectManager().subscribeToObject(2, this);
|
||||
// register our test notification receiver
|
||||
client.getInvocationManager().registerReceiver(TestService.MODULE,
|
||||
new TestReceiver());
|
||||
// issue a test invocation request
|
||||
TestService.test(client, "foo", 1, this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// $Id: TestReceiver.java,v 1.1 2001/07/19 19:18:06 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.client.test;
|
||||
|
||||
import com.threerings.cocktail.cher.Log;
|
||||
import com.threerings.cocktail.cher.client.InvocationReceiver;
|
||||
|
||||
public class TestReceiver extends InvocationReceiver
|
||||
{
|
||||
public void handleTestNotification (int one, String two)
|
||||
{
|
||||
Log.info("Received tell notification [one=" + one +
|
||||
", two=" + two + "].");
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
//
|
||||
// $Id: TestProvider.java,v 1.3 2001/07/19 18:08:20 mdb Exp $
|
||||
// $Id: TestProvider.java,v 1.4 2001/07/19 19:18:07 mdb Exp $
|
||||
|
||||
package com.threerings.cocktail.cher.server.test;
|
||||
|
||||
import com.threerings.cocktail.cher.Log;
|
||||
import com.threerings.cocktail.cher.client.test.TestService;
|
||||
import com.threerings.cocktail.cher.data.ClientObject;
|
||||
import com.threerings.cocktail.cher.server.CherServer;
|
||||
import com.threerings.cocktail.cher.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
@@ -11,9 +14,17 @@ import com.threerings.cocktail.cher.server.InvocationProvider;
|
||||
*/
|
||||
public class TestProvider extends InvocationProvider
|
||||
{
|
||||
public Object[] handleTestRequest (String one, int two)
|
||||
public Object[] handleTestRequest (
|
||||
ClientObject source, String one, int two)
|
||||
{
|
||||
Log.info("Test request [one=" + one + ", two=" + two + "].");
|
||||
|
||||
// issue a test notification just for kicks
|
||||
Object[] args = new Object[] { new Integer(1), "two" };
|
||||
CherServer.invmgr.sendNotification(
|
||||
source.getOid(), TestService.MODULE, "Test", args);
|
||||
|
||||
// and issue a response to this invocation request
|
||||
return createResponse("TestSucceeded", one, new Integer(two));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user