Further wired up the client side of the distributed object system. Removed
the facilities for fetching (without subscribing to) an object. This is done extremely rarely and the user might as well just subscribe and immediately unsubscribe because the dichotomy between fetching and subscribing just served to overly complicate the internals for no good reason. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@30 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 <code>objectAvailable</code> or <code>handleEvent</code>).
|
||||
*
|
||||
* <p> 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 <code>handleEvent</code>, but can also be done directly
|
||||
* through a call to <code>removeSubscriber</code>. 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 <code>objectAvailable</code> or <code>handleEvent</code>).
|
||||
*
|
||||
* @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();
|
||||
|
||||
@@ -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 <code>requestFailed</code>.
|
||||
*
|
||||
* @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.
|
||||
|
||||
@@ -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
|
||||
* <code>ObjectAccessException</code>.
|
||||
*
|
||||
* @see DObjectManager.subscribeToObject
|
||||
* @see DObjectManager.fetchObject
|
||||
*/
|
||||
public void requestFailed (int oid, ObjectAccessException cause);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user