diff --git a/src/java/com/threerings/presents/client/Communicator.java b/src/java/com/threerings/presents/client/Communicator.java
index 911017622..d87f01fc1 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.1 2001/05/22 06:07:59 mdb Exp $
+// $Id: Communicator.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.client;
@@ -8,7 +8,9 @@ import java.net.Socket;
import java.net.InetAddress;
import com.samskivert.util.Queue;
+
import com.samskivert.cocktail.cher.Log;
+import com.samskivert.cocktail.cher.io.FramingOutputStream;
import com.samskivert.cocktail.cher.net.*;
/**
@@ -100,6 +102,38 @@ class Communicator
_client.communicatorDidExit();
}
+ /**
+ * Writes the supplied message to the socket.
+ */
+ protected void sendMessage (UpstreamMessage msg)
+ throws IOException
+ {
+ // first we flatten the message so that we can measure it's length
+ msg.writeTo(_dout);
+ // then write the framed message to actual output stream
+ _fout.writeFrameAndReset(_out);
+ }
+
+ /**
+ * Reads a new message from the socket (blocking until a message has
+ * arrived).
+ */
+ protected DownstreamMessage receiveMessage ()
+ throws IOException
+ {
+ // read the frame size which comes first
+ int count = _din.readInt();
+
+ // now read
+ return null;
+ }
+
+ /**
+ * The reader encapsulates the authentication and message reading
+ * process. It calls back to the Communicator class to do
+ * things, but the general flow of the reader thread is encapsulated
+ * in this class.
+ */
protected class Reader extends Thread
{
public void run ()
@@ -145,11 +179,24 @@ class Communicator
// establish a socket connection to said server
_socket = new Socket(host, _client.getPort());
+
+ // create our input and output streams
+ InputStream in = _socket.getInputStream();
+ _din = new DataInputStream(new BufferedInputStream(in));
+ _out = _socket.getOutputStream();
+
+ // we frame our messages here and then write them directly to
+ // the real output stream
+ _fout = new FramingOutputStream();
+ _dout = new DataOutputStream(_fout);
}
protected void logon ()
- throws LogonException
+ throws IOException, LogonException
{
+ // construct an auth request and send it
+ AuthRequest req = new AuthRequest(_client.getCredentials());
+ sendMessage(req);
}
protected void listen ()
@@ -157,6 +204,11 @@ class Communicator
}
}
+ /**
+ * The writer encapsulates the message writing process. It calls back
+ * to the Communicator class to do things, but the
+ * general flow of the writer thread is encapsulated in this class.
+ */
protected class Writer extends Thread
{
public void run ()
@@ -170,6 +222,10 @@ class Communicator
protected Socket _socket;
protected DataInputStream _din;
- protected DataInputStream _dout;
+ protected OutputStream _out;
protected Queue _msgq = new Queue();
+
+ /** We use this to frame our upstream messages. */
+ protected FramingOutputStream _fout;
+ protected DataOutputStream _dout;
}
diff --git a/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java
new file mode 100644
index 000000000..a53897501
--- /dev/null
+++ b/src/java/com/threerings/presents/dobj/DObject.java
@@ -0,0 +1,31 @@
+//
+// $Id: DObject.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
+
+package com.samskivert.cocktail.cher.dobj;
+
+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;
+
+public class DObject extends TypedObject
+{
+ public static short TYPE = 400;
+
+ public short getType ()
+ {
+ return TYPE;
+ }
+
+ public void writeTo (DataOutputStream out)
+ throws IOException
+ {
+ }
+
+ public void readFrom (DataInputStream in)
+ throws IOException
+ {
+ }
+}
diff --git a/src/java/com/threerings/presents/io/FramedInputStream.java b/src/java/com/threerings/presents/io/FramedInputStream.java
new file mode 100644
index 000000000..234619803
--- /dev/null
+++ b/src/java/com/threerings/presents/io/FramedInputStream.java
@@ -0,0 +1,250 @@
+//
+// $Id: FramedInputStream.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
+
+package com.samskivert.cocktail.cher.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.samskivert.util.StringUtil;
+
+/**
+ * The framed input stream reads input that was framed by a framing output
+ * stream. Framing in this case simply means writing the length of the
+ * frame followed by the data associated with the frame so that an entire
+ * frame can be loaded from the network layer before any higher layer
+ * attempts to process it. Additionally, any failure in decoding a frame
+ * won't result in the entire stream being skewed due to the remainder of
+ * the undecoded frame.
+ *
+ *
The framed input stream reads an entire frame worth of data into its
+ * internal buffer when readFrame() is called. It then
+ * behaves as if this is the only data available on the stream (meaning
+ * that when the data in the frame is exhausted, it will behave as if the
+ * end of the stream has been reached). A new frame can be read at any
+ * time and will be appended to the data available (the frame length data
+ * is never inserted into the stream data), but it is assumed that the
+ * caller will want to read and process an entire frame before going on to
+ * read the next frame (so that clear() can be called in the
+ * event of a frame decoding failure without clearing out the data from
+ * subsequent frames).
+ *
+ *
Note: The framing input stream does not synchronize reads + * from its internal buffer. It is intended to only be accessed from a + * single thread. + * + *
Implementation note: maybe this should derive from
+ * FilterInputStream and be tied to a single
+ * InputStream for its lifetime.
+ */
+public class FramedInputStream extends InputStream
+{
+ public FramedInputStream ()
+ {
+ _header = new byte[HEADER_SIZE];
+ _buffer = new byte[INITIAL_BUFFER_SIZE];
+ }
+
+ /**
+ * Reads a single frame from the provided input stream and appends
+ * that data to the existing data available via the framed input
+ * stream's read methods.
+ *
+ * @return the length of the read frame in bytes.
+ */
+ public synchronized int readFrame (InputStream source)
+ throws IOException
+ {
+ // first read in the frame length
+ if (source.read(_header, 0, HEADER_SIZE) < HEADER_SIZE) {
+ throw new EOFException();
+ }
+
+ // now decode the frame length
+ int flength = (_header[0] << 24) & 0xFF;
+ flength += (_header[1] << 16) & 0xFF;
+ flength += (_header[2] << 8) & 0xFF;
+ flength += _header[3] & 0xFF;
+
+ // expand our buffer to accomodate the frame data
+ int newcount = _count + flength;
+ if (newcount > _buffer.length) {
+ // increase the buffer size in large increments
+ byte[] newbuf = new byte[Math.max(_buffer.length << 1, newcount)];
+ System.arraycopy(_buffer, 0, newbuf, 0, _count);
+ _buffer = newbuf;
+ }
+
+ // read the data into the buffer
+ source.read(_buffer, _count, flength);
+ _count = newcount;
+
+ return flength;
+ }
+
+ /**
+ * Clears out any previously read frame data and reads a new frame
+ * into the buffer.
+ *
+ * @return the length of the read frame in bytes.
+ */
+ public int clearAndReadFrame (InputStream source)
+ throws IOException
+ {
+ clear();
+ return readFrame(source);
+ }
+
+ /**
+ * Clears out any frame data already in the buffer including anything
+ * that hasn't yet been read.
+ */
+ public void clear ()
+ {
+ _pos = 0;
+ _count = 0;
+ }
+
+ /**
+ * Reads the next byte of data from this input stream. The value byte
+ * is returned as an int in the range 0 to
+ * 255. If no byte is available because the end of the
+ * stream has been reached, the value -1 is returned.
+ *
+ *
This read method cannot block.
+ *
+ * @return the next byte of data, or -1 if the end of the
+ * stream has been reached.
+ */
+ public int read ()
+ {
+ return (_pos < _count) ? (_buffer[_pos++] & 0xff) : -1;
+ }
+
+ /**
+ * Reads up to len bytes of data into an array of bytes
+ * from this input stream. If pos equals
+ * count, then -1 is returned to indicate
+ * end of file. Otherwise, the number k of bytes read is
+ * equal to the smaller of len and
+ * count-pos. If k is positive, then bytes
+ * buf[pos] through buf[pos+k-1] are copied
+ * into b[off] through b[off+k-1] in the
+ * manner performed by System.arraycopy. The value
+ * k is added into pos and k is
+ * returned.
+ *
+ *
This read method cannot block.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset of the data.
+ * @param len the maximum number of bytes read.
+ *
+ * @return the total number of bytes read into the buffer, or
+ * -1 if there is no more data because the end of the
+ * stream has been reached.
+ */
+ public int read (byte[] b, int off, int len)
+ {
+ // sanity check the arguments
+ if (b == null) {
+ throw new NullPointerException();
+ } else if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ // figure out how much data we'll return
+ if (_pos >= _count) {
+ return -1;
+ }
+ if (_pos + len > _count) {
+ len = _count - _pos;
+ }
+ if (len <= 0) {
+ return 0;
+ }
+
+ // copy and advance
+ System.arraycopy(_buffer, _pos, b, off, len);
+ _pos += len;
+
+ return len;
+ }
+
+ /**
+ * Skips n bytes of input from this input stream. Fewer
+ * bytes might be skipped if the end of the input stream is reached.
+ * The actual number k of bytes to be skipped is equal to
+ * the smaller of n and count-pos. The value
+ * k is added into pos and k is
+ * returned.
+ *
+ * @param n the number of bytes to be skipped.
+ *
+ * @return the actual number of bytes skipped.
+ */
+ public long skip (long n)
+ {
+ if (_pos + n > _count) {
+ n = _count - _pos;
+ }
+ if (n <= 0) {
+ return 0;
+ }
+ _pos += n;
+ return n;
+ }
+
+ /**
+ * Returns the number of bytes that can be read from this input stream
+ * without blocking. The value returned is count - pos,
+ * which is the number of bytes remaining to be read from the input
+ * buffer.
+ *
+ * @return the number of bytes remaining to be read from the buffered
+ * frames.
+ */
+ public int available ()
+ {
+ return _count - _pos;
+ }
+
+ /**
+ * Always returns false as framed input streams do not support
+ * marking.
+ */
+ public boolean markSupported ()
+ {
+ return false;
+ }
+
+ /**
+ * Does nothing, as marking is not supported.
+ */
+ public void mark (int readAheadLimit)
+ {
+ // not supported; do nothing
+ }
+
+ /**
+ * Resets the buffer to the beginning of the buffered frames.
+ */
+ public void reset ()
+ {
+ _pos = 0;
+ }
+
+ protected byte[] _header;
+ protected byte[] _buffer;
+ protected int _pos;
+ protected int _count;
+
+ /** The size of the frame header (a 32-bit integer). */
+ protected static final int HEADER_SIZE = 4;
+
+ /** The default initial size of the internal buffer. */
+ protected static final int INITIAL_BUFFER_SIZE = 32;
+}
diff --git a/src/java/com/threerings/presents/io/FramingOutputStream.java b/src/java/com/threerings/presents/io/FramingOutputStream.java
new file mode 100644
index 000000000..9dcef376e
--- /dev/null
+++ b/src/java/com/threerings/presents/io/FramingOutputStream.java
@@ -0,0 +1,123 @@
+//
+// $Id: FramingOutputStream.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
+
+package com.samskivert.cocktail.cher.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * The framing output stream accumulates output into a byte array just
+ * like the byte array output stream, but can then be instructed to send
+ * its contents down another output stream, prefixed by the length
+ * (written as an integer) of those contents. It does this efficiently so
+ * that data is copied as little as possible and so that the output stream
+ * to which the data is written need not be buffered because the framed
+ * output is written in a single call to write().
+ *
+ *
Note: The framing output stream does not synchronize writes + * to its internal buffer. It is intended to only be accessed from a + * single thread. + * + *
Implementation note: maybe this should derive from
+ * FilterOutputStream and be tied to a single
+ * OutputStream for its lifetime.
+ */
+public class FramingOutputStream extends OutputStream
+{
+ public FramingOutputStream ()
+ {
+ _buffer = new byte[INITIAL_BUFFER_SIZE];
+ _count = 4; // leave room for the frame size at the beginning
+ }
+
+ /**
+ * Writes the specified byte to this framing output stream.
+ *
+ * @param b the byte to be written.
+ */
+ public void write (int b)
+ {
+ // expand our buffer if necessary
+ int newcount = _count + 1;
+ if (newcount > _buffer.length) {
+ // increase the buffer size in large increments
+ byte[] newbuf = new byte[Math.max(_buffer.length << 1, newcount)];
+ System.arraycopy(_buffer, 0, newbuf, 0, _count);
+ _buffer = newbuf;
+ }
+
+ // copy and advance
+ _buffer[_count] = (byte)b;
+ _count = newcount;
+ }
+
+ /**
+ * Writes len bytes from the specified byte array
+ * starting at offset off to this framing output stream.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ */
+ public void write (byte[] b, int off, int len)
+ {
+ // sanity check the arguments
+ if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+
+ // expand the buffer if necessary
+ int newcount = _count + len;
+ if (newcount > _buffer.length) {
+ // increase the buffer size in large increments
+ byte[] newbuf = new byte[Math.max(_buffer.length << 1, newcount)];
+ System.arraycopy(_buffer, 0, newbuf, 0, _count);
+ _buffer = newbuf;
+ }
+
+ // copy and advance
+ System.arraycopy(b, off, _buffer, _count, len);
+ _count = newcount;
+ }
+
+ /**
+ * Writes the contents of this framing output stream to the target
+ * output stream, prefixed by an integer with value equal to the
+ * number of bytes written folling that integer. It then resets the
+ * framing output stream to prepare for another framed message.
+ */
+ public synchronized void writeFrameAndReset (OutputStream target)
+ throws IOException
+ {
+ // prefix the frame with the byte count in network byte order (the
+ // format used by DataOutputStream)
+ int count = _count - 4;
+ _buffer[0] = (byte)((count >>> 24) & 0xFF);
+ _buffer[1] = (byte)((count >>> 16) & 0xFF);
+ _buffer[2] = (byte)((count >>> 8) & 0xFF);
+ _buffer[3] = (byte)((count >>> 0) & 0xFF);
+
+ // write the data
+ target.write(_buffer, 0, _count);
+
+ // reset our internal buffer
+ reset();
+ }
+
+ public void reset ()
+ {
+ // leave room for the frame size at the beginning
+ _count = 4;
+ }
+
+ protected byte[] _buffer;
+ protected int _count;
+
+ /** The default initial size of the internal buffer. */
+ protected static final int INITIAL_BUFFER_SIZE = 32;
+}
diff --git a/src/java/com/threerings/presents/io/ObjectStreamException.java b/src/java/com/threerings/presents/io/ObjectStreamException.java
index 4627b8bab..189a132fc 100644
--- a/src/java/com/threerings/presents/io/ObjectStreamException.java
+++ b/src/java/com/threerings/presents/io/ObjectStreamException.java
@@ -1,8 +1,10 @@
//
-// $Id: ObjectStreamException.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
+// $Id: ObjectStreamException.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.io;
+import java.io.IOException;
+
/**
* The object stream exception is used to communicate an error in
* processing a typed object stream. As any error is fatal, we don't
@@ -10,7 +12,7 @@ package com.samskivert.cocktail.cher.io;
* 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 class ObjectStreamException extends IOException
{
public ObjectStreamException (String message)
{
diff --git a/src/java/com/threerings/presents/io/TypedObjectFactory.java b/src/java/com/threerings/presents/io/TypedObjectFactory.java
index 97332a271..0c1e4d278 100644
--- a/src/java/com/threerings/presents/io/TypedObjectFactory.java
+++ b/src/java/com/threerings/presents/io/TypedObjectFactory.java
@@ -1,5 +1,5 @@
//
-// $Id: TypedObjectFactory.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
+// $Id: TypedObjectFactory.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.io;
@@ -37,6 +37,28 @@ public class TypedObjectFactory
return msg;
}
+ /**
+ * Registers the supplied class with the specified type code. If a
+ * class is already registered with that type code a runtime exception
+ * will be thrown so that the proper freaking out can occur.
+ */
+ public 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);
+ }
+
protected static TypedObject newObjectByType (short type)
throws ObjectStreamException
{
@@ -55,23 +77,6 @@ public class TypedObjectFactory
}
}
- 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();
}
diff --git a/src/java/com/threerings/presents/net/AuthRequest.java b/src/java/com/threerings/presents/net/AuthRequest.java
index 9fc21cf7a..5f201beb8 100644
--- a/src/java/com/threerings/presents/net/AuthRequest.java
+++ b/src/java/com/threerings/presents/net/AuthRequest.java
@@ -1,5 +1,5 @@
//
-// $Id: AuthRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
+// $Id: AuthRequest.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.net;
@@ -9,23 +9,23 @@ import java.io.DataOutputStream;
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
-public class AuthenticationRequest extends UpstreamMessage
+public class AuthRequest extends UpstreamMessage
{
- /** The code for an object subscription request. */
+ /** The code for an auth request. */
public static final short TYPE = TYPE_BASE + 0;
/**
* Zero argument constructor used when unserializing an instance.
*/
- public AuthenticationRequest ()
+ public AuthRequest ()
{
super();
}
/**
- * Constructs a authentication request with the supplied credentials.
+ * Constructs a auth request with the supplied credentials.
*/
- public AuthenticationRequest (Credentials creds)
+ public AuthRequest (Credentials creds)
{
_creds = creds;
}
@@ -50,8 +50,7 @@ public class AuthenticationRequest extends UpstreamMessage
}
/**
- * The object id of the distributed object to which we are
- * subscribing.
+ * The credentials associated with this auth request.
*/
protected Credentials _creds;
}
diff --git a/src/java/com/threerings/presents/net/AuthResponse.java b/src/java/com/threerings/presents/net/AuthResponse.java
new file mode 100644
index 000000000..174299a8f
--- /dev/null
+++ b/src/java/com/threerings/presents/net/AuthResponse.java
@@ -0,0 +1,72 @@
+//
+// $Id: AuthResponse.java,v 1.1 2001/05/22 21:51:29 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.DObject;
+import com.samskivert.cocktail.cher.io.TypedObjectFactory;
+
+/**
+ * The auth response communicates authentication success or failure as
+ * well as associated information via a distribted object transmitted
+ * along with the response. The distributed object simply serves as a
+ * container for the varied and manifold data involved in the
+ * authentication process.
+ */
+public class AuthResponse extends DownstreamMessage
+{
+ /** The code for an auth response. */
+ public static final short TYPE = TYPE_BASE + 0;
+
+ /** The response code key in the data object. */
+ public static final String CODE = "code";
+
+ /** The failure reason key in the data object. */
+ public static final String REASON = "reason";
+
+ /**
+ * Zero argument constructor used when unserializing an instance.
+ */
+ public AuthResponse ()
+ {
+ super();
+ }
+
+ /**
+ * Constructs a auth response with the supplied credentials.
+ */
+ public AuthResponse (DObject data)
+ {
+ _data = data;
+ }
+
+ public DObject getData ()
+ {
+ return _data;
+ }
+
+ public short getType ()
+ {
+ return TYPE;
+ }
+
+ public void writeTo (DataOutputStream out)
+ throws IOException
+ {
+ super.writeTo(out);
+ _data.writeTo(out);
+ }
+
+ public void readFrom (DataInputStream in)
+ throws IOException
+ {
+ super.readFrom(in);
+ _data = (DObject)TypedObjectFactory.readFrom(in);
+ }
+
+ protected DObject _data;
+}
diff --git a/src/java/com/threerings/presents/net/DownstreamMessage.java b/src/java/com/threerings/presents/net/DownstreamMessage.java
index 0cffaacb7..035e33a6d 100644
--- a/src/java/com/threerings/presents/net/DownstreamMessage.java
+++ b/src/java/com/threerings/presents/net/DownstreamMessage.java
@@ -1,5 +1,5 @@
//
-// $Id: DownstreamMessage.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
+// $Id: DownstreamMessage.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.net;
@@ -12,7 +12,7 @@ import com.samskivert.cocktail.cher.io.TypedObjectFactory;
* client. Downstream messages include object subscription, event
* forwarding and session management.
*/
-public class DownstreamMessage
+public abstract class DownstreamMessage extends TypedObject
{
/**
* All downstream message derived classes should base their typed
@@ -43,30 +43,15 @@ public class DownstreamMessage
// 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);
+ TypedObjectFactory.registerClass(AuthResponse.TYPE,
+ AuthResponse.class);
+ TypedObjectFactory.registerClass(EventNotification.TYPE,
+ EventNotification.class);
+ TypedObjectFactory.registerClass(ObjectResponse.TYPE,
+ ObjectResponse.class);
+ TypedObjectFactory.registerClass(FailureResponse.TYPE,
+ FailureResponse.class);
+ TypedObjectFactory.registerClass(PongNotification.TYPE,
+ PongNotification.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;
}
diff --git a/src/java/com/threerings/presents/net/EventNotification.java b/src/java/com/threerings/presents/net/EventNotification.java
index 52769a633..00507537b 100644
--- a/src/java/com/threerings/presents/net/EventNotification.java
+++ b/src/java/com/threerings/presents/net/EventNotification.java
@@ -1,8 +1,15 @@
//
-// $Id: EventNotification.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
+// $Id: EventNotification.java,v 1.2 2001/05/22 21:51:29 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 EventNotification extends DownstreamMessage
{
/** The code for an event notification. */
diff --git a/src/java/com/threerings/presents/net/FailureResponse.java b/src/java/com/threerings/presents/net/FailureResponse.java
new file mode 100644
index 000000000..94270ceba
--- /dev/null
+++ b/src/java/com/threerings/presents/net/FailureResponse.java
@@ -0,0 +1,50 @@
+//
+// $Id: FailureResponse.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
+
+package com.samskivert.cocktail.cher.net;
+
+import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+
+public class FailureResponse extends DownstreamMessage
+{
+ /** The code for a logoff notification. */
+ public static final short TYPE = TYPE_BASE + 3;
+
+ /**
+ * Zero argument constructor used when unserializing an instance.
+ */
+ public FailureResponse ()
+ {
+ super();
+ }
+
+ /**
+ * Constructs a failure response that is associated with the specified
+ * upstream message id.
+ */
+ public FailureResponse (short messageId)
+ {
+ this.messageId = messageId;
+ }
+
+ public short getType ()
+ {
+ return TYPE;
+ }
+
+ public void writeTo (DataOutputStream out)
+ throws IOException
+ {
+ super.writeTo(out);
+ out.writeShort(messageId);
+ }
+
+ public void readFrom (DataInputStream in)
+ throws IOException
+ {
+ super.readFrom(in);
+ messageId = in.readShort();
+ }
+}
diff --git a/src/java/com/threerings/presents/net/ObjectResponse.java b/src/java/com/threerings/presents/net/ObjectResponse.java
new file mode 100644
index 000000000..7080657ea
--- /dev/null
+++ b/src/java/com/threerings/presents/net/ObjectResponse.java
@@ -0,0 +1,59 @@
+//
+// $Id: ObjectResponse.java,v 1.1 2001/05/22 21:51:29 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.DObject;
+import com.samskivert.cocktail.cher.io.TypedObjectFactory;
+
+public class ObjectResponse extends DownstreamMessage
+{
+ /** The code for an event notification. */
+ public static final short TYPE = TYPE_BASE + 2;
+
+ /**
+ * Zero argument constructor used when unserializing an instance.
+ */
+ public ObjectResponse ()
+ {
+ super();
+ }
+
+ /**
+ * Constructs an object response with supplied distributed object that
+ * is associated with the specified upstream message id.
+ */
+ public ObjectResponse (short messageId, DObject dobj)
+ {
+ this.messageId = messageId;
+ _dobj = dobj;
+ }
+
+ public short getType ()
+ {
+ return TYPE;
+ }
+
+ public void writeTo (DataOutputStream out)
+ throws IOException
+ {
+ super.writeTo(out);
+ out.writeShort(messageId);
+ _dobj.writeTo(out);
+ }
+
+ public void readFrom (DataInputStream in)
+ throws IOException
+ {
+ super.readFrom(in);
+ messageId = in.readShort();
+ _dobj = (DObject)TypedObjectFactory.readFrom(in);
+ }
+
+ /** The object which is associated with this response. */
+ protected DObject _dobj;
+}
diff --git a/src/java/com/threerings/presents/net/PongResponse.java b/src/java/com/threerings/presents/net/PongResponse.java
new file mode 100644
index 000000000..8615d99ff
--- /dev/null
+++ b/src/java/com/threerings/presents/net/PongResponse.java
@@ -0,0 +1,23 @@
+//
+// $Id: PongResponse.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
+
+package com.samskivert.cocktail.cher.net;
+
+public class PongNotification extends DownstreamMessage
+{
+ /** The code for a pong notification. */
+ public static final short TYPE = TYPE_BASE + 4;
+
+ /**
+ * Zero argument constructor used when unserializing an instance.
+ */
+ public PongNotification ()
+ {
+ super();
+ }
+
+ public short getType ()
+ {
+ return TYPE;
+ }
+}
diff --git a/src/java/com/threerings/presents/net/UpstreamMessage.java b/src/java/com/threerings/presents/net/UpstreamMessage.java
index 6e80787bc..37424d53d 100644
--- a/src/java/com/threerings/presents/net/UpstreamMessage.java
+++ b/src/java/com/threerings/presents/net/UpstreamMessage.java
@@ -1,5 +1,5 @@
//
-// $Id: UpstreamMessage.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
+// $Id: UpstreamMessage.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.net;
@@ -87,8 +87,8 @@ public abstract class UpstreamMessage extends TypedObject
// register our upstream message classes
static {
- TypedObjectFactory.registerClass(AuthenticationRequest.TYPE,
- AuthenticationRequest.class);
+ TypedObjectFactory.registerClass(AuthRequest.TYPE,
+ AuthRequest.class);
TypedObjectFactory.registerClass(SubscribeRequest.TYPE,
SubscribeRequest.class);
TypedObjectFactory.registerClass(FetchRequest.TYPE,
diff --git a/src/java/com/threerings/presents/net/UsernamePasswordCreds.java b/src/java/com/threerings/presents/net/UsernamePasswordCreds.java
index bfeac031a..a7f93dc8b 100644
--- a/src/java/com/threerings/presents/net/UsernamePasswordCreds.java
+++ b/src/java/com/threerings/presents/net/UsernamePasswordCreds.java
@@ -1,5 +1,5 @@
//
-// $Id: UsernamePasswordCreds.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
+// $Id: UsernamePasswordCreds.java,v 1.2 2001/05/22 21:51:29 mdb Exp $
package com.samskivert.cocktail.cher.net;
@@ -7,7 +7,7 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-public class UsernamePasswordCreds
+public class UsernamePasswordCreds extends Credentials
{
public static final short TYPE = TYPE_BASE + 0;
diff --git a/tests/src/java/com/threerings/io/FrameTest.java b/tests/src/java/com/threerings/io/FrameTest.java
new file mode 100644
index 000000000..810668cc8
--- /dev/null
+++ b/tests/src/java/com/threerings/io/FrameTest.java
@@ -0,0 +1,75 @@
+//
+// $Id: FrameTest.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
+
+package com.samskivert.cocktail.cher.io.test;
+
+import java.io.*;
+import com.samskivert.cocktail.cher.io.*;
+
+public class FrameTest
+{
+ public static void writeFrames (OutputStream out)
+ throws IOException
+ {
+ FramingOutputStream fout = new FramingOutputStream();
+ DataOutputStream dout = new DataOutputStream(fout);
+
+ // create a few frames and write them to the output stream
+ dout.writeUTF("This is a test.");
+ dout.writeUTF("This is only a test.");
+ dout.writeUTF("If this were not a test, there would be " +
+ "meaningful data in this frame and someone " +
+ "would probably be enjoying themselves.");
+ fout.writeFrameAndReset(out);
+
+ dout.writeUTF("Now is the time for all good men to come to the " +
+ "aid of their country.");
+ dout.writeUTF("Every good boy deserves fudge.");
+ dout.writeUTF("The quick brown fox jumped over the lazy cow.");
+ fout.writeFrameAndReset(out);
+
+ dout.writeUTF("Third time is the charm.");
+ fout.writeFrameAndReset(out);
+ }
+
+ public static void readFrames (InputStream in)
+ throws IOException
+ {
+ FramedInputStream fin = new FramedInputStream();
+ DataInputStream din = new DataInputStream(fin);
+
+ // read the first frame
+ fin.readFrame(in);
+ System.out.println(din.readUTF());
+ System.out.println(din.readUTF());
+ System.out.println(din.readUTF());
+ System.out.println("This should be -1: " + fin.read());
+
+ // read the second frame
+ fin.readFrame(in);
+ System.out.println(din.readUTF());
+ System.out.println(din.readUTF());
+ System.out.println(din.readUTF());
+ System.out.println("This should be -1: " + fin.read());
+
+ // read the third frame
+ fin.readFrame(in);
+ System.out.println(din.readUTF());
+ System.out.println("This should be -1: " + fin.read());
+ }
+
+ public static void main (String[] args)
+ {
+ try {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ writeFrames(bout);
+
+ byte[] data = bout.toByteArray();
+ ByteArrayInputStream bin = new ByteArrayInputStream(data);
+ readFrames(bin);
+
+ } catch (IOException ioe) {
+ ioe.printStackTrace(System.err);
+ }
+ }
+}