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:
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: BasicDirector.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
import com.threerings.presents.util.PresentsContext;
|
||||
|
||||
/**
|
||||
* Handles functionality common to nearly all client directors. They
|
||||
* generally need to be session observers so that they can set themselves
|
||||
* up when the client logs on (by overriding {@link #clientDidLogon}) and
|
||||
* clean up after themselves when the client logs off (by overriding
|
||||
* {@link #clientDidLogoff}).
|
||||
*/
|
||||
public class BasicDirector
|
||||
implements SessionObserver
|
||||
{
|
||||
/**
|
||||
* Derived directors will need to provide the basic director with a
|
||||
* context that it can use to register itself with the necessary
|
||||
* entities.
|
||||
*/
|
||||
protected BasicDirector (PresentsContext ctx)
|
||||
{
|
||||
// listen for session start and end
|
||||
Client client = ctx.getClient();
|
||||
client.addClientObserver(this);
|
||||
|
||||
// if we're already logged on, fire off a call to fetch services
|
||||
if (client.isLoggedOn()) {
|
||||
fetchServices(client);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientDidLogon (Client client)
|
||||
{
|
||||
fetchServices(client);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void clientDidLogoff (Client client)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived directors can override this method and obtain any services
|
||||
* they'll need during their operation via calls to {@link
|
||||
* Client#getService}. It will automatically be called when the client
|
||||
* logs on or when the director is constructed if it is constructed
|
||||
* after the client is already logged on.
|
||||
*/
|
||||
protected void fetchServices (Client client)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Client.java,v 1.27 2002/06/04 16:34:24 mdb Exp $
|
||||
// $Id: Client.java,v 1.28 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
@@ -196,6 +196,40 @@ public class Client
|
||||
return _invdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first bootstrap service that could be located that
|
||||
* implements the supplied {@link InvocationService} derivation.
|
||||
* <code>null</code> is returned if no such service could be found.
|
||||
*/
|
||||
public InvocationService getService (Class sclass)
|
||||
{
|
||||
int scount = _bstrap.services.size();
|
||||
for (int ii = 0; ii < scount; ii++) {
|
||||
InvocationService service = (InvocationService)
|
||||
_bstrap.services.get(ii);
|
||||
if (sclass.isInstance(service)) {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getService} except that a {@link RuntimeException} is
|
||||
* thrown if the service is not available. Useful to avoid redundant
|
||||
* error checking when you know that the shit will hit the fan if a
|
||||
* particular invocation service is not available.
|
||||
*/
|
||||
public InvocationService requireService (Class sclass)
|
||||
{
|
||||
InvocationService isvc = getService(sclass);
|
||||
if (isvc == null) {
|
||||
throw new RuntimeException(sclass.getName() + " isn't available. " +
|
||||
"I can't bear to go on.");
|
||||
}
|
||||
return isvc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the bootstrap data provided to this client
|
||||
* at logon time.
|
||||
@@ -219,7 +253,7 @@ public class Client
|
||||
/**
|
||||
* Returns true if we are logged on, false if we're not.
|
||||
*/
|
||||
public synchronized boolean loggedOn ()
|
||||
public synchronized boolean isLoggedOn ()
|
||||
{
|
||||
// if we have a communicator, we're logged on
|
||||
return (_comm != null);
|
||||
@@ -312,7 +346,7 @@ public class Client
|
||||
notifyObservers(Client.CLIENT_FAILED_TO_LOGON, cause);
|
||||
}
|
||||
};
|
||||
_invdir.init(_comm.getDObjectManager(), _cloid, _bstrap.invOid, rl);
|
||||
_invdir.init(_comm.getDObjectManager(), _cloid, rl);
|
||||
|
||||
// we can't quite call initialization completed at this point
|
||||
// because we need for the invocation director to fully initialize
|
||||
@@ -356,6 +390,8 @@ public class Client
|
||||
public void run () {
|
||||
// clear out our communicator reference
|
||||
_comm = null;
|
||||
// and let our invocation director know we're logged off
|
||||
_invdir.cleanup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// $Id: InvocationDecoder.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.presents.Log;
|
||||
|
||||
/**
|
||||
* Provides the basic functionality used to dispatch invocation
|
||||
* notification events.
|
||||
*/
|
||||
public abstract class InvocationDecoder
|
||||
{
|
||||
/** The receiver for which we're decoding and dipatching
|
||||
* notifications. */
|
||||
public InvocationReceiver receiver;
|
||||
|
||||
/**
|
||||
* Returns the generated hash code that is used to identify this
|
||||
* invocation notification service.
|
||||
*/
|
||||
public abstract String getReceiverCode ();
|
||||
|
||||
/**
|
||||
* Dispatches the specified method to our receiver.
|
||||
*/
|
||||
public void dispatchNotification (int methodId, Object[] args)
|
||||
{
|
||||
Log.warning("Requested to dispatch unknown method " +
|
||||
"[receiver=" + receiver + ", methodId=" + methodId +
|
||||
", args=" + StringUtil.toString(args) + "].");
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,69 @@
|
||||
//
|
||||
// $Id: InvocationDirector.java,v 1.20 2002/04/16 21:37:23 mdb Exp $
|
||||
// $Id: InvocationDirector.java,v 1.21 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.ResultListener;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.Log;
|
||||
import com.threerings.presents.data.*;
|
||||
import com.threerings.presents.dobj.*;
|
||||
import com.threerings.presents.util.ClassUtil;
|
||||
import com.threerings.presents.client.InvocationReceiver.Registration;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.InvocationMarshaller.ListenerMarshaller;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
|
||||
import com.threerings.presents.dobj.DEvent;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
import com.threerings.presents.dobj.EventListener;
|
||||
import com.threerings.presents.dobj.InvocationNotificationEvent;
|
||||
import com.threerings.presents.dobj.InvocationRequestEvent;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
|
||||
/**
|
||||
* 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 client invocation director delivers invocation requests to the
|
||||
* server invocation manager and maps the responses back to the proper
|
||||
* response target objects when they arrive. It also maintains the mapping
|
||||
* of invocation receivers that can receive asynchronous invocation
|
||||
* notifications at any time from the server.
|
||||
* Handles the client side management of the invocation services.
|
||||
*/
|
||||
public class InvocationDirector
|
||||
implements MessageListener
|
||||
implements EventListener
|
||||
{
|
||||
/**
|
||||
* Initializes the invocation director.
|
||||
* Initializes the invocation director. This is called when the client
|
||||
* establishes a connection with the server.
|
||||
*
|
||||
* @param omgr the distributed object manager via which the invocation
|
||||
* manager will send and receive events.
|
||||
* @param cloid the oid of the object on which invocation
|
||||
* notifications as well as invocation responses will be received.
|
||||
* @param imoid the oid of the object on the server to which to
|
||||
* deliver invocation requests.
|
||||
* @param initListener a result listener that will be notified when
|
||||
* the invocation director is up and running (meaning it has
|
||||
* subscribed successfully to the client object and is ready to
|
||||
* process invocation requests); or when initialization has failed.
|
||||
*/
|
||||
public void init (DObjectManager omgr, final int cloid, int imoid,
|
||||
public void init (DObjectManager omgr, final int cloid,
|
||||
final ResultListener initListener)
|
||||
{
|
||||
// keep this for later
|
||||
_omgr = omgr;
|
||||
_imoid = imoid;
|
||||
_cloid = cloid;
|
||||
|
||||
// add ourselves as a subscriber to the client object
|
||||
_omgr.subscribeToObject(cloid, new Subscriber() {
|
||||
public void objectAvailable (DObject object)
|
||||
{
|
||||
// keep a handle on this bad boy
|
||||
_clobj = (ClientObject)object;
|
||||
|
||||
// add ourselves as a message listener
|
||||
object.addListener(InvocationDirector.this);
|
||||
_clobj.addListener(InvocationDirector.this);
|
||||
|
||||
// assign a mapping to already registered receivers
|
||||
assignReceiverIds();
|
||||
|
||||
// let the client know that we're ready to go now that
|
||||
// we've got our subscription to the client object
|
||||
@@ -83,224 +82,243 @@ public class InvocationDirector
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an invocation request to the server. If a response target is
|
||||
* supplied, the response will be delivered to that object by calling
|
||||
* a member function on it whose name is defined in the response
|
||||
* generated by the invocation implementation. In general, this is a
|
||||
* derivative of the invocation procedure name. For example, if the
|
||||
* caller invoked a procedure named <code>Switch</code>, the response
|
||||
* may be delivered via a call to the <code>handleSwitchSuccess</code>
|
||||
* method on the response target object. The signature of that method
|
||||
* would be defined by the arguments provided in the response message
|
||||
* with the addition of a first argument which is the invocation
|
||||
* identifier for this particular request (that is also returned by
|
||||
* this function).
|
||||
*
|
||||
* @param module the name of the invocation module to use.
|
||||
* @param procedure the name of the procedure within that module.
|
||||
* @param args the arguments of the invocation.
|
||||
* @param rsptarget the object that will receive the response, or null
|
||||
* if no response is desired.
|
||||
*
|
||||
* @return a unique identifier associated with this invocation
|
||||
* request. This identifier will be passed as the first argument to
|
||||
* the response function.
|
||||
* Clears out our session information. This is called when the client
|
||||
* ends its session with the server.
|
||||
*/
|
||||
public int invoke (String module, String procedure, Object[] args,
|
||||
Object rsptarget)
|
||||
public void cleanup ()
|
||||
{
|
||||
int invid = nextInvocationId();
|
||||
// wipe our client object, receiver mappings and listener mappings
|
||||
_clobj = null;
|
||||
_receivers.clear();
|
||||
_listeners.clear();
|
||||
|
||||
// if null arguments were supplied, assume zero arguments
|
||||
if (args == null) {
|
||||
args = new Object[0];
|
||||
// also reset our counters
|
||||
_requestId = 0;
|
||||
_receiverId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an invocation notification receiver by way of its
|
||||
* notification event decoder.
|
||||
*/
|
||||
public void registerReceiver (InvocationDecoder decoder)
|
||||
{
|
||||
// add the receiver to the list
|
||||
_reclist.add(decoder);
|
||||
|
||||
// if we're already online, assign a receiver id to this decoder
|
||||
if (_clobj != null) {
|
||||
assignReceiverId(decoder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a receiver registration.
|
||||
*/
|
||||
public void unregisterReceiver (String receiverCode)
|
||||
{
|
||||
// remove the receiver from the list
|
||||
for (Iterator iter = _reclist.iterator(); iter.hasNext(); ) {
|
||||
InvocationDecoder decoder = (InvocationDecoder)iter.next();
|
||||
if (decoder.getReceiverCode().equals(receiverCode)) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// we need an args array for a message that can contain the
|
||||
// invocation names, an invocation id and the invocation arguments
|
||||
Object[] iargs = new Object[args.length+3];
|
||||
iargs[0] = module;
|
||||
iargs[1] = procedure;
|
||||
iargs[2] = new Integer(invid);
|
||||
System.arraycopy(args, 0, iargs, 3, args.length);
|
||||
|
||||
// create a message event on the invocation manager object
|
||||
MessageEvent event = new MessageEvent(
|
||||
_imoid, InvocationObject.REQUEST_NAME, iargs);
|
||||
|
||||
// if we have a response target, register that for later receipt
|
||||
// of the response
|
||||
if (rsptarget != null) {
|
||||
_targets.put(invid, rsptarget);
|
||||
// if we're logged on, clear out any receiver id mapping
|
||||
if (_clobj != null) {
|
||||
_clobj.removeFromReceivers(receiverCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a receiver id to this decoder and publishes it in the
|
||||
* {@link ClientObject#receivers} field.
|
||||
*/
|
||||
protected void assignReceiverId (InvocationDecoder decoder)
|
||||
{
|
||||
Registration reg = new Registration(
|
||||
decoder.getReceiverCode(), nextReceiverId());
|
||||
// stick the mapping into the client object
|
||||
_clobj.addToReceivers(reg);
|
||||
// and map the receiver in our receivers table
|
||||
_receivers.put(reg.receiverId, decoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we log on; generates mappings for all receivers
|
||||
* registered prior to logon.
|
||||
*/
|
||||
protected void assignReceiverIds ()
|
||||
{
|
||||
// pack all the set add events into a single transaction
|
||||
_clobj.startTransaction();
|
||||
try {
|
||||
for (Iterator iter = _reclist.iterator(); iter.hasNext(); ) {
|
||||
assignReceiverId((InvocationDecoder)iter.next());
|
||||
}
|
||||
} finally {
|
||||
_clobj.commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified invocation request be packaged up and
|
||||
* sent to the supplied invocation oid.
|
||||
*/
|
||||
public void sendRequest (
|
||||
int invOid, int invCode, int methodId, Object[] args)
|
||||
{
|
||||
// configure any invocation listener marshallers among the
|
||||
// arguments
|
||||
int acount = args.length;
|
||||
for (int ii = 0; ii < acount; ii++) {
|
||||
Object arg = args[ii];
|
||||
if (arg instanceof ListenerMarshaller) {
|
||||
ListenerMarshaller lm = (ListenerMarshaller)arg;
|
||||
lm.callerOid = _clobj.getOid();
|
||||
lm.requestId = nextRequestId();
|
||||
// create a mapping for this marshaller so that we can
|
||||
// properly dispatch responses sent to it
|
||||
_listeners.put(lm.requestId, lm);
|
||||
}
|
||||
}
|
||||
|
||||
// create an invocation request event
|
||||
InvocationRequestEvent event =
|
||||
new InvocationRequestEvent(invOid, invCode, methodId, args);
|
||||
|
||||
// because invocation directors are used on the server, we set the
|
||||
// source oid here so that invocation requests are properly
|
||||
// attributed to the right client object when created by
|
||||
// server-side entities only sort of pretending to be a client
|
||||
event.setSourceOid(_cloid);
|
||||
event.setSourceOid(_clobj.getOid());
|
||||
|
||||
// and finally ship off the invocation message
|
||||
// Log.info("Sending invocation request " + event + ".");
|
||||
|
||||
// now dispatch the event
|
||||
_omgr.postEvent(event);
|
||||
|
||||
return invid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the supplied invocation receiver instance as the handler
|
||||
* for all invocation notifications for the specified module.
|
||||
* Process notification and response events arriving on user object.
|
||||
*/
|
||||
public void registerReceiver (String module, InvocationReceiver receiver)
|
||||
public void eventReceived (DEvent event)
|
||||
{
|
||||
if (_receivers == null) {
|
||||
_receivers = new HashMap();
|
||||
}
|
||||
_receivers.put(module, receiver);
|
||||
}
|
||||
if (event instanceof InvocationResponseEvent) {
|
||||
InvocationResponseEvent ire = (InvocationResponseEvent)event;
|
||||
handleInvocationResponse(
|
||||
ire.getRequestId(), ire.getMethodId(), ire.getArgs());
|
||||
|
||||
/**
|
||||
* Removes the registration for the supplied invocation receiver
|
||||
* instance as the handler for invocation notifications for the
|
||||
* specified module.
|
||||
*/
|
||||
public void unregisterReceiver (String module)
|
||||
{
|
||||
if (_receivers != null) {
|
||||
_receivers.remove(module);
|
||||
} else if (event instanceof InvocationNotificationEvent) {
|
||||
InvocationNotificationEvent ine =
|
||||
(InvocationNotificationEvent)event;
|
||||
handleInvocationNotification(
|
||||
ine.getReceiverId(), ine.getMethodId(), ine.getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process incoming message requests on user object.
|
||||
* Dispatches an invocation response.
|
||||
*/
|
||||
public void messageReceived (MessageEvent event)
|
||||
protected void handleInvocationResponse (
|
||||
int reqId, int methodId, Object[] args)
|
||||
{
|
||||
String name = event.getName();
|
||||
if (name.equals(InvocationObject.RESPONSE_NAME)) {
|
||||
handleInvocationResponse(event.getArgs());
|
||||
} else if (name.equals(InvocationObject.NOTIFICATION_NAME)) {
|
||||
handleInvocationNotification(event.getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 " +
|
||||
"[args=" + StringUtil.toString(args) + "].");
|
||||
// look up the invocation marshaller registered for that response
|
||||
ListenerMarshaller listener = (ListenerMarshaller)
|
||||
_listeners.remove(reqId);
|
||||
if (listener == null) {
|
||||
Log.warning("Received invocation response for which we have " +
|
||||
"no registered listener [reqId=" + reqId +
|
||||
", methId=" + methodId +
|
||||
", args=" + StringUtil.toString(args) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// prune the invocation id and method arguments from the full
|
||||
// message arguments
|
||||
Object[] rargs = new Object[args.length-1];
|
||||
System.arraycopy(args, 1, rargs, 0, rargs.length);
|
||||
// Log.info("Dispatching invocation response " +
|
||||
// "[listener=" + listener + ", methId=" + methodId +
|
||||
// ", args=" + StringUtil.toString(args) + "].");
|
||||
|
||||
// and invoke the response method; we'd cache these but the key
|
||||
// for the method would have to include all of the class names of
|
||||
// all of the arguments and would probably be more expensive to
|
||||
// create than just reflecting the method (we should really test
|
||||
// this because that's half intuition and half wild-ass guess, but
|
||||
// we're going with it for now)
|
||||
String mname = "handle" + name;
|
||||
Method rspmeth = ClassUtil.getMethod(mname, rsptarg, rargs);
|
||||
if (rspmeth == null) {
|
||||
Log.warning("Unable to resolve response method " +
|
||||
"[target=" + rsptarg.getClass().getName() +
|
||||
", method=" + mname +
|
||||
", args=" + StringUtil.toString(rargs) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// and invoke it
|
||||
// dispatch the response
|
||||
try {
|
||||
// Log.info("Invoking method [meth=" +
|
||||
// rsptarg.getClass().getName() + "." + rspmeth.getName() +
|
||||
// ", args=" + StringUtil.toString(rargs) + "].");
|
||||
rspmeth.invoke(rsptarg, rargs);
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error invoking response target method " +
|
||||
"[target=" + rsptarg + ", method=" + rspmeth + "].");
|
||||
Log.logStackTrace(e);
|
||||
listener.dispatchResponse(methodId, args);
|
||||
} catch (Throwable t) {
|
||||
Log.warning("Invocation response listener choked " +
|
||||
"[listener=" + listener + ", methId=" + methodId +
|
||||
", args=" + StringUtil.toString(args) + "].");
|
||||
Log.logStackTrace(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an invocation notification message.
|
||||
* Dispatches an invocation notification.
|
||||
*/
|
||||
protected void handleInvocationNotification (Object[] args)
|
||||
protected void handleInvocationNotification (
|
||||
int receiverId, int methodId, Object[] args)
|
||||
{
|
||||
String module = (String)args[0];
|
||||
String proc = (String)args[1];
|
||||
|
||||
InvocationReceiver receiver = null;
|
||||
if (_receivers != null) {
|
||||
receiver = (InvocationReceiver)_receivers.get(module);
|
||||
}
|
||||
if (receiver == null) {
|
||||
Log.warning("No receiver registered for notification " +
|
||||
"[args=" + StringUtil.toString(args) + "].");
|
||||
// look up the decoder registered for this receiver
|
||||
InvocationDecoder decoder = (InvocationDecoder)
|
||||
_receivers.get(receiverId);
|
||||
if (decoder == null) {
|
||||
Log.warning("Received notification for which we have no " +
|
||||
"registered receiver [recvId=" + receiverId +
|
||||
", methodId=" + methodId +
|
||||
", 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);
|
||||
// Log.info("Dispatching invocation notification " +
|
||||
// "[receiver=" + decoder.receiver + ", methodId=" + methodId +
|
||||
// ", args=" + StringUtil.toString(args) + "].");
|
||||
|
||||
// and invoke the receiver method; we'd cache these but the key
|
||||
// for the method would have to include all of the class names of
|
||||
// all of the arguments and would probably be more expensive to
|
||||
// create than just reflecting the method (we should really test
|
||||
// this because that's half intuition and half wild-ass guess, but
|
||||
// we're going with it for now)
|
||||
String mname = "handle" + proc + "Notification";
|
||||
Method rspmeth = ClassUtil.getMethod(mname, receiver, nargs);
|
||||
if (rspmeth == null) {
|
||||
Log.warning("Unable to resolve receiver method " +
|
||||
"[target=" + receiver.getClass().getName() +
|
||||
", method=" + mname +
|
||||
", args=" + StringUtil.toString(nargs) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// and invoke it
|
||||
try {
|
||||
rspmeth.invoke(receiver, nargs);
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error invoking receiver method " +
|
||||
"[receiver=" + receiver + ", method=" + rspmeth + "].");
|
||||
Log.logStackTrace(e);
|
||||
decoder.dispatchNotification(methodId, args);
|
||||
} catch (Throwable t) {
|
||||
Log.warning("Invocation notification receiver choked " +
|
||||
"[receiver=" + decoder.receiver +
|
||||
", methId=" + methodId +
|
||||
", args=" + StringUtil.toString(args) + "].");
|
||||
Log.logStackTrace(t);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int nextInvocationId ()
|
||||
/**
|
||||
* Used to generate monotonically increasing invocation request ids.
|
||||
*/
|
||||
protected synchronized short nextRequestId ()
|
||||
{
|
||||
return _invocationId++;
|
||||
return _requestId++;
|
||||
}
|
||||
|
||||
protected static class Response
|
||||
/**
|
||||
* Used to generate monotonically increasing invocation receiver ids.
|
||||
*/
|
||||
protected synchronized short nextReceiverId ()
|
||||
{
|
||||
public String name;
|
||||
public Object target;
|
||||
public Response (String name, Object target)
|
||||
{
|
||||
this.name = name;
|
||||
this.target = target;
|
||||
}
|
||||
return _receiverId++;
|
||||
}
|
||||
|
||||
/** The distributed object manager with which we interact. */
|
||||
protected DObjectManager _omgr;
|
||||
protected int _imoid;
|
||||
protected int _cloid;
|
||||
|
||||
protected int _invocationId;
|
||||
protected HashIntMap _targets = new HashIntMap();
|
||||
protected HashMap _receivers = new HashMap();
|
||||
/** Our client object; invocation responses and notifications are
|
||||
* received on this object. */
|
||||
protected ClientObject _clobj;
|
||||
|
||||
/** Used to generate monotonically increasing request ids. */
|
||||
protected short _requestId;
|
||||
|
||||
/** Used to generate monotonically increasing receiver ids. */
|
||||
protected short _receiverId;
|
||||
|
||||
/** Used to keep track of invocation service listeners which will
|
||||
* receive responses from invocation service requests. */
|
||||
protected HashIntMap _listeners = new HashIntMap();
|
||||
|
||||
/** Used to keep track of invocation notification receivers. */
|
||||
protected HashIntMap _receivers = new HashIntMap();
|
||||
|
||||
/** All registered receivers are maintained in a list so that we can
|
||||
* assign receiver ids to them when we go online. */
|
||||
protected ArrayList _reclist = new ArrayList();
|
||||
}
|
||||
|
||||
@@ -1,36 +1,70 @@
|
||||
//
|
||||
// $Id: InvocationReceiver.java,v 1.4 2001/10/11 04:07:52 mdb Exp $
|
||||
// $Id: InvocationReceiver.java,v 1.5 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
/**
|
||||
* Classes registered to process invocation notifications should implement
|
||||
* the invocation receiver interface and register themselves with the
|
||||
* invocation director. Because the invocation notification procedures are
|
||||
* looked up using reflection, there are no methods to implement in the
|
||||
* receiver interface, but it serves as a useful point for documentation
|
||||
* and as a useful indicator that the class in question is serving as an
|
||||
* invocation receiver.
|
||||
* Invocation notification receipt interfaces should be defined as
|
||||
* extending this interface. Actual notification receivers will implement
|
||||
* the requisite receiver interface definition and register themselves
|
||||
* with the {@link InvocationDirector} using the generated {@link
|
||||
* InvocationDispatcher} class specific to the notification receiver
|
||||
* interface in question. For example:
|
||||
*
|
||||
* <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 director 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.
|
||||
*
|
||||
* <p> 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).
|
||||
* <pre>
|
||||
* public class FooDirector implements FooReceiver
|
||||
* {
|
||||
* public FooDirector (PresentsContext ctx)
|
||||
* {
|
||||
* InvocationDirector idir = ctx.getClient().getInvocationDirector();
|
||||
* idir.registerReceiver(new FooDispatcher(this));
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @see InvocationDirector#registerReceiver
|
||||
*/
|
||||
public interface InvocationReceiver
|
||||
{
|
||||
/**
|
||||
* Used to maintain a registry of invocation receivers that can be
|
||||
* used to convert (large) hash codes into (small) registration
|
||||
* numbers.
|
||||
*/
|
||||
public static class Registration implements DSet.Entry
|
||||
{
|
||||
/** The unique hash code associated with this invocation receiver
|
||||
* class. */
|
||||
public String receiverCode;
|
||||
|
||||
/** The unique id assigned to this invocation receiver class at
|
||||
* registration time. */
|
||||
public short receiverId;
|
||||
|
||||
/** Creates and initializes a registration instance. */
|
||||
public Registration (String receiverCode, short receiverId)
|
||||
{
|
||||
this.receiverCode = receiverCode;
|
||||
this.receiverId = receiverId;
|
||||
}
|
||||
|
||||
/** Creates a blank instance suitable for unserialization. */
|
||||
public Registration ()
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public Comparable getKey ()
|
||||
{
|
||||
return receiverCode;
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[" + receiverCode + " => " + receiverId + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// $Id: InvocationService.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
/**
|
||||
* Serves as the base interface for invocation services. An invocation
|
||||
* service can be defined by extending this interface and defining service
|
||||
* methods, as well as response listeners (which must extend {@link
|
||||
* InvocationListener}). For example:
|
||||
*
|
||||
* <pre>
|
||||
* public interface LocationService extends InvocationService
|
||||
* {
|
||||
*
|
||||
* // Used to communicate responses to moveTo() requests.
|
||||
* public interface MoveListener extends InvocationListener
|
||||
* {
|
||||
* // Called in response to a successful moveTo() request.
|
||||
* public void moveSucceeded (PlaceConfig config);
|
||||
* }
|
||||
*
|
||||
* // Requests that this client's body be moved to the specified
|
||||
* // location.
|
||||
* //
|
||||
* // @param placeId the object id of the place object to which the
|
||||
* // body should be moved.
|
||||
* // @param listener the listener that will be informed of success or
|
||||
* // failure.
|
||||
* public void moveTo (int placeId, MoveListener listener);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* From this interface, a <code>LocationProvider</code> interface will be
|
||||
* generated which should be implemented by whatever server entity that
|
||||
* will actually provide the server side of this invocation service. That
|
||||
* provider interface would look like the following:
|
||||
*
|
||||
* <pre>
|
||||
* public interface LocationProvider extends InvocationProvider
|
||||
* {
|
||||
* // Requests that this client's body be moved to the specified
|
||||
* // location.
|
||||
* //
|
||||
* // @param caller the client object of the client that invoked this
|
||||
* // remotely callable method.
|
||||
* // @param placeId the object id of the place object to which the
|
||||
* // body should be moved.
|
||||
* // @param listener the listener that should be informed of success
|
||||
* // or failure.
|
||||
* public void moveTo (ClientObject caller, int placeId,
|
||||
* MoveListener listener)
|
||||
* throws InvocationException;
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public interface InvocationService
|
||||
{
|
||||
/**
|
||||
* Invocation service methods that require a response should take a
|
||||
* listener argument that can be notified of request success or
|
||||
* failure. The listener argument should extend this interface so that
|
||||
* generic failure can be reported in all cases. For example:
|
||||
*
|
||||
* <pre>
|
||||
* // Used to communicate responses to <code>moveTo</code> requests.
|
||||
* public interface MoveListener extends InvocationListener
|
||||
* {
|
||||
* // Called in response to a successful <code>moveTo</code>
|
||||
* // request.
|
||||
* public void moveSucceeded (PlaceConfig config);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static interface InvocationListener
|
||||
{
|
||||
/**
|
||||
* Called to report request failure. If the invocation services
|
||||
* system detects failure of any kind, it will report it via this
|
||||
* callback. Particular services may also make use of this
|
||||
* callback to report failures of their own, or they may opt to
|
||||
* define more specific failure callbacks.
|
||||
*/
|
||||
public void requestFailed (String cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// $Id: LoggingListener.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
import com.samskivert.util.Log;
|
||||
|
||||
/**
|
||||
* Implements the basic {@link InvocationListener} and logs the failure.
|
||||
*/
|
||||
public class LoggingListener
|
||||
implements InvocationService.InvocationListener
|
||||
{
|
||||
/**
|
||||
* Constructs a listener that will report the supplied error message
|
||||
* along with the reason for failure to the supplied log object.
|
||||
*/
|
||||
public LoggingListener (Log log, String errmsg)
|
||||
{
|
||||
_log = log;
|
||||
_errmsg = errmsg;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (String reason)
|
||||
{
|
||||
_log.warning(_errmsg + " [reason=" + reason + "].");
|
||||
}
|
||||
|
||||
protected Log _log;
|
||||
protected String _errmsg;
|
||||
}
|
||||
@@ -1,36 +1,32 @@
|
||||
//
|
||||
// $Id: TimeBaseService.java,v 1.2 2002/05/29 18:44:36 mdb Exp $
|
||||
// $Id: TimeBaseService.java,v 1.3 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.client;
|
||||
|
||||
import com.threerings.presents.Log;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationDirector;
|
||||
import com.threerings.presents.data.TimeBaseCodes;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* Provides a means by which to obtain access to a time base object which
|
||||
* can be used to convert delta times into absolute times.
|
||||
*/
|
||||
public class TimeBaseService
|
||||
implements TimeBaseCodes
|
||||
public interface TimeBaseService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Requests the oid of the specified time base object be returned. The
|
||||
* supplied response target must provide two methods to handle the
|
||||
* responses generated by this request:
|
||||
*
|
||||
* <pre>
|
||||
* public void handleTimeOidResponse (int invid, int timeOid);
|
||||
* public void handleGetTimeOidFailed (int invid, String reason);
|
||||
* </pre>
|
||||
* Used to communicated the result of a {@link #getTimeOid} request.
|
||||
*/
|
||||
public static void getTimeOid (
|
||||
Client client, String timeBase, Object rsptarget)
|
||||
public static interface GotTimeBaseListener extends InvocationListener
|
||||
{
|
||||
InvocationDirector invdir = client.getInvocationDirector();
|
||||
invdir.invoke(MODULE_NAME, GET_TIME_OID_REQUEST,
|
||||
new Object[] { timeBase }, rsptarget);
|
||||
Log.debug("Sent getTimeOid request [timeBase=" + timeBase + "].");
|
||||
/**
|
||||
* Communicates the result of a successful {@link #getTimeOid}
|
||||
* request.
|
||||
*/
|
||||
public void gotTimeOid (int timeOid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the oid of the specified time base object be fetched.
|
||||
*/
|
||||
public void getTimeOid (
|
||||
Client client, String timeBase, GotTimeBaseListener listener);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// $Id: ClientObject.dobj,v 1.1 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.data;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
/**
|
||||
* Every client in the system has an associated client object to which
|
||||
* only they subscribe. The client object can be used to deliver messages
|
||||
* solely to a particular client as well as to publish client-specific
|
||||
* data.
|
||||
*/
|
||||
public class ClientObject extends DObject
|
||||
{
|
||||
/** Used to publish all invocation service receivers registered on
|
||||
* this client. */
|
||||
public DSet receivers = new DSet();
|
||||
|
||||
/**
|
||||
* Returns a short string identifying this client.
|
||||
*/
|
||||
public String who ()
|
||||
{
|
||||
return "(" + getOid() + ")";
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
//
|
||||
// $Id: ClientObject.java,v 1.2 2001/10/11 04:07:52 mdb Exp $
|
||||
// $Id: ClientObject.java,v 1.3 2002/08/14 19:07:54 mdb Exp $
|
||||
|
||||
package com.threerings.presents.data;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
/**
|
||||
* Every client in the system has an associated client object to which
|
||||
@@ -13,4 +14,64 @@ import com.threerings.presents.dobj.DObject;
|
||||
*/
|
||||
public class ClientObject extends DObject
|
||||
{
|
||||
/** The field name of the <code>receivers</code> field. */
|
||||
public static final String RECEIVERS = "receivers";
|
||||
|
||||
/** Used to publish all invocation service receivers registered on
|
||||
* this client. */
|
||||
public DSet receivers = new DSet();
|
||||
|
||||
/**
|
||||
* Returns a short string identifying this client.
|
||||
*/
|
||||
public String who ()
|
||||
{
|
||||
return "(" + getOid() + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be added to the
|
||||
* <code>receivers</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void addToReceivers (DSet.Entry elem)
|
||||
{
|
||||
requestEntryAdd(RECEIVERS, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the entry matching the supplied key be removed from
|
||||
* the <code>receivers</code> set. The set will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void removeFromReceivers (Object key)
|
||||
{
|
||||
requestEntryRemove(RECEIVERS, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be updated in the
|
||||
* <code>receivers</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void updateReceivers (DSet.Entry elem)
|
||||
{
|
||||
requestEntryUpdate(RECEIVERS, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>receivers</code> field be set to the
|
||||
* specified value. Generally one only adds, updates and removes
|
||||
* entries of a distributed set, but certain situations call for a
|
||||
* complete replacement of the set 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.
|
||||
*/
|
||||
public void setReceivers (DSet receivers)
|
||||
{
|
||||
this.receivers = receivers;
|
||||
requestAttributeChange(RECEIVERS, receivers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// $Id: InvocationMarshaller.java,v 1.1 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.presents.Log;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
|
||||
/**
|
||||
* Provides a base from which all invocation service marshallers extend.
|
||||
* Handles functionality common to all marshallers.
|
||||
*/
|
||||
public class InvocationMarshaller
|
||||
implements Streamable, InvocationService
|
||||
{
|
||||
/**
|
||||
* Provides a base from which invocation listener marshallers extend.
|
||||
*/
|
||||
public static class ListenerMarshaller
|
||||
implements Streamable, InvocationListener
|
||||
{
|
||||
/** The method id used to dispatch a {@link #requestFailed}
|
||||
* response. */
|
||||
public static final int REQUEST_FAILED_RSPID = 0;
|
||||
|
||||
/** The oid of the invocation service requester. */
|
||||
public int callerOid;
|
||||
|
||||
/** The request id associated with this listener. */
|
||||
public short requestId;
|
||||
|
||||
/** The actual invocation listener associated with this
|
||||
* marshalling listener. This is only valid on the client. */
|
||||
public transient InvocationListener listener;
|
||||
|
||||
/** The distributed object manager to use when dispatching proxied
|
||||
* responses. This is only valid on the server. */
|
||||
public transient DObjectManager omgr;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (String cause)
|
||||
{
|
||||
omgr.postEvent(new InvocationResponseEvent(
|
||||
callerOid, requestId, REQUEST_FAILED_RSPID,
|
||||
new Object[] { cause }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to dispatch an invocation response to our target
|
||||
* listener.
|
||||
*/
|
||||
public void dispatchResponse (int methodId, Object[] args)
|
||||
{
|
||||
if (methodId == REQUEST_FAILED_RSPID) {
|
||||
listener.requestFailed((String)args[0]);
|
||||
|
||||
} else {
|
||||
Log.warning("Requested to dispatch unknown invocation " +
|
||||
"response [listener=" + listener +
|
||||
", methodId=" + methodId +
|
||||
", args=" + StringUtil.toString(args) + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return "[callerOid=" + callerOid + ", reqId=" + requestId +
|
||||
", type=" + getClass().getName() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this invocation marshaller instance with the requisite
|
||||
* information to allow it to operate in the wide world. This is
|
||||
* called by the invocation manager when an invocation provider is
|
||||
* registered and should not be called otherwise.
|
||||
*/
|
||||
public void init (int invOid, int invCode)
|
||||
{
|
||||
_invOid = invOid;
|
||||
_invCode = invCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the invocation oid to which this marshaller should send its
|
||||
* invocation service requests. This is called by the invocation
|
||||
* manager in certain initialization circumstances.
|
||||
*/
|
||||
public void setInvocationOid (int invOid)
|
||||
{
|
||||
_invOid = invOid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by generated invocation marshaller code; packages up and
|
||||
* sends the specified invocation service request.
|
||||
*/
|
||||
protected void sendRequest (Client client, int methodId, Object[] args)
|
||||
{
|
||||
client.getInvocationDirector().sendRequest(
|
||||
_invOid, _invCode, methodId, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes this instance to the supplied output stream.
|
||||
*/
|
||||
public void writeObject (ObjectOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
out.writeInt(_invOid);
|
||||
out.writeShort(_invCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this instance from the supplied input stream.
|
||||
*/
|
||||
public void readObject (ObjectInputStream in)
|
||||
throws IOException
|
||||
{
|
||||
_invOid = in.readInt();
|
||||
_invCode = in.readShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return "[invOid=" + _invOid + ", code=" + _invCode +
|
||||
", type=" + getClass().getName() + "]";
|
||||
}
|
||||
|
||||
/** The oid of the invocation object, where invocation service
|
||||
* requests are sent. */
|
||||
protected int _invOid;
|
||||
|
||||
/** The invocation service code assigned to this service when it was
|
||||
* registered on the server. */
|
||||
protected int _invCode;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: InvocationObject.java,v 1.3 2001/10/11 04:07:52 mdb Exp $
|
||||
// $Id: InvocationObject.java,v 1.4 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.data;
|
||||
|
||||
@@ -13,21 +13,4 @@ import com.threerings.presents.dobj.DObject;
|
||||
*/
|
||||
public class InvocationObject extends DObject
|
||||
{
|
||||
/**
|
||||
* This constant is used to identify invocation requests sent to the
|
||||
* server.
|
||||
*/
|
||||
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,23 +1,13 @@
|
||||
//
|
||||
// $Id: TimeBaseCodes.java,v 1.1 2002/05/28 23:14:06 mdb Exp $
|
||||
// $Id: TimeBaseCodes.java,v 1.2 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.data;
|
||||
|
||||
/**
|
||||
* Codes and constants relating to the Presents time base services.
|
||||
*/
|
||||
public interface TimeBaseCodes
|
||||
public interface TimeBaseCodes extends InvocationCodes
|
||||
{
|
||||
/** The module name for the time services. */
|
||||
public static final String MODULE_NAME = "time";
|
||||
|
||||
/** The message identifier for a request to obtain a particular time
|
||||
* object. */
|
||||
public static final String GET_TIME_OID_REQUEST = "GetTimeOid";
|
||||
|
||||
/** A response generated for a successful getTimeOid request. */
|
||||
public static final String TIME_OID_RESPONSE = "TimeOid";
|
||||
|
||||
/** An error response generated for GetTimeOid requests. */
|
||||
public static final String NO_SUCH_TIME_BASE = "m.no_such_time_base";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $Id: TimeBaseMarshaller.java,v 1.1 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.data;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.TimeBaseService;
|
||||
import com.threerings.presents.client.TimeBaseService.GotTimeBaseListener;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link TimeBaseService} 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.
|
||||
*/
|
||||
public class TimeBaseMarshaller extends InvocationMarshaller
|
||||
implements TimeBaseService
|
||||
{
|
||||
// documentation inherited
|
||||
public static class GotTimeBaseMarshaller extends ListenerMarshaller
|
||||
implements GotTimeBaseListener
|
||||
{
|
||||
/** The method id used to dispatch {@link #gotTimeOid}
|
||||
* responses. */
|
||||
public static final int GOT_TIME_OID = 0;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void gotTimeOid (int arg1)
|
||||
{
|
||||
omgr.postEvent(new InvocationResponseEvent(
|
||||
callerOid, requestId, GOT_TIME_OID,
|
||||
new Object[] { new Integer(arg1) }));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void dispatchResponse (int methodId, Object[] args)
|
||||
{
|
||||
switch (methodId) {
|
||||
case GOT_TIME_OID:
|
||||
((GotTimeBaseListener)listener).gotTimeOid(
|
||||
((Integer)args[0]).intValue());
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchResponse(methodId, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #getTimeOid} requests. */
|
||||
public static final int GET_TIME_OID = 1;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getTimeOid (Client arg1, String arg2, GotTimeBaseListener arg3)
|
||||
{
|
||||
GotTimeBaseMarshaller listener3 = new GotTimeBaseMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, GET_TIME_OID, new Object[] {
|
||||
arg2, listener3
|
||||
});
|
||||
}
|
||||
|
||||
// Class file generated on 00:26:01 08/11/02.
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: DObject.java,v 1.46 2002/07/23 05:52:48 mdb Exp $
|
||||
// $Id: DObject.java,v 1.47 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
@@ -473,6 +473,14 @@ public class DObject implements Streamable
|
||||
_oid = oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a concise string representation of this object.
|
||||
*/
|
||||
public String which ()
|
||||
{
|
||||
return "[" + getClass().getName() + " " + getOid() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this object.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
//
|
||||
// $Id: DSet.java,v 1.17 2002/07/23 05:52:48 mdb Exp $
|
||||
// $Id: DSet.java,v 1.18 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
@@ -45,7 +47,7 @@ public class DSet
|
||||
* its uniqueness in the set. See the {@link DSet} class
|
||||
* documentation for further information.
|
||||
*/
|
||||
public Object getKey ();
|
||||
public Comparable getKey ();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,17 +63,8 @@ public class DSet
|
||||
*/
|
||||
public DSet (Iterator source)
|
||||
{
|
||||
for (int index = 0; source.hasNext(); index++) {
|
||||
Entry elem = (Entry)source.next();
|
||||
|
||||
// expand the array if necessary
|
||||
if (index >= _entries.length) {
|
||||
expand(index);
|
||||
}
|
||||
|
||||
// insert the item
|
||||
_entries[index] = elem;
|
||||
_size++;
|
||||
while (source.hasNext()) {
|
||||
add((Entry)source.next());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,37 +133,16 @@ public class DSet
|
||||
public Iterator entries ()
|
||||
{
|
||||
return new Iterator() {
|
||||
public boolean hasNext ()
|
||||
{
|
||||
// we need to scan to the next entry the first time
|
||||
if (_index < 0) {
|
||||
scanToNext();
|
||||
}
|
||||
return (_index < _entries.length);
|
||||
public boolean hasNext () {
|
||||
return (_index < _size);
|
||||
}
|
||||
|
||||
public Object next ()
|
||||
{
|
||||
Object val = _entries[_index];
|
||||
scanToNext();
|
||||
return val;
|
||||
public Object next () {
|
||||
return _entries[_index++];
|
||||
}
|
||||
|
||||
public void remove ()
|
||||
{
|
||||
public void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void scanToNext ()
|
||||
{
|
||||
for (_index++; _index < _entries.length; _index++) {
|
||||
if (_entries[_index] != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int _index = -1;
|
||||
protected int _index = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -185,32 +157,44 @@ public class DSet
|
||||
*/
|
||||
protected boolean add (Entry elem)
|
||||
{
|
||||
Object key = elem.getKey();
|
||||
// determine where we'll be adding the new element
|
||||
int eidx = ArrayUtil.binarySearch(
|
||||
_entries, 0, _size, elem, ENTRY_COMP);
|
||||
|
||||
// if the element is already in the set, bail now
|
||||
if (eidx >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert the index into happy positive land
|
||||
eidx = (eidx+1)*-1;
|
||||
|
||||
// expand our entries array if necessary
|
||||
int elength = _entries.length;
|
||||
int index = elength;
|
||||
|
||||
// scan the array looking for a slot and/or the entry already in
|
||||
// the set
|
||||
for (int i = 0; i < elength; i++) {
|
||||
Entry el = _entries[i];
|
||||
// the array may be sparse
|
||||
if (el == null) {
|
||||
if (index == elength) {
|
||||
index = i;
|
||||
}
|
||||
} else if (el.getKey().equals(key)) {
|
||||
return false;
|
||||
if (_size == elength-1) {
|
||||
// sanity check
|
||||
if (elength > 2048) {
|
||||
Log.warning("Requested to expand to questionably large size " +
|
||||
"[length=" + elength + "].");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
|
||||
// create a new array and copy our data into it
|
||||
Entry[] elems = new Entry[elength*2];
|
||||
System.arraycopy(_entries, 0, elems, 0, elength);
|
||||
_entries = elems;
|
||||
}
|
||||
|
||||
// expand the array if necessary
|
||||
if (index >= _entries.length) {
|
||||
expand(index);
|
||||
// if the entry doesn't go at the end, shift the elements down to
|
||||
// accomodate it
|
||||
if (eidx < _size) {
|
||||
System.arraycopy(_entries, eidx, _entries, eidx+1, _size-eidx);
|
||||
}
|
||||
|
||||
// insert the item
|
||||
_entries[index] = elem;
|
||||
// stuff the entry into the array and note that we're bigger
|
||||
_entries[eidx] = elem;
|
||||
_size++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -239,17 +223,20 @@ public class DSet
|
||||
*/
|
||||
protected boolean removeKey (Object key)
|
||||
{
|
||||
// scan the array looking for a matching entry
|
||||
int elength = _entries.length;
|
||||
for (int i = 0; i < elength; i++) {
|
||||
Entry el = _entries[i];
|
||||
if (el != null && el.getKey().equals(key)) {
|
||||
_entries[i] = null;
|
||||
_size--;
|
||||
return true;
|
||||
}
|
||||
// look up this entry's position in our set
|
||||
int eidx = ArrayUtil.binarySearch(
|
||||
_entries, 0, _size, key, ENTRY_COMP);
|
||||
|
||||
// if we found it, remove it
|
||||
if (eidx >= 0) {
|
||||
// shift the remaining elements down
|
||||
System.arraycopy(_entries, eidx+1, _entries, eidx, _size-eidx-1);
|
||||
_entries[--_size] = null;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,19 +251,17 @@ public class DSet
|
||||
*/
|
||||
protected boolean update (Entry elem)
|
||||
{
|
||||
Object key = elem.getKey();
|
||||
// look up this entry's position in our set
|
||||
int eidx = ArrayUtil.binarySearch(
|
||||
_entries, 0, _size, elem, ENTRY_COMP);
|
||||
|
||||
// scan the array looking for a matching entry
|
||||
int elength = _entries.length;
|
||||
for (int i = 0; i < elength; i++) {
|
||||
Entry el = _entries[i];
|
||||
if (el != null && el.getKey().equals(key)) {
|
||||
_entries[i] = elem;
|
||||
return true;
|
||||
}
|
||||
// if we found it, update it
|
||||
if (eidx >= 0) {
|
||||
_entries[eidx] = elem;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,35 +325,6 @@ public class DSet
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected void expand (int index)
|
||||
{
|
||||
// sanity check
|
||||
if (index < 0 || index > Short.MAX_VALUE) {
|
||||
Log.warning("Requested to expand to accomodate bogus index! " +
|
||||
"[index=" + index + "].");
|
||||
Thread.dumpStack();
|
||||
index = 0;
|
||||
}
|
||||
|
||||
// increase our length in powers of two until we're big enough
|
||||
int tlength = _entries.length;
|
||||
while (index >= tlength) {
|
||||
tlength *= 2;
|
||||
}
|
||||
|
||||
// further sanity checks
|
||||
if (tlength > 4096) {
|
||||
Log.warning("Requested to expand to questionably large size " +
|
||||
"[index=" + index + ", tlength=" + tlength + "].");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
|
||||
// create a new array and copy our data into it
|
||||
Entry[] elems = new Entry[tlength];
|
||||
System.arraycopy(_entries, 0, elems, 0, _entries.length);
|
||||
_entries = elems;
|
||||
}
|
||||
|
||||
/** The entries of the set (in a sparse array). */
|
||||
protected Entry[] _entries = new Entry[INITIAL_CAPACITY];
|
||||
|
||||
@@ -377,4 +333,21 @@ public class DSet
|
||||
|
||||
/** The default capacity of a set instance. */
|
||||
protected static final int INITIAL_CAPACITY = 2;
|
||||
|
||||
/** Used for lookups and to keep the set contents sorted on
|
||||
* insertions. */
|
||||
protected static Comparator ENTRY_COMP = new Comparator() {
|
||||
public int compare (Object o1, Object o2) {
|
||||
Comparable c1 = (o1 instanceof Entry) ?
|
||||
((Entry)o1).getKey() : (Comparable)o1;
|
||||
Comparable c2 = (o2 instanceof Entry) ?
|
||||
((Entry)o2).getKey() : (Comparable)o2;
|
||||
return c1.compareTo(c2);
|
||||
}
|
||||
|
||||
public boolean equals (Object obj) {
|
||||
// we don't care about comparing comparators
|
||||
return (obj == this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// $Id: InvocationNotificationEvent.java,v 1.1 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
/**
|
||||
* Used to dispatch an invocation notification from the server to a
|
||||
* client.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class InvocationNotificationEvent extends DEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new invocation notification event on the specified
|
||||
* target object with the supplied receiver id, method id and
|
||||
* arguments.
|
||||
*
|
||||
* @param targetOid the object id of the object on which the event is
|
||||
* to be dispatched.
|
||||
* @param receiverId identifies the receiver to which this notification
|
||||
* is being dispatched.
|
||||
* @param methodId the id of the method to be invoked.
|
||||
* @param args the arguments for the method. This array should contain
|
||||
* only values of valid distributed object types.
|
||||
*/
|
||||
public InvocationNotificationEvent (
|
||||
int targetOid, short receiverId, int methodId, Object[] args)
|
||||
{
|
||||
super(targetOid);
|
||||
_receiverId = receiverId;
|
||||
_methodId = (byte)methodId;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a blank instance of this event in preparation for
|
||||
* unserialization from the network.
|
||||
*/
|
||||
public InvocationNotificationEvent ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the receiver id associated with this notification.
|
||||
*/
|
||||
public int getReceiverId ()
|
||||
{
|
||||
return _receiverId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the method associated with this notification.
|
||||
*/
|
||||
public int getMethodId ()
|
||||
{
|
||||
return _methodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments associated with this notification.
|
||||
*/
|
||||
public Object[] getArgs ()
|
||||
{
|
||||
return _args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this attribute change to the object.
|
||||
*/
|
||||
public boolean applyToObject (DObject target)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
// nothing to do here
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes our custom streamable fields.
|
||||
*/
|
||||
public void writeObject (ObjectOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeShort(_receiverId);
|
||||
out.writeByte(_methodId);
|
||||
out.writeObject(_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads our custom streamable fields.
|
||||
*/
|
||||
public void readObject (ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readObject(in);
|
||||
_receiverId = in.readShort();
|
||||
_methodId = in.readByte();
|
||||
_args = (Object[])in.readObject();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void notifyListener (Object listener)
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuffer buf)
|
||||
{
|
||||
buf.append("INOT:");
|
||||
super.toString(buf);
|
||||
buf.append(", rcvId=").append(_receiverId);
|
||||
buf.append(", methodId=").append(_methodId);
|
||||
buf.append(", args=").append(StringUtil.toString(_args));
|
||||
}
|
||||
|
||||
/** Identifies the receiver to which this notification is being
|
||||
* dispatched. */
|
||||
protected short _receiverId;
|
||||
|
||||
/** The id of the receiver method being invoked. */
|
||||
protected byte _methodId;
|
||||
|
||||
/** The arguments to the receiver method being invoked. */
|
||||
protected Object[] _args;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// $Id: InvocationRequestEvent.java,v 1.1 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
/**
|
||||
* Used to dispatch an invocation request from the client to the server.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class InvocationRequestEvent extends DEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new invocation request event on the specified target
|
||||
* object with the supplied code, method and arguments.
|
||||
*
|
||||
* @param targetOid the object id of the object on which the event is
|
||||
* to be dispatched.
|
||||
* @param invCode the invocation provider identification code.
|
||||
* @param methodId the id of the method to be invoked.
|
||||
* @param args the arguments for the method. This array should contain
|
||||
* only values of valid distributed object types.
|
||||
*/
|
||||
public InvocationRequestEvent (
|
||||
int targetOid, int invCode, int methodId, Object[] args)
|
||||
{
|
||||
super(targetOid);
|
||||
_invCode = invCode;
|
||||
_methodId = (byte)methodId;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a blank instance of this event in preparation for
|
||||
* unserialization from the network.
|
||||
*/
|
||||
public InvocationRequestEvent ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the invocation code associated with this request.
|
||||
*/
|
||||
public int getInvCode ()
|
||||
{
|
||||
return _invCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the method associated with this request.
|
||||
*/
|
||||
public int getMethodId ()
|
||||
{
|
||||
return _methodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments associated with this request.
|
||||
*/
|
||||
public Object[] getArgs ()
|
||||
{
|
||||
return _args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this attribute change to the object.
|
||||
*/
|
||||
public boolean applyToObject (DObject target)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
// nothing to do here
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes our custom streamable fields.
|
||||
*/
|
||||
public void writeObject (ObjectOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeInt(_invCode);
|
||||
out.writeByte(_methodId);
|
||||
out.writeObject(_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads our custom streamable fields.
|
||||
*/
|
||||
public void readObject (ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readObject(in);
|
||||
_invCode = in.readInt();
|
||||
_methodId = in.readByte();
|
||||
_args = (Object[])in.readObject();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void notifyListener (Object listener)
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuffer buf)
|
||||
{
|
||||
buf.append("IREQ:");
|
||||
super.toString(buf);
|
||||
buf.append(", code=").append(_invCode);
|
||||
buf.append(", methodId=").append(_methodId);
|
||||
buf.append(", args=").append(StringUtil.toString(_args));
|
||||
}
|
||||
|
||||
/** The code identifying which invocation provider to which this
|
||||
* request is directed. */
|
||||
protected int _invCode;
|
||||
|
||||
/** The id of the method being invoked. */
|
||||
protected byte _methodId;
|
||||
|
||||
/** The arguments to the method being invoked. */
|
||||
protected Object[] _args;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// $Id: InvocationResponseEvent.java,v 1.1 2002/08/14 19:07:55 mdb Exp $
|
||||
|
||||
package com.threerings.presents.dobj;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
import com.threerings.io.ObjectOutputStream;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
/**
|
||||
* Used to dispatch an invocation response from the server to the client.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class InvocationResponseEvent extends DEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new invocation response event on the specified target
|
||||
* object with the supplied code, method and arguments.
|
||||
*
|
||||
* @param targetOid the object id of the object on which the event is
|
||||
* to be dispatched.
|
||||
* @param requestId the id of the request to which we are responding.
|
||||
* @param methodId the method to be invoked.
|
||||
* @param args the arguments for the method. This array should contain
|
||||
* only values of valid distributed object types.
|
||||
*/
|
||||
public InvocationResponseEvent (
|
||||
int targetOid, int requestId, int methodId, Object[] args)
|
||||
{
|
||||
super(targetOid);
|
||||
_requestId = (short)requestId;
|
||||
_methodId = (byte)methodId;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a blank instance of this event in preparation for
|
||||
* unserialization from the network.
|
||||
*/
|
||||
public InvocationResponseEvent ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the invocation request id associated with this response.
|
||||
*/
|
||||
public int getRequestId ()
|
||||
{
|
||||
return _requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the method associated with this response.
|
||||
*/
|
||||
public int getMethodId ()
|
||||
{
|
||||
return _methodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments associated with this response.
|
||||
*/
|
||||
public Object[] getArgs ()
|
||||
{
|
||||
return _args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this attribute change to the object.
|
||||
*/
|
||||
public boolean applyToObject (DObject target)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
// nothing to do here
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes our custom streamable fields.
|
||||
*/
|
||||
public void writeObject (ObjectOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeObject(out);
|
||||
out.writeShort(_requestId);
|
||||
out.writeByte(_methodId);
|
||||
out.writeObject(_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads our custom streamable fields.
|
||||
*/
|
||||
public void readObject (ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
super.readObject(in);
|
||||
_requestId = in.readShort();
|
||||
_methodId = in.readByte();
|
||||
_args = (Object[])in.readObject();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void notifyListener (Object listener)
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuffer buf)
|
||||
{
|
||||
buf.append("IRSP:");
|
||||
super.toString(buf);
|
||||
buf.append(", reqid=").append(_requestId);
|
||||
buf.append(", methodId=").append(_methodId);
|
||||
buf.append(", args=").append(StringUtil.toString(_args));
|
||||
}
|
||||
|
||||
/** The id of the request with which this response is associated. */
|
||||
protected short _requestId;
|
||||
|
||||
/** The id of the method being invoked. */
|
||||
protected byte _methodId;
|
||||
|
||||
/** The arguments to the method being invoked. */
|
||||
protected Object[] _args;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
//
|
||||
// $Id: BootstrapData.java,v 1.6 2002/05/28 22:54:43 mdb Exp $
|
||||
// $Id: BootstrapData.java,v 1.7 2002/08/14 19:07:56 mdb Exp $
|
||||
|
||||
package com.threerings.presents.net;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.util.StreamableArrayList;
|
||||
|
||||
/**
|
||||
* A <code>BootstrapData</code> object is communicated back to the client
|
||||
@@ -16,6 +17,6 @@ public class BootstrapData extends DObject
|
||||
/** The oid of this client's associated distributed object. */
|
||||
public int clientOid;
|
||||
|
||||
/** The oid to which to send invocation requests. */
|
||||
public int invOid;
|
||||
/** A list of handles to invocation services. */
|
||||
public StreamableArrayList services;
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user