diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java
index 08252cf97..31a2a4390 100644
--- a/src/java/com/threerings/presents/client/Client.java
+++ b/src/java/com/threerings/presents/client/Client.java
@@ -1,5 +1,5 @@
//
-// $Id: Client.java,v 1.4 2001/05/30 23:58:31 mdb Exp $
+// $Id: Client.java,v 1.5 2001/06/09 23:39:03 mdb Exp $
package com.threerings.cocktail.cher.client;
@@ -7,24 +7,47 @@ import java.util.ArrayList;
import java.util.List;
import com.threerings.cocktail.cher.Log;
+import com.threerings.cocktail.cher.dobj.DObjectManager;
import com.threerings.cocktail.cher.net.Credentials;
import com.threerings.cocktail.cher.net.Registry;
/**
* Through the client object, a connection to the system is established
* and maintained. The client object maintains two separate threads (a
- * reader and a writer) through which all network traffic is managed.
+ * reader and a writer) by which all network traffic is managed.
*/
public class Client
{
/**
- * Constructs a client object with the supplied credentials. These
- * creds will be used to authenticate with any server to which this
- * client attempts to connect.
+ * This is used by the client to allow dobj event dispatching to take
+ * place along side the activities of the rest of the application
+ * (usually this means running dobj events on the AWT thread).
*/
- public Client (Credentials creds)
+ public static interface Invoker
+ {
+ /**
+ * Requests that the supplied runnable be queued up for invocation
+ * on the main event dispatching thread of the application.
+ */
+ public void invokeLater (Runnable run);
+ }
+
+ /**
+ * Constructs a client object with the supplied credentials and
+ * invoker. The creds will be used to authenticate with any server to
+ * which this client attempts to connect. The invoker is used to
+ * operate the distributed object event dispatch mechanism. To allow
+ * the dobj event dispatch to coexist with threads like the AWT
+ * thread, the client will request that the invoker queue up a
+ * runnable whenever there are distributed object events that need to
+ * be processed. The invoker can then queue that runnable up on the
+ * AWT thread if it is so inclined to make life simpler for the rest
+ * of the application.
+ */
+ public Client (Credentials creds, Invoker invoker)
{
_creds = creds;
+ _invoker = invoker;
}
/**
@@ -69,6 +92,15 @@ public class Client
_port = port;
}
+ /**
+ * Returns the invoker in use by this client. This can be used to
+ * queue up event dispatching stints.
+ */
+ public Invoker getInvoker ()
+ {
+ return _invoker;
+ }
+
/**
* Returns the hostname of the server to which this client is
* currently configured to connect.
@@ -96,6 +128,20 @@ public class Client
return _creds;
}
+ /**
+ * Returns the distributed object manager associated with this
+ * session. This reference is only valid for the duration of the
+ * session and a new reference must be obtained if the client
+ * disconnects and reconnects to the server.
+ *
+ * @return the dobjmgr in effect or null if we have no established
+ * connection to the server.
+ */
+ public DObjectManager getDObjectManager ()
+ {
+ return (_comm != null) ? _comm.getDObjectManager() : null;
+ }
+
/**
* Requests that this client connect and logon to the server with
* which it was previously configured.
@@ -180,6 +226,8 @@ public class Client
}
protected Credentials _creds;
+ protected Invoker _invoker;
+
protected String _hostname;
protected int _port;
diff --git a/src/java/com/threerings/presents/client/ClientDObjectMgr.java b/src/java/com/threerings/presents/client/ClientDObjectMgr.java
new file mode 100644
index 000000000..cf0cc534f
--- /dev/null
+++ b/src/java/com/threerings/presents/client/ClientDObjectMgr.java
@@ -0,0 +1,275 @@
+//
+// $Id: ClientDObjectMgr.java,v 1.1 2001/06/09 23:39:03 mdb Exp $
+
+package com.threerings.cocktail.cher.client;
+
+import java.util.ArrayList;
+import com.samskivert.util.IntMap;
+import com.samskivert.util.Queue;
+
+import com.threerings.cocktail.cher.Log;
+import com.threerings.cocktail.cher.dobj.*;
+import com.threerings.cocktail.cher.net.*;
+
+/**
+ * The client distributed object manager manages a set of proxy objects
+ * which mirror the distributed objects maintained on the server.
+ * Requests for modifications, etc. are forwarded to the server and events
+ * are dispatched from the server to this client for objects to which this
+ * client is subscribed.
+ */
+public class ClientDObjectMgr
+ implements DObjectManager, Runnable
+{
+ /**
+ * Constructs a client distributed object manager.
+ *
+ * @param comm a communicator instance by which it can communicate
+ * with the server.
+ * @param client a reference to the client that is managing this whole
+ * communications and event dispatch business.
+ */
+ public ClientDObjectMgr (Communicator comm, Client client)
+ {
+ _comm = comm;
+ _client = client;
+ }
+
+ // inherit documentation from the interface
+ public void createObject (Class dclass, Subscriber target,
+ boolean subscribe)
+ {
+ // not presently supported
+ throw new RuntimeException("createObject() not supported");
+ }
+
+ // inherit documentation from the interface
+ public void subscribeToObject (int oid, Subscriber target)
+ {
+ queueAction(oid, target, true);
+ }
+
+ // inherit documentation from the interface
+ public void unsubscribeFromObject (int oid, Subscriber target)
+ {
+ queueAction(oid, target, false);
+
+ // forward an unsubscribe request to the server
+ _comm.postMessage(new UnsubscribeRequest(oid));
+ }
+
+ protected void queueAction (int oid, Subscriber target, boolean subscribe)
+ {
+ // queue up an action
+ _actions.append(new ObjectAction(oid, target, subscribe));
+ // and queue up the omgr to get invoked on the invoker thread
+ _client.getInvoker().invokeLater(this);
+ }
+
+ // inherit documentation from the interface
+ public void postEvent (DEvent event)
+ {
+ // send a forward event request to the server
+ _comm.postMessage(new ForwardEventRequest(event));
+ }
+
+ /**
+ * Called by the communicator when a downstream message arrives from
+ * the network layer. We queue it up for processing and request some
+ * processing time on the main thread.
+ */
+ public void processMessage (DownstreamMessage msg)
+ {
+ // append it to our queue
+ _actions.append(msg);
+ // and queue ourselves up to be run
+ _client.getInvoker().invokeLater(this);
+ }
+
+ /**
+ * Invoked on the AWT thread to process any newly arrived messages
+ * that we have waiting in our queue.
+ */
+ public void run ()
+ {
+ // process all of the events on our queue
+ Object obj;
+ while ((obj = _actions.getNonBlocking()) != null) {
+ // do the proper thing depending on the object
+ if (obj instanceof EventNotification) {
+ DEvent evt = ((EventNotification)obj).getEvent();
+ dispatchEvent(evt);
+
+ } else if (obj instanceof ObjectResponse) {
+ registerObjectAndNotify(((ObjectResponse)obj).getObject());
+
+ } else if (obj instanceof FailureResponse) {
+ int oid = ((FailureResponse)obj).getOid();
+ notifyFailure(oid);
+
+ } else if (obj instanceof PongResponse) {
+ Log.info("Got pong.");
+
+ } else if (obj instanceof ObjectAction) {
+ ObjectAction act = (ObjectAction)obj;
+ if (act.subscribe) {
+ doSubscribe(act.oid, act.target);
+ } else {
+ doUnsubscribe(act.oid, act.target);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when a new event arrives from the server that should be
+ * dispatched to subscribers here on the client.
+ */
+ protected void dispatchEvent (DEvent event)
+ {
+ Log.info("Dispatch event: " + event);
+
+ // look up the object on which we're dispatching this event
+ DObject target = (DObject)_ocache.get(event.getTargetOid());
+ if (target == null) {
+ Log.info("Unable to dispatch event on non-proxied " +
+ "object [event=" + event + "].");
+ return;
+ }
+ }
+
+ /**
+ * Registers this object in our proxy cache and notifies the
+ * subscribers that were waiting for subscription to this object.
+ */
+ protected void registerObjectAndNotify (DObject obj)
+ {
+ // let the object know that we'll be managing it
+ obj.setManager(this);
+
+ // stick the object into the proxy object table
+ _ocache.put(obj.getOid(), obj);
+
+ // let the penders know that the object is available
+ PendingRequest req = (PendingRequest)_penders.remove(obj.getOid());
+ if (req == null) {
+ Log.warning("Got object, but no one cares?! " +
+ "[oid=" + obj.getOid() + ", obj=" + obj + "].");
+ return;
+ }
+
+ for (int i = 0; i < req.targets.size(); i++) {
+ Subscriber target = (Subscriber)req.targets.get(i);
+ target.objectAvailable(obj);
+ }
+ }
+
+ /**
+ * Notifies the subscribers that had requested this object (for
+ * subscription) that it is not available.
+ */
+ protected void notifyFailure (int oid)
+ {
+ Log.info("Get failed: " + oid);
+ }
+
+ /**
+ * This is guaranteed to be invoked via the invoker and can safely do
+ * main thread type things like call back to the subscriber.
+ */
+ protected void doSubscribe (int oid, Subscriber target)
+ {
+ Log.info("doSubscribe: " + oid + ": " + target);
+
+ // first see if we've already got the object in our table
+ DObject obj = (DObject)_ocache.get(oid);
+ if (obj != null) {
+ // add the subscriber and call them back straight away
+ obj.addSubscriber(target);
+ target.objectAvailable(obj);
+ return;
+ }
+
+ // see if we've already got an outstanding request for this object
+ PendingRequest req = (PendingRequest)_penders.get(oid);
+ if (req != null) {
+ // add this subscriber to the list of subscribers to be
+ // notified when the request is satisfied
+ req.addTarget(target);
+ return;
+ }
+
+ // otherwise we need to create a new request
+ req = new PendingRequest(oid);
+ req.addTarget(target);
+ _penders.put(oid, req);
+ Log.info("Registering pending request [oid=" + oid + "].");
+
+ // and issue a request to get things rolling
+ _comm.postMessage(new SubscribeRequest(oid));
+ }
+
+ /**
+ * This is guaranteed to be invoked via the invoker and can safely do
+ * main thread type things like call back to the subscriber.
+ */
+ protected void doUnsubscribe (int oid, Subscriber target)
+ {
+ DObject dobj = (DObject)_ocache.get(oid);
+ if (dobj != null) {
+ dobj.removeSubscriber(target);
+
+ } else {
+ Log.info("Requested to remove subscriber from " +
+ "non-proxied object [oid=" + oid +
+ ", sub=" + target + "].");
+ }
+ }
+
+ /**
+ * The object action is used to queue up a subscribe or unsubscribe
+ * request.
+ */
+ protected class ObjectAction
+ {
+ public int oid;
+ public Subscriber target;
+ public boolean subscribe;
+
+ public ObjectAction (int oid, Subscriber target, boolean subscribe)
+ {
+ this.oid = oid;
+ this.target = target;
+ this.subscribe = subscribe;
+ }
+ }
+
+ protected static class PendingRequest
+ {
+ public int oid;
+ public ArrayList targets = new ArrayList();
+
+ public PendingRequest (int oid)
+ {
+ this.oid = oid;
+ }
+
+ public void addTarget (Subscriber target)
+ {
+ targets.add(target);
+ }
+ }
+
+ protected Communicator _comm;
+ protected Client _client;
+ protected Queue _actions = new Queue();
+
+ /**
+ * This table contains all of the distributed objects that are active
+ * on this client.
+ */
+ protected IntMap _ocache = new IntMap();
+
+ /** This table contains pending subscriptions. */
+ protected IntMap _penders = new IntMap();
+}
diff --git a/src/java/com/threerings/presents/client/Communicator.java b/src/java/com/threerings/presents/client/Communicator.java
index ee27418c2..1bfdcd2e0 100644
--- a/src/java/com/threerings/presents/client/Communicator.java
+++ b/src/java/com/threerings/presents/client/Communicator.java
@@ -1,5 +1,5 @@
//
-// $Id: Communicator.java,v 1.6 2001/05/30 23:58:31 mdb Exp $
+// $Id: Communicator.java,v 1.7 2001/06/09 23:39:03 mdb Exp $
package com.threerings.cocktail.cher.client;
@@ -11,6 +11,7 @@ import com.samskivert.util.LoopingThread;
import com.samskivert.util.Queue;
import com.threerings.cocktail.cher.Log;
+import com.threerings.cocktail.cher.dobj.DObjectManager;
import com.threerings.cocktail.cher.io.*;
import com.threerings.cocktail.cher.io.ObjectStreamException;
import com.threerings.cocktail.cher.net.*;
@@ -50,6 +51,17 @@ public class Communicator
_client = client;
}
+ /**
+ * Returns the distributed object manager in effect for this session.
+ * This instance is only valid while the client is connected to the
+ * server. If we become disconnected and have to reconnect, a new omgr
+ * instance should be obtained.
+ */
+ public DObjectManager getDObjectManager ()
+ {
+ return _omgr;
+ }
+
/**
* Logs on to the server and initiates our full-duplex message
* exchange.
@@ -144,6 +156,9 @@ public class Communicator
// extract bootstrap information
+ // create our distributed object manager
+ _omgr = new ClientDObjectMgr(this, _client);
+
// create a new writer thread and start it up
if (_writer != null) {
throw new RuntimeException("Writer already started!?");
@@ -176,6 +191,23 @@ public class Communicator
logoff();
}
+ /**
+ * Callback called by the reader if the server closes the other end of
+ * the connection.
+ */
+ protected synchronized void connectionClosed ()
+ {
+ // make sure the socket isn't already closed down (meaning we've
+ // already dealt with the closed connection)
+ if (_socket == null) {
+ return;
+ }
+
+ Log.info("Connection closed.");
+ // now do the whole logoff thing
+ logoff();
+ }
+
/**
* Callback called by the reader thread when it goes away.
*/
@@ -239,6 +271,8 @@ public class Communicator
protected void processMessage (DownstreamMessage msg)
{
Log.info("Process msg: " + msg);
+ // post this message to the dobjmgr queue
+ _omgr.processMessage(msg);
}
/**
@@ -342,8 +376,10 @@ public class Communicator
Log.info("Reader thread woken up in time to die.");
} catch (EOFException eofe) {
- Log.info("Connection closed by peer.");
- // nothing left for us to do
+ // let the communicator know that our connection was
+ // closed
+ connectionClosed();
+ // and shut ourselves down
shutdown();
} catch (IOException ioe) {
@@ -443,4 +479,6 @@ public class Communicator
/** We use this to frame our downstream messages. */
protected FramedInputStream _fin;
protected DataInputStream _din;
+
+ protected ClientDObjectMgr _omgr;
}
diff --git a/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java
index 4f941ccf9..4fb8e1b76 100644
--- a/src/java/com/threerings/presents/dobj/DObject.java
+++ b/src/java/com/threerings/presents/dobj/DObject.java
@@ -1,5 +1,5 @@
//
-// $Id: DObject.java,v 1.8 2001/06/01 20:35:39 mdb Exp $
+// $Id: DObject.java,v 1.9 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.dobj;
@@ -73,20 +73,6 @@ import java.util.ArrayList;
*/
public class DObject
{
- /**
- * Initializes this distributed object with the supplied object id and
- * distributed object manager. This is called by the distributed
- * object manager when an object is created and registered with the
- * system. Don't call this function yourself.
- *
- * @see DObjectManager.createObject
- */
- public void init (int oid, DObjectManager mgr)
- {
- _oid = oid;
- _mgr = mgr;
- }
-
/**
* Returns the object id of this object. All objects in the system
* have a unique object id.
@@ -97,15 +83,13 @@ public class DObject
}
/**
- * Adds the supplied subscriber to the subscriber list for this
- * object. This is done automatically when an object is requested for
- * subscription by the distributed object manager, thus this function
- * should not be called directly except in circumstances where one
- * subscriber has already obtained a subscription to an object and
- * wishes to include a subordinate subscriber in on the fun.
+ * Don't call this function! Go through the distributed object manager
+ * instead to ensure that everything is done on the proper thread.
+ * This function can only safely be called directly when you know you
+ * are operating on the omgr thread (you are in the middle of a call
+ * to objectAvailable or handleEvent).
*
- *
If the specified subscriber is already subscribed to this
- * object, they will not be added to the list a second time.
+ * @see DObjectManager.subscribeToObject
*/
public void addSubscriber (Subscriber sub)
{
@@ -115,12 +99,13 @@ public class DObject
}
/**
- * Removes the specified subscriber from the subscriber list for this
- * object. This is done automatically when a subscriber returns false
- * from handleEvent, but can also be done directly
- * through a call to removeSubscriber. If the specified
- * subscriber is not currently on the list of subscribers for this
- * object, nothing happens.
+ * Don't call this function! Go through the distributed object manager
+ * instead to ensure that everything is done on the proper thread.
+ * This function can only safely be called directly when you know you
+ * are operating on the omgr thread (you are in the middle of a call
+ * to objectAvailable or handleEvent).
+ *
+ * @see DObjectManager.unsubscribeFromObject
*/
public void removeSubscriber (Subscriber sub)
{
@@ -129,13 +114,12 @@ public class DObject
/**
* Checks to ensure that the specified subscriber has access to this
- * object. This will be called before satisfying any fetch or
- * subscription request. By default objects are accessible to all
- * subscriber, but certain objects may wish to implement more fine
- * grained access control.
+ * object. This will be called before satisfying a subscription
+ * request. By default objects are accessible to all subscribers, but
+ * certain objects may wish to implement more fine grained access
+ * control.
*
- * @param sub the subscriber that will fetch or subscribe to this
- * object.
+ * @param sub the subscriber that will subscribe to this object.
*
* @return true if the subscriber has access to the object, false if
* they do not.
@@ -201,6 +185,30 @@ public class DObject
}
}
+ /**
+ * Don't call this function! It initializes this distributed object
+ * with the supplied distributed object manager. This is called by the
+ * distributed object manager when an object is created and registered
+ * with the system.
+ *
+ * @see DObjectManager.createObject
+ */
+ public void setManager (DObjectManager mgr)
+ {
+ _mgr = mgr;
+ }
+
+ /**
+ * Don't call this function. It is called by the distributed object
+ * manager when an object is created and registered with the system.
+ *
+ * @see DObjectManager.createObject
+ */
+ public void setOid (int oid)
+ {
+ _oid = oid;
+ }
+
protected int _oid;
protected DObjectManager _mgr;
protected ArrayList _subscribers = new ArrayList();
diff --git a/src/java/com/threerings/presents/dobj/DObjectManager.java b/src/java/com/threerings/presents/dobj/DObjectManager.java
index 38b66282f..425acf633 100644
--- a/src/java/com/threerings/presents/dobj/DObjectManager.java
+++ b/src/java/com/threerings/presents/dobj/DObjectManager.java
@@ -1,5 +1,5 @@
//
-// $Id: DObjectManager.java,v 1.4 2001/06/05 22:44:31 mdb Exp $
+// $Id: DObjectManager.java,v 1.5 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.dobj;
@@ -49,25 +49,6 @@ public interface DObjectManager
*/
public void subscribeToObject (int oid, Subscriber target);
- /**
- * Fetches an up-to-date copy of the specified distributed object and
- * makes it available to the subscriber for a one-time access. The
- * subscriber will not be added to the object's subscriber list and
- * will not be notified of updates to the object and the object
- * represents a snapshot in time which, it should be acknowledged,
- * could be out of date by the time it reaches the subscriber. If the
- * object cannot be fetched for some reason, the subscriber will be
- * notified via requestFailed.
- *
- * @param oid The object id of the distributed object of which a
- * snapshot is desired.
- * @param target The subscriber that will receive the snapshot.
- *
- * @see Subscriber.objectAvailable
- * @see Subscriber.requestFailed
- */
- public void fetchObject (int oid, Subscriber target);
-
/**
* Requests that the specified subscriber be unsubscribed from the
* object identified by the supplied object id.
diff --git a/src/java/com/threerings/presents/dobj/Subscriber.java b/src/java/com/threerings/presents/dobj/Subscriber.java
index cfdcd5ba7..4792cc0c1 100644
--- a/src/java/com/threerings/presents/dobj/Subscriber.java
+++ b/src/java/com/threerings/presents/dobj/Subscriber.java
@@ -1,5 +1,5 @@
//
-// $Id: Subscriber.java,v 1.3 2001/06/02 01:30:37 mdb Exp $
+// $Id: Subscriber.java,v 1.4 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.dobj;
@@ -22,17 +22,15 @@ public interface Subscriber
* should not attempt to modify the object.
*
* @see DObjectManager.subscribeToObject
- * @see DObjectManager.fetchObject
*/
public void objectAvailable (DObject object);
/**
- * Called when a subscription or fetch request has failed. The nature
- * of the failure will be communicated via the supplied
+ * Called when a subscription request has failed. The nature of the
+ * failure will be communicated via the supplied
* ObjectAccessException.
*
* @see DObjectManager.subscribeToObject
- * @see DObjectManager.fetchObject
*/
public void requestFailed (int oid, ObjectAccessException cause);
diff --git a/src/java/com/threerings/presents/dobj/io/DObjectFactory.java b/src/java/com/threerings/presents/dobj/io/DObjectFactory.java
index 22658ca07..a687f073c 100644
--- a/src/java/com/threerings/presents/dobj/io/DObjectFactory.java
+++ b/src/java/com/threerings/presents/dobj/io/DObjectFactory.java
@@ -1,5 +1,5 @@
//
-// $Id: DObjectFactory.java,v 1.3 2001/05/30 23:58:31 mdb Exp $
+// $Id: DObjectFactory.java,v 1.4 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.dobj.net;
@@ -32,6 +32,8 @@ public class DObjectFactory
Marshaller marsh = getMarshaller(clazz);
// then write the class of the object to the stream
out.writeUTF(clazz.getName());
+ // write out the oid
+ out.writeInt(dobj.getOid());
// then use the marshaller to write the object itself
marsh.writeTo(out, dobj);
}
@@ -46,6 +48,7 @@ public class DObjectFactory
// read in the class name and create an instance of that class
Class clazz = Class.forName(in.readUTF());
DObject dobj = (DObject)clazz.newInstance();
+ dobj.setOid(in.readInt()); // read and set the oid
Log.info("Unmarshalling object: " + dobj);
// look up the marshaller for that class
diff --git a/src/java/com/threerings/presents/io/Marshaller.java b/src/java/com/threerings/presents/io/Marshaller.java
index 27121342c..39201821b 100644
--- a/src/java/com/threerings/presents/io/Marshaller.java
+++ b/src/java/com/threerings/presents/io/Marshaller.java
@@ -1,5 +1,5 @@
//
-// $Id: Marshaller.java,v 1.2 2001/05/30 23:58:31 mdb Exp $
+// $Id: Marshaller.java,v 1.3 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.dobj.net;
@@ -7,7 +7,10 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Comparator;
import com.threerings.cocktail.cher.Log;
@@ -24,7 +27,24 @@ public class Marshaller
public Marshaller (Class clazz)
{
// we introspect on the class and cache the public data members
- _fields = clazz.getFields();
+ Field[] fields = clazz.getFields();
+ ArrayList flist = new ArrayList();
+
+ // we only want non-static, non-final fields
+ for (int i = 0; i < fields.length; i++) {
+ int mods = fields[i].getModifiers();
+ if ((mods & Modifier.PUBLIC) == 0 ||
+ (mods & Modifier.STATIC) != 0 ||
+ (mods & Modifier.FINAL) != 0) {
+ continue;
+ }
+ flist.add(fields[i]);
+ }
+
+ // create an array of the fields we want
+ _fields = new Field[flist.size()];
+ flist.toArray(_fields);
+
// sort the fields so that they are written and read in the same
// order on all VMs
Arrays.sort(_fields, FIELD_COMP);
@@ -83,6 +103,7 @@ public class Marshaller
String errmsg = "Unable to unmarshall dobj field " +
"[field=" + _fields[i].getName() +
", dobj=" + dobj + "].";
+ Log.logStackTrace(iae);
throw new ObjectStreamException(errmsg);
}
}
diff --git a/src/java/com/threerings/presents/io/StringFieldMarshaller.java b/src/java/com/threerings/presents/io/StringFieldMarshaller.java
index aeac223b1..dc475b671 100644
--- a/src/java/com/threerings/presents/io/StringFieldMarshaller.java
+++ b/src/java/com/threerings/presents/io/StringFieldMarshaller.java
@@ -1,5 +1,5 @@
//
-// $Id: StringFieldMarshaller.java,v 1.2 2001/05/30 23:58:31 mdb Exp $
+// $Id: StringFieldMarshaller.java,v 1.3 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.dobj.net;
@@ -18,7 +18,12 @@ public class StringFieldMarshaller implements FieldMarshaller
public void writeTo (DataOutputStream out, Field field, DObject dobj)
throws IOException, IllegalAccessException
{
- out.writeUTF((String)field.get(dobj));
+ String value = (String)field.get(dobj);
+ // we convert null strings to empty strings
+ if (value == null) {
+ value = "";
+ }
+ out.writeUTF(value);
}
public void readFrom (DataInputStream in, Field field, DObject dobj)
diff --git a/src/java/com/threerings/presents/net/EventNotification.java b/src/java/com/threerings/presents/net/EventNotification.java
index b5c54b5e8..743ffc295 100644
--- a/src/java/com/threerings/presents/net/EventNotification.java
+++ b/src/java/com/threerings/presents/net/EventNotification.java
@@ -1,5 +1,5 @@
//
-// $Id: EventNotification.java,v 1.5 2001/06/02 01:30:37 mdb Exp $
+// $Id: EventNotification.java,v 1.6 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.net;
@@ -35,6 +35,11 @@ public class EventNotification extends DownstreamMessage
return TYPE;
}
+ public DEvent getEvent ()
+ {
+ return _event;
+ }
+
public void writeTo (DataOutputStream out)
throws IOException
{
diff --git a/src/java/com/threerings/presents/net/FailureResponse.java b/src/java/com/threerings/presents/net/FailureResponse.java
index 6aa78933b..f8438bc25 100644
--- a/src/java/com/threerings/presents/net/FailureResponse.java
+++ b/src/java/com/threerings/presents/net/FailureResponse.java
@@ -1,5 +1,5 @@
//
-// $Id: FailureResponse.java,v 1.3 2001/06/02 01:30:37 mdb Exp $
+// $Id: FailureResponse.java,v 1.4 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.net;
@@ -10,7 +10,7 @@ import java.io.DataOutputStream;
public class FailureResponse extends DownstreamMessage
{
/** The code for a logoff notification. */
- public static final short TYPE = TYPE_BASE + 3;
+ public static final short TYPE = TYPE_BASE + 4;
/**
* Zero argument constructor used when unserializing an instance.
diff --git a/src/java/com/threerings/presents/net/FetchRequest.java b/src/java/com/threerings/presents/net/FetchRequest.java
deleted file mode 100644
index f3ec531f8..000000000
--- a/src/java/com/threerings/presents/net/FetchRequest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// $Id: FetchRequest.java,v 1.3 2001/06/05 22:44:31 mdb Exp $
-
-package com.threerings.cocktail.cher.net;
-
-import java.io.IOException;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-
-public class FetchRequest extends UpstreamMessage
-{
- /** The code for an object fetch request. */
- public static final short TYPE = TYPE_BASE + 2;
-
- /**
- * Zero argument constructor used when unserializing an instance.
- */
- public FetchRequest ()
- {
- super();
- }
-
- /**
- * Constructs a fetch request for the distributed object with the
- * specified object id.
- */
- public FetchRequest (int oid)
- {
- _oid = oid;
- }
-
- public short getType ()
- {
- return TYPE;
- }
-
- /**
- * Returns the oid of the object we desire to fetch.
- */
- public int getOid ()
- {
- return _oid;
- }
-
- public void writeTo (DataOutputStream out)
- throws IOException
- {
- super.writeTo(out);
- out.writeInt(_oid);
- }
-
- public void readFrom (DataInputStream in)
- throws IOException
- {
- super.readFrom(in);
- _oid = in.readInt();
- }
-
- /**
- * The object id of the distributed object which we are fetching.
- */
- protected int _oid;
-}
diff --git a/src/java/com/threerings/presents/net/ObjectResponse.java b/src/java/com/threerings/presents/net/ObjectResponse.java
index ca32017c1..2d2af79f4 100644
--- a/src/java/com/threerings/presents/net/ObjectResponse.java
+++ b/src/java/com/threerings/presents/net/ObjectResponse.java
@@ -1,5 +1,5 @@
//
-// $Id: ObjectResponse.java,v 1.6 2001/06/02 01:30:37 mdb Exp $
+// $Id: ObjectResponse.java,v 1.7 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.net;
@@ -12,7 +12,7 @@ import com.threerings.cocktail.cher.dobj.net.DObjectFactory;
public class ObjectResponse extends DownstreamMessage
{
- /** The code for an event notification. */
+ /** The code for an object repsonse. */
public static final short TYPE = TYPE_BASE + 2;
/**
@@ -24,7 +24,7 @@ public class ObjectResponse extends DownstreamMessage
}
/**
- * Constructs an object response with supplied distributed object.
+ * Constructs an object response with the supplied distributed object.
*/
public ObjectResponse (DObject dobj)
{
@@ -36,6 +36,11 @@ public class ObjectResponse extends DownstreamMessage
return TYPE;
}
+ public DObject getObject ()
+ {
+ return _dobj;
+ }
+
public void writeTo (DataOutputStream out)
throws IOException
{
diff --git a/src/java/com/threerings/presents/net/PongResponse.java b/src/java/com/threerings/presents/net/PongResponse.java
index 4068939b4..1d963482c 100644
--- a/src/java/com/threerings/presents/net/PongResponse.java
+++ b/src/java/com/threerings/presents/net/PongResponse.java
@@ -1,12 +1,12 @@
//
-// $Id: PongResponse.java,v 1.3 2001/06/05 21:53:45 mdb Exp $
+// $Id: PongResponse.java,v 1.4 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.net;
public class PongResponse extends DownstreamMessage
{
/** The code for a pong response. */
- public static final short TYPE = TYPE_BASE + 4;
+ public static final short TYPE = TYPE_BASE + 5;
/**
* Zero argument constructor used when unserializing an instance.
diff --git a/src/java/com/threerings/presents/net/Registry.java b/src/java/com/threerings/presents/net/Registry.java
index 7a0b066e0..1623f251d 100644
--- a/src/java/com/threerings/presents/net/Registry.java
+++ b/src/java/com/threerings/presents/net/Registry.java
@@ -1,5 +1,5 @@
//
-// $Id: Registry.java,v 1.4 2001/06/05 21:53:45 mdb Exp $
+// $Id: Registry.java,v 1.5 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.net;
@@ -25,8 +25,6 @@ public class Registry
AuthRequest.class);
TypedObjectFactory.registerClass(SubscribeRequest.TYPE,
SubscribeRequest.class);
- TypedObjectFactory.registerClass(FetchRequest.TYPE,
- FetchRequest.class);
TypedObjectFactory.registerClass(UnsubscribeRequest.TYPE,
UnsubscribeRequest.class);
TypedObjectFactory.registerClass(ForwardEventRequest.TYPE,
diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java
index ccb2d5e71..2ddfb0daa 100644
--- a/src/java/com/threerings/presents/server/ClientManager.java
+++ b/src/java/com/threerings/presents/server/ClientManager.java
@@ -1,5 +1,5 @@
//
-// $Id: ClientManager.java,v 1.4 2001/06/05 22:50:08 mdb Exp $
+// $Id: ClientManager.java,v 1.5 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.server;
@@ -56,6 +56,9 @@ public class ClientManager implements ConnectionObserver
client = new Client(this, username, conn);
_usermap.put(username, conn);
}
+
+ // map this connection to this client
+ _conmap.put(conn, client);
}
/**
@@ -71,7 +74,7 @@ public class ClientManager implements ConnectionObserver
void connectionFailed (Connection conn, IOException fault)
{
// remove the client from the connection map
- Client client = (Client)_connmap.remove(conn);
+ Client client = (Client)_conmap.remove(conn);
if (client != null) {
Log.info("Unmapped failed client [client=" + client +
", conn=" + conn + ", fault=" + fault + "].");
@@ -92,7 +95,7 @@ public class ClientManager implements ConnectionObserver
public synchronized void connectionClosed (Connection conn)
{
// remove the client from the connection map
- Client client = (Client)_connmap.remove(conn);
+ Client client = (Client)_conmap.remove(conn);
if (client != null) {
Log.info("Unmapped client [client=" + client +
", conn=" + conn + "].");
@@ -125,5 +128,5 @@ public class ClientManager implements ConnectionObserver
}
protected HashMap _usermap = new HashMap();
- protected HashMap _connmap = new HashMap();
+ protected HashMap _conmap = new HashMap();
}
diff --git a/src/java/com/threerings/presents/server/PresentsClient.java b/src/java/com/threerings/presents/server/PresentsClient.java
index f51107f4e..e4d1e7b36 100644
--- a/src/java/com/threerings/presents/server/PresentsClient.java
+++ b/src/java/com/threerings/presents/server/PresentsClient.java
@@ -1,5 +1,5 @@
//
-// $Id: PresentsClient.java,v 1.2 2001/06/05 22:44:31 mdb Exp $
+// $Id: PresentsClient.java,v 1.3 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.server;
@@ -192,21 +192,6 @@ public class Client implements Subscriber, MessageHandler
}
}
- /**
- * Processes fetch requests.
- */
- protected static class FetchDispatcher implements MessageDispatcher
- {
- public void dispatch (Client client, UpstreamMessage msg)
- {
- FetchRequest req = (FetchRequest)msg;
- Log.info("Fetching [client=" + client +
- ", oid=" + req.getOid() + "].");
- // forward the fetch request to the omgr for processing
- CherServer.omgr.fetchObject(req.getOid(), client);
- }
- }
-
/**
* Processes unsubscribe requests.
*/
@@ -287,7 +272,6 @@ public class Client implements Subscriber, MessageHandler
// register our message dispatchers
static {
_disps.put(SubscribeRequest.class, new SubscribeDispatcher());
- _disps.put(FetchRequest.class, new FetchDispatcher());
_disps.put(UnsubscribeRequest.class, new UnsubscribeDispatcher());
_disps.put(ForwardEventRequest.class, new ForwardEventDispatcher());
_disps.put(PingRequest.class, new PingDispatcher());
diff --git a/src/java/com/threerings/presents/server/PresentsDObjectMgr.java b/src/java/com/threerings/presents/server/PresentsDObjectMgr.java
index b7507fd14..fad669009 100644
--- a/src/java/com/threerings/presents/server/PresentsDObjectMgr.java
+++ b/src/java/com/threerings/presents/server/PresentsDObjectMgr.java
@@ -1,5 +1,5 @@
//
-// $Id: PresentsDObjectMgr.java,v 1.4 2001/06/05 22:44:31 mdb Exp $
+// $Id: PresentsDObjectMgr.java,v 1.5 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.server;
@@ -32,7 +32,8 @@ public class CherDObjectMgr implements DObjectManager
// we create a dummy object to live as oid zero and we'll use that
// for some internal event trickery
DObject dummy = new DObject();
- dummy.init(0, this);
+ dummy.setOid(0);
+ dummy.setManager(this);
_objects.put(0, new DObject());
}
@@ -52,14 +53,6 @@ public class CherDObjectMgr implements DObjectManager
AccessObjectEvent.SUBSCRIBE));
}
- // inherit documentation from the interface
- public void fetchObject (int oid, Subscriber target)
- {
- // queue up an access object event
- postEvent(new AccessObjectEvent(oid, target,
- AccessObjectEvent.FETCH));
- }
-
// inherit documentation from the interface
public void unsubscribeFromObject (int oid, Subscriber target)
{
@@ -192,10 +185,14 @@ public class CherDObjectMgr implements DObjectManager
DObject obj = (DObject)_class.newInstance();
// initialize this object
- obj.init(oid, CherDObjectMgr.this);
+ obj.setOid(oid);
+ obj.setManager(CherDObjectMgr.this);
// insert it into the table
_objects.put(oid, obj);
+ Log.info("Created object [oid=" + oid +
+ ", obj=" + obj + "].");
+
if (_target != null) {
// add the subscriber to this object's subscriber list
// if they requested it
@@ -239,8 +236,7 @@ public class CherDObjectMgr implements DObjectManager
protected class AccessObjectEvent extends DEvent
{
public static final int SUBSCRIBE = 0;
- public static final int FETCH = 1;
- public static final int UNSUBSCRIBE = 2;
+ public static final int UNSUBSCRIBE = 1;
public AccessObjectEvent (int oid, Subscriber target,
int action)
@@ -278,10 +274,8 @@ public class CherDObjectMgr implements DObjectManager
return false;
}
- // if they wanted to subscribe, do so
- if (_action == SUBSCRIBE) {
- obj.addSubscriber(_target);
- }
+ // subscribe 'em
+ obj.addSubscriber(_target);
// let them know that things are groovy
_target.objectAvailable(obj);
diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java
index 8b247734a..fcbc5c8d7 100644
--- a/src/java/com/threerings/presents/server/PresentsServer.java
+++ b/src/java/com/threerings/presents/server/PresentsServer.java
@@ -1,5 +1,5 @@
//
-// $Id: PresentsServer.java,v 1.4 2001/06/01 22:12:03 mdb Exp $
+// $Id: PresentsServer.java,v 1.5 2001/06/09 23:39:04 mdb Exp $
package com.threerings.cocktail.cher.server;
@@ -8,6 +8,8 @@ import com.threerings.cocktail.cher.dobj.DObjectManager;
import com.threerings.cocktail.cher.server.net.AuthManager;
import com.threerings.cocktail.cher.server.net.ConnectionManager;
+import com.threerings.cocktail.cher.server.test.TestObject;
+
/**
* The cher server provides a central point of access to the various
* facilities that make up the cher layer of the system.
@@ -41,6 +43,9 @@ public class CherServer
// create our distributed object manager
omgr = new CherDObjectMgr();
+ // create an object for testing
+ omgr.createObject(TestObject.class, null, false);
+
} catch (Exception e) {
Log.warning("Unable to initialize server.");
Log.logStackTrace(e);
diff --git a/tests/src/java/com/threerings/presents/client/TestClient.java b/tests/src/java/com/threerings/presents/client/TestClient.java
index 35311e535..cf5ef812d 100644
--- a/tests/src/java/com/threerings/presents/client/TestClient.java
+++ b/tests/src/java/com/threerings/presents/client/TestClient.java
@@ -1,22 +1,99 @@
//
-// $Id: TestClient.java,v 1.2 2001/05/30 23:58:31 mdb Exp $
+// $Id: TestClient.java,v 1.3 2001/06/09 23:39:03 mdb Exp $
package com.threerings.cocktail.cher.client.test;
+import com.samskivert.util.Queue;
+
+import com.threerings.cocktail.cher.Log;
import com.threerings.cocktail.cher.net.*;
import com.threerings.cocktail.cher.client.*;
+import com.threerings.cocktail.cher.dobj.*;
+
+import com.threerings.cocktail.cher.server.test.TestObject;
/**
* A standalone test client.
*/
public class TestClient
+ implements Client.Invoker, ClientObserver, Subscriber
{
+ public void invokeLater (Runnable run)
+ {
+ // queue it on up
+ _queue.append(run);
+ }
+
+ public void run ()
+ {
+ // loop over our queue, running the runnables
+ while (true) {
+ Runnable run = (Runnable)_queue.get();
+ run.run();
+ }
+ }
+
+ public void clientDidLogon (Client client)
+ {
+ Log.info("Client did logon [client=" + client + "].");
+ // try subscribing to a test object
+ client.getDObjectManager().subscribeToObject(1, this);
+ }
+
+ public void clientFailedToLogon (Client client, Exception cause)
+ {
+ Log.info("Client failed to logon [client=" + client +
+ ", cause=" + cause + "].");
+ }
+
+ public void clientConnectionFailed (Client client, Exception cause)
+ {
+ Log.info("Client connection failed [client=" + client +
+ ", cause=" + cause + "].");
+ }
+
+ public boolean clientWillLogoff (Client client)
+ {
+ Log.info("Client will logoff [client=" + client + "].");
+ return true;
+ }
+
+ public void clientDidLogoff (Client client)
+ {
+ Log.info("Client did logoff [client=" + client + "].");
+ System.exit(0);
+ }
+
+ public void objectAvailable (DObject object)
+ {
+ Log.info("Object available: " + object);
+ ((TestObject)object).setBar("lawl!");
+ }
+
+ public void requestFailed (int oid, ObjectAccessException cause)
+ {
+ Log.info("Object unavailable [oid=" + oid +
+ ", reason=" + cause + "].");
+ }
+
+ public boolean handleEvent (DEvent event, DObject target)
+ {
+ Log.info("Got event [event=" + event + ", target=" + target + "].");
+ return true;
+ }
+
public static void main (String[] args)
{
+ TestClient tclient = new TestClient();
UsernamePasswordCreds creds =
new UsernamePasswordCreds("test", "test");
- Client client = new Client(creds);
+ Client client = new Client(creds, tclient);
+ client.addObserver(tclient);
client.setServer("localhost", 4007);
client.logon();
+ // start up our event processing loop
+ tclient.run();
}
+
+ protected Queue _queue = new Queue();
}