From f8f3645f673069a3fc00fdece7d48c9a5be5829a Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 22 May 2001 21:51:29 +0000 Subject: [PATCH] Further progress. Heeya! git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../presents/client/Communicator.java | 62 ++++- .../com/threerings/presents/dobj/DObject.java | 31 +++ .../presents/io/FramedInputStream.java | 250 ++++++++++++++++++ .../presents/io/FramingOutputStream.java | 123 +++++++++ .../presents/io/ObjectStreamException.java | 6 +- .../presents/io/TypedObjectFactory.java | 41 +-- .../threerings/presents/net/AuthRequest.java | 15 +- .../threerings/presents/net/AuthResponse.java | 72 +++++ .../presents/net/DownstreamMessage.java | 39 +-- .../presents/net/EventNotification.java | 9 +- .../presents/net/FailureResponse.java | 50 ++++ .../presents/net/ObjectResponse.java | 59 +++++ .../threerings/presents/net/PongResponse.java | 23 ++ .../presents/net/UpstreamMessage.java | 6 +- .../presents/net/UsernamePasswordCreds.java | 4 +- .../src/java/com/threerings/io/FrameTest.java | 75 ++++++ 16 files changed, 801 insertions(+), 64 deletions(-) create mode 100644 src/java/com/threerings/presents/dobj/DObject.java create mode 100644 src/java/com/threerings/presents/io/FramedInputStream.java create mode 100644 src/java/com/threerings/presents/io/FramingOutputStream.java create mode 100644 src/java/com/threerings/presents/net/AuthResponse.java create mode 100644 src/java/com/threerings/presents/net/FailureResponse.java create mode 100644 src/java/com/threerings/presents/net/ObjectResponse.java create mode 100644 src/java/com/threerings/presents/net/PongResponse.java create mode 100644 tests/src/java/com/threerings/io/FrameTest.java 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); + } + } +}