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(); }