Initial revision of cocktail multiplayer networked gaming platform. (Far

from complete.)


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2001-05-22 06:08:00 +00:00
parent 53d02843bb
commit 40ecd80e09
25 changed files with 1544 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
//
// $Id: Log.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher;
/**
* A placeholder class that contains a reference to the log object used by
* the Spine package.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("robodj");
/** Convenience function. */
public static void debug (String message)
{
log.debug(message);
}
/** Convenience function. */
public static void info (String message)
{
log.info(message);
}
/** Convenience function. */
public static void warning (String message)
{
log.warning(message);
}
/** Convenience function. */
public static void logStackTrace (Throwable t)
{
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
}
}
@@ -0,0 +1,197 @@
//
// $Id: Client.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.client;
import java.util.ArrayList;
import java.util.List;
import com.samskivert.cocktail.cher.Log;
import com.samskivert.cocktail.cher.net.Credentials;
/**
* 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.
*/
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.
*/
public Client (Credentials creds)
{
_creds = creds;
}
/**
* Registers the supplied observer with this client. While registered
* the observer will receive notifications of state changes within the
* client. The function will refuse to register an already registered
* observer.
*
* @see ClientObserver
*/
public void addObserver (ClientObserver observer)
{
synchronized (_observers) {
// disallow multiple instances of the same observer
if (!_observers.contains(observer)) {
_observers.add(observer);
}
}
}
/**
* Unregisters the supplied observer. Upon return of this function,
* the observer will no longer receive notifications of state changes
* within the client.
*/
public void removeObserver (ClientObserver observer)
{
synchronized (_observers) {
_observers.remove(observer);
}
}
/**
* Configures the client to communicate with the server on the
* supplied hostname/port combination.
*
* @see #logon
*/
public void setServer (String hostname, int port)
{
_hostname = hostname;
_port = port;
}
/**
* Returns the hostname of the server to which this client is
* currently configured to connect.
*/
public String getHostname ()
{
return _hostname;
}
/**
* Returns the port on which this client is currently configured to
* connect to the server.
*/
public int getPort ()
{
return _port;
}
/**
* Returns the credentials with which this client is currently
* configured to connect to the server.
*/
public Credentials getCredentials ()
{
return _creds;
}
/**
* Requests that this client connect and logon to the server with
* which it was previously configured.
*/
public synchronized void logon ()
{
// if we have a communicator reference, we're already logged on
if (_comm != null) {
return;
}
// otherwise create a new communicator instance and start it up.
// this will initiate the logon process
_comm = new Communicator(this);
_comm.logon();
}
/**
* Requests that the client log off of the server to which it is
* connected.
*
* @param abortable If true, the client will call
* <code>clientWillDisconnect</code> on all of the client observers
* and abort the logoff process if any of them return false. If false,
* <code>clientWillDisconnect</code> will not be called at all.
*
* @return true if the logoff succeeded, false if it failed due to a
* disagreeable observer.
*/
public boolean logoff (boolean abortable)
{
// if the request is abortable, let's run it past the observers
// before we act upon it
if (abortable && notifyObservers(CLIENT_WILL_LOGOFF, null)) {
return false;
}
// ask the communicator to send a logoff message and disconnect
// from the server
_comm.logoff();
return true;
}
boolean notifyObservers (int code, Exception cause)
{
boolean rejected = false;
synchronized (_observers) {
for (int i = 0; i < _observers.size(); i++) {
ClientObserver obs = (ClientObserver)_observers.get(i);
switch (code) {
case CLIENT_DID_LOGON:
obs.clientDidLogon(this);
break;
case CLIENT_FAILED_TO_LOGON:
obs.clientFailedToLogon(this, cause);
break;
case CLIENT_CONNETION_FAILED:
obs.clientConnectionFailed(this, cause);
break;
case CLIENT_WILL_LOGOFF:
if (!obs.clientWillLogoff(this)) {
rejected = true;
}
break;
case CLIENT_DID_LOGOFF:
obs.clientDidLogoff(this);
break;
default:
throw new RuntimeException("Invalid code supplied to " +
"notifyObservers: " + code);
}
}
}
return rejected;
}
synchronized void communicatorDidExit ()
{
// clear out our communicator reference
_comm = null;
}
protected Credentials _creds;
protected String _hostname;
protected int _port;
/** Our list of client observers. */
protected List _observers = new ArrayList();
/** The entity that manages our network communications. */
protected Communicator _comm;
// client observer codes
static final int CLIENT_DID_LOGON = 0;
static final int CLIENT_FAILED_TO_LOGON = 1;
static final int CLIENT_CONNETION_FAILED = 2;
static final int CLIENT_WILL_LOGOFF = 3;
static final int CLIENT_DID_LOGOFF = 4;
}
@@ -0,0 +1,72 @@
//
// $Id: ClientObserver.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.client;
/**
* A client observer is registered with the client instance to be notified
* when state changes happen within the client. Specifically, logon
* sucess/failure and logoff.
*
* <p> These callbacks happen on the client thread and should therefore
* not be used to perform any complex action or do much more than relay
* the signal to some other thread (like the AWT thread) to act more fully
* on the notice.
*
* <p> In the normal course of affairs, <code>clientDidLogon</code> will
* be called after the client successfully logs on to the server and
* <code>clientDidLogoff</code> will be called after the client logs off
* of the server. If logon fails for any reson,
* <code>clientFailedToLogon</code> will be called to explain the failure.
*
* <p> <code>clientWillLogoff</code> will only be called when an abortable
* logoff is requested (like when the user clicks on a logoff button of
* some sort). It will not be called during non-abortable logoff requests
* (like when the browser calls stop on the applet and is about to yank
* the rug out from under us). If an observer aborts the logoff request,
* it should notify the user in some way why the request was aborted
* (<em>but it shouldn't do so on the thread that calls
* <code>clientWillLogoff</code></em>).
*
* <p> If the client connection fails unexpectedly,
* <code>clientConnectionFailed</code> will be called to let the
* observers know that we lost our connection to the
* server. <code>clientDidLogoff</code> will be called immediately
* afterwards as a normal logoff procedure is effected.
*/
public interface ClientObserver
{
/**
* Called after the client successfully connected to and authenticated
* with the server. The entire object system is up and running by the
* time this method is called.
*/
public void clientDidLogon (Client client);
/**
* Called if anything fails during the logon attempt. This could be a
* network failure, authentication failure or otherwise. The exception
* provided will indicate the cause of the failure.
*/
public void clientFailedToLogon (Client client, Exception cause);
/**
* Called when the connection to the server went away for some
* unexpected reason. This will be followed by a call to
* <code>clientDidLogoff</code>.
*/
public void clientConnectionFailed (Client client, Exception cause);
/**
* Called when an abortable logoff requrest is made. If the observer
* returns false from this method, the client will abort the logoff
* request.
*/
public boolean clientWillLogoff (Client client);
/**
* Called after the client has been logged off of the server and has
* disconnected.
*/
public void clientDidLogoff (Client client);
}
@@ -0,0 +1,175 @@
//
// $Id: Communicator.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.client;
import java.io.*;
import java.net.Socket;
import java.net.InetAddress;
import com.samskivert.util.Queue;
import com.samskivert.cocktail.cher.Log;
import com.samskivert.cocktail.cher.net.*;
/**
* The client performs all network I/O on separate threads (one for
* reading and one for writing). The communicator class encapsulates that
* functionality.
*
* <pre>
* Logon synopsis:
*
* Client.logon():
* - Calls Communicator.start()
* Communicator.start():
* - spawn Reader thread
* Reader.run():
* { - connect
* - authenticate
* } if either fail, notify observers of failed logon
* - start writer thread
* - notify observers that we're logged on
* - read loop
* Writer.run():
* - write loop
* </pre>
*/
class Communicator
{
/**
* Creates a new communicator instance which is associated with the
* supplied client.
*/
public Communicator (Client client)
{
_client = client;
}
/**
* Logs on to the server and initiates our full-duplex message
* exchange.
*/
public void logon ()
{
// make sure things are copacetic
if (_reader != null) {
throw new RuntimeException("Communicator already started.");
}
// start up the reader thread. it will connect to the server and
// start up the writer thread if everything went successfully
_reader = new Reader();
_reader.start();
}
/**
* Delivers a logoff notification to the server and shuts down the
* network connection. Also causes all communication threads to
* terminate.
*/
public void logoff ()
{
}
/**
* Queues up the specified message for delivery to the server.
*/
public void postMessage (UpstreamMessage msg)
{
// simply append the message to the queue
_msgq.append(msg);
}
protected void startWriter ()
{
if (_writer != null) {
throw new RuntimeException("Writer already started!?");
}
// create a new writer thread and start it up
_writer = new Writer();
_writer.start();
}
protected synchronized void readerDidExit ()
{
// clear out our reader reference
_reader = null;
// let the client know when we finally go away
_client.communicatorDidExit();
}
protected class Reader extends Thread
{
public void run ()
{
try {
// first we connect and authenticate with the server
try {
// connect to the server
connect();
// then authenticate
logon();
} catch (Exception e) {
// let the observers know that we've failed
_client.notifyObservers(Client.CLIENT_FAILED_TO_LOGON, e);
// and terminate our communicator thread
return;
}
// once authenticated, we go into full-duplex mode,
// starting up another thread to listen for messages while
// we handle the delivery of messages
startWriter();
listen();
} finally {
// let the communicator know when we finally go away
readerDidExit();
}
}
protected void connect ()
throws IOException
{
// if we're already connected, we freak out
if (_socket != null) {
throw new IOException("Already connected.");
}
// look up the address of the target server
InetAddress host = InetAddress.getByName(_client.getHostname());
// establish a socket connection to said server
_socket = new Socket(host, _client.getPort());
}
protected void logon ()
throws LogonException
{
}
protected void listen ()
{
}
}
protected class Writer extends Thread
{
public void run ()
{
}
}
protected Client _client;
protected Reader _reader;
protected Writer _writer;
protected Socket _socket;
protected DataInputStream _din;
protected DataInputStream _dout;
protected Queue _msgq = new Queue();
}
@@ -0,0 +1,20 @@
//
// $Id: LogonException.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.client;
/**
* A logon exception is used to indicate a failure to log on to the
* server. The reason for failure is encoded as a string and stored in the
* message field of the exception.
*/
public class LogonException extends Exception
{
/**
* Constructs a logon exception with the supplied logon failure code.
*/
public LogonException (String code)
{
super(code);
}
}
@@ -0,0 +1,21 @@
//
// $Id: Event.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.dobj;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
public class Event
{
public void writeTo (DataOutputStream out)
throws IOException
{
}
public void readFrom (DataInputStream in)
throws IOException
{
}
}
@@ -0,0 +1,19 @@
//
// $Id: ObjectStreamException.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.io;
/**
* The object stream exception is used to communicate an error in
* processing a typed object stream. As any error is fatal, we don't
* differentiate exceptions with separate classes, but instead communicate
* the nature of the error in the exception message with the assumption
* that the caller will want to raise holy hell in every case.
*/
public class ObjectStreamException extends Exception
{
public ObjectStreamException (String message)
{
super(message);
}
}
@@ -0,0 +1,49 @@
//
// $Id: TypedObject.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.io;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
/**
* A typed object is one that is associated with a particular type code.
* The type code can be communicated on the wire and used by the receiving
* end to instantiate the proper typed object class for decoding.
*/
public abstract class TypedObject
{
/**
* Each typed object class must associate itself with a type value via
* the <code>TypedObjectFactory</code> so that the factory can
* instantiate the proper <code>TypedObject</code> derived class when
* decoding an incoming message.
*
* @return The type code associated with this object.
*/
public abstract short getType ();
/**
* Each typed object class must be able to write itself to a stream.
* It should first call <code>super.writeTo()</code> before writing
* its own fields to the stream.
*/
public void writeTo (DataOutputStream out)
throws IOException
{
out.writeShort(getType());
}
/**
* Each typed object class must be able to read itself from a stream.
* It should first call <code>super.readFrom()</code> before reading
* its own fields from the stream.
*/
public void readFrom (DataInputStream in)
throws IOException
{
// nothing to do because the TypedObjectFactory already read our
// type value from the stream
}
}
@@ -0,0 +1,77 @@
//
// $Id: TypedObjectFactory.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.io;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.HashMap;
/**
* The encodable factory is used to unserialize encodable object instances
* from a stream. It maintains a mapping of encodable object classes to
* identifier codes which it uses to determine what sort of object is on
* the stream. The encodable mechanism is used to associate a particular
* class with some compact code that can be transmitted on the wire to
* identify that class.
*/
public class TypedObjectFactory
{
/**
* Reads (unserializes) a typed object from the supplied data input
* stream.
*
* @return The unserialized typed object instance.
*/
public static TypedObject readFrom (DataInputStream din)
throws IOException, ObjectStreamException
{
// first determine the type of the incoming object
short type = din.readShort();
// now instantiate the proper object and decode the remainder
TypedObject msg = newObjectByType(type);
msg.readFrom(din);
return msg;
}
protected static TypedObject newObjectByType (short type)
throws ObjectStreamException
{
Class clazz = (Class)_classes.get(new Short(type));
if (clazz == null) {
String errmsg = "Unknown object type: " + type;
throw new ObjectStreamException(errmsg);
}
try {
return (TypedObject)clazz.newInstance();
} catch (Throwable t) {
String errmsg = "Unable to instantiate typed object " +
"[class=" + clazz.getName() + ", error=" + t + "].";
throw new ObjectStreamException(errmsg);
}
}
protected static void registerClass (short type, Class clazz)
{
Short key = new Short(type);
// make sure no funny business is afoot
if (_classes.containsKey(key)) {
Class incumbent = (Class)_classes.get(key);
String errmsg = "Cannot register " + clazz.getName() +
" as type " + type + " because " + incumbent.getName() +
" is already registered with that type.";
throw new RuntimeException(errmsg);
}
// set up the mapping
_classes.put(key, clazz);
}
/** Our type to class mapping table. */
protected static HashMap _classes = new HashMap();
}
@@ -0,0 +1,57 @@
//
// $Id: AuthRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.net;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
public class AuthenticationRequest extends UpstreamMessage
{
/** The code for an object subscription request. */
public static final short TYPE = TYPE_BASE + 0;
/**
* Zero argument constructor used when unserializing an instance.
*/
public AuthenticationRequest ()
{
super();
}
/**
* Constructs a authentication request with the supplied credentials.
*/
public AuthenticationRequest (Credentials creds)
{
_creds = creds;
}
public short getType ()
{
return TYPE;
}
public void writeTo (DataOutputStream out)
throws IOException
{
super.writeTo(out);
_creds.writeTo(out);
}
public void readFrom (DataInputStream in)
throws IOException
{
super.readFrom(in);
_creds = (Credentials)TypedObjectFactory.readFrom(in);
}
/**
* The object id of the distributed object to which we are
* subscribing.
*/
protected Credentials _creds;
}
@@ -0,0 +1,33 @@
//
// $Id: Credentials.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.net;
import com.samskivert.cocktail.cher.io.TypedObject;
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
/**
* Credentials are supplied by the client implementation and sent along to
* the server during the authentication process. To provide support for a
* variety of authentication methods, the credentials class is meant to be
* subclassed for the particular method (ie. username + password) in use
* in a given system.
*
* <p> All derived classes should provide a no argument constructor so
* that they can be instantiated prior to reconstruction from a data input
* stream.
*/
public abstract class Credentials extends TypedObject
{
/**
* All credential derived classes should base their typed object code
* on this base value.
*/
public static final short TYPE_BASE = 300;
// register our credential classes
static {
TypedObjectFactory.registerClass(UsernamePasswordCreds.TYPE,
UsernamePasswordCreds.class);
}
}
@@ -0,0 +1,72 @@
//
// $Id: DownstreamMessage.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.net;
import com.samskivert.cocktail.cher.io.TypedObject;
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
/**
* The <code>DownstreamMessage</code> class encapsulates a message in the
* Distributed Object Protocol that flows from the server to the
* client. Downstream messages include object subscription, event
* forwarding and session management.
*/
public class DownstreamMessage
{
/**
* All downstream message derived classes should base their typed
* object code on this base value.
*/
public static final short TYPE_BASE = 200;
/**
* The message id of the upstream message with which this downstream
* message is associated (or -1 if it is not associated with any
* upstream message). Because not every downstream message class cares
* to provide an upstream message id, this field is not serialized
* when the base downstream message class is serialized. Thus derived
* classes that care about message id should take care to initialize,
* serialize and unserialize the value theirselves.
*/
public short messageId = -1;
/**
* Each downstream message derived class must provide a zero argument
* constructor so that the <code>TypedObjectFactory</code> can create
* a new instance of said class prior to unserializing it.
*/
public DownstreamMessage ()
{
// nothing to do...
}
// register our downstream message classes
static {
TypedObjectFactory.registerClass(AuthenticationRequest.TYPE,
AuthenticationRequest.class);
TypedObjectFactory.registerClass(SubscribeRequest.TYPE,
SubscribeRequest.class);
TypedObjectFactory.registerClass(FetchRequest.TYPE,
FetchRequest.class);
TypedObjectFactory.registerClass(UnsubscribeNotification.TYPE,
UnsubscribeNotification.class);
TypedObjectFactory.registerClass(ForwardEventNotification.TYPE,
ForwardEventNotification.class);
TypedObjectFactory.registerClass(PingNotification.TYPE,
PingNotification.class);
TypedObjectFactory.registerClass(LogoffNotification.TYPE,
LogoffNotification.class);
}
/** The code for an event notification. */
public static final byte EVENT = 0x00;
/** The code for a fetch/subscribe object response. */
public static final byte OBJECT = 0x01;
/** The code for a request failure. */
public static final byte FAILURE = 0x02;
/** The code for a pong response. */
public static final byte PONG = 0x03;
/** The code for an end of transmission notification. */
public static final byte EOT = 0x04;
}
@@ -0,0 +1,48 @@
//
// $Id: EventNotification.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.net;
public class EventNotification extends DownstreamMessage
{
/** The code for an event notification. */
public static final short TYPE = TYPE_BASE + 1;
/**
* Zero argument constructor used when unserializing an instance.
*/
public EventNotification ()
{
super();
}
/**
* Constructs an event notification for the supplied event.
*/
public EventNotification (Event event)
{
_event = event;
}
public short getType ()
{
return TYPE;
}
public void writeTo (DataOutputStream out)
throws IOException
{
super.writeTo(out);
_event.writeTo(out);
}
public void readFrom (DataInputStream in)
throws IOException
{
super.readFrom(in);
_event = EventFactory.readFrom(in);
}
/** The event which we are forwarding. */
protected Event _event;
}
@@ -0,0 +1,55 @@
//
// $Id: FetchRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.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;
}
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;
}
@@ -0,0 +1,55 @@
//
// $Id: ForwardEventRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
package com.samskivert.cocktail.cher.net;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import com.samskivert.cocktail.cher.dobj.Event;
import com.samskivert.cocktail.cher.dobj.EventFactory;
public class ForwardEventNotification extends UpstreamMessage
{
/** The code for a forward event notification. */
public static final short TYPE = TYPE_BASE + 4;
/**
* Zero argument constructor used when unserializing an instance.
*/
public ForwardEventNotification ()
{
super();
}
/**
* Constructs a forward event notification for the supplied event.
*/
public ForwardEventNotification (Event event)
{
_event = event;
}
public short getType ()
{
return TYPE;
}
public void writeTo (DataOutputStream out)
throws IOException
{
super.writeTo(out);
_event.writeTo(out);
}
public void readFrom (DataInputStream in)
throws IOException
{
super.readFrom(in);
_event = EventFactory.readFrom(in);
}
/** The event which we are forwarding. */
protected Event _event;
}
@@ -0,0 +1,23 @@
//
// $Id: LogoffRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
package com.samskivert.cocktail.cher.net;
public class LogoffNotification extends UpstreamMessage
{
/** The code for a logoff notification. */
public static final short TYPE = TYPE_BASE + 6;
/**
* Zero argument constructor used when unserializing an instance.
*/
public LogoffNotification ()
{
super();
}
public short getType ()
{
return TYPE;
}
}
@@ -0,0 +1,23 @@
//
// $Id: PingRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
package com.samskivert.cocktail.cher.net;
public class PingNotification extends UpstreamMessage
{
/** The code for a ping notification. */
public static final short TYPE = TYPE_BASE + 5;
/**
* Zero argument constructor used when unserializing an instance.
*/
public PingNotification ()
{
super();
}
public short getType ()
{
return TYPE;
}
}
@@ -0,0 +1,56 @@
//
// $Id: SubscribeRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
package com.samskivert.cocktail.cher.net;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
public class SubscribeRequest extends UpstreamMessage
{
/** The code for an object subscription request. */
public static final short TYPE = TYPE_BASE + 1;
/**
* Zero argument constructor used when unserializing an instance.
*/
public SubscribeRequest ()
{
super();
}
/**
* Constructs a subscribe request for the distributed object with the
* specified object id.
*/
public SubscribeRequest (int oid)
{
_oid = oid;
}
public short getType ()
{
return TYPE;
}
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 to which we are
* subscribing.
*/
protected int _oid;
}
@@ -0,0 +1,56 @@
//
// $Id: UnsubscribeRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
package com.samskivert.cocktail.cher.net;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
public class UnsubscribeNotification extends UpstreamMessage
{
/** The code for an unsubscribe notification. */
public static final short TYPE = TYPE_BASE + 3;
/**
* Zero argument constructor used when unserializing an instance.
*/
public UnsubscribeNotification ()
{
super();
}
/**
* Constructs a unsubscribe notification for the distributed object
* with the specified object id.
*/
public UnsubscribeNotification (int oid)
{
_oid = oid;
}
public short getType ()
{
return TYPE;
}
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 from which we are
* unsubscribing.
*/
protected int _oid;
}
@@ -0,0 +1,105 @@
//
// $Id: UpstreamMessage.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
package com.samskivert.cocktail.cher.net;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import com.samskivert.cocktail.cher.io.TypedObject;
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
/**
* The <code>UpstreamMessage</code> class encapsulates a message in the
* Distributed Object Protocol that flows from the client to the server.
* Upstream messages include object subscription, event forwarding and
* session management.
*/
public abstract class UpstreamMessage extends TypedObject
{
/**
* All upstream message derived classes should base their typed object
* code on this base value.
*/
public static final short TYPE_BASE = 100;
/**
* This is a unique (within the context of a reasonable period of
* time) identifier assigned to each upstream message. The message ids
* are used to correlate a downstream response message to the
* appropriate upstream request message.
*/
public short messageId;
/**
* Each upstream message derived class must provide a zero argument
* constructor so that the <code>TypedObjectFactory</code> can create
* a new instance of said class prior to unserializing it.
*/
public UpstreamMessage ()
{
// automatically generate a valid message id; on the client, this
// will be used, on the server it will be overwritten by the
// unserialized value
this.messageId = nextMessageId();
}
/**
* Derived classes should override this function to write their fields
* out to the supplied data output stream. They <em>must</em> be sure
* to first call <code>super.writeTo()</code>.
*/
public void writeTo (DataOutputStream out)
throws IOException
{
super.writeTo(out);
out.writeShort(messageId);
}
/**
* Derived classes should override this function to read their fields
* from the supplied data input stream. They <em>must</em> be sure to
* first call <code>super.readFrom()</code>.
*/
public void readFrom (DataInputStream in)
throws IOException
{
super.readFrom(in);
messageId = in.readShort();
}
/**
* Returns the next message id suitable for use by an upstream
* message.
*/
protected static synchronized short nextMessageId ()
{
_nextMessageId = (short)((_nextMessageId + 1) % Short.MAX_VALUE);
return _nextMessageId;
}
/**
* This is used to generate monotonically increasing message ids on
* the client as new messages are generated.
*/
protected static short _nextMessageId;
// register our upstream message classes
static {
TypedObjectFactory.registerClass(AuthenticationRequest.TYPE,
AuthenticationRequest.class);
TypedObjectFactory.registerClass(SubscribeRequest.TYPE,
SubscribeRequest.class);
TypedObjectFactory.registerClass(FetchRequest.TYPE,
FetchRequest.class);
TypedObjectFactory.registerClass(UnsubscribeNotification.TYPE,
UnsubscribeNotification.class);
TypedObjectFactory.registerClass(ForwardEventNotification.TYPE,
ForwardEventNotification.class);
TypedObjectFactory.registerClass(PingNotification.TYPE,
PingNotification.class);
TypedObjectFactory.registerClass(LogoffNotification.TYPE,
LogoffNotification.class);
}
}
@@ -0,0 +1,52 @@
//
// $Id: UsernamePasswordCreds.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
package com.samskivert.cocktail.cher.net;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class UsernamePasswordCreds
{
public static final short TYPE = TYPE_BASE + 0;
/**
* Zero argument constructor used when unserializing an instance.
*/
public UsernamePasswordCreds ()
{
super();
}
/**
* Construct credentials with the supplied username and password.
*/
public UsernamePasswordCreds (String username, String password)
{
_username = username;
_password = password;
}
public short getType ()
{
return TYPE;
}
public void writeTo (DataOutputStream out)
throws IOException
{
out.writeUTF(_username);
out.writeUTF(_password);
}
public void readFrom (DataInputStream in)
throws IOException
{
_username = in.readUTF();
_password = in.readUTF();
}
protected String _username;
protected String _password;
}