Har! More progress mateys.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -36,3 +36,19 @@ the outgoing socket
|
||||
Informs exo-client about connection state changes; provides interface to
|
||||
connection + authentication (logon) and disconnection (logoff); provides
|
||||
access to omgr and client dobj
|
||||
|
||||
* Server components
|
||||
|
||||
** Connection Manager
|
||||
Listens on accepting socket; creates and manages connection objects;
|
||||
informs connection observer of state changes; handles all network traffic
|
||||
on own thread;
|
||||
|
||||
** Auth Manager
|
||||
Processes auth requests on own thread; uses pluggable Authenticator to
|
||||
perform actual authentication;
|
||||
|
||||
** Client Manager
|
||||
Registers with connection manager; manages authentication; maps
|
||||
connections to existing client objects or creates new client objects for
|
||||
newly connecting clients;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
Cher Mk3 Notes -*- outline -*-
|
||||
|
||||
* Check into "connection closed by peer" thread exiting on client
|
||||
|
||||
* TypedObjectFactory
|
||||
Maybe modify so that types are assigned automatically even if everything
|
||||
has to be registered in a single place, since it pretty much does anyway.
|
||||
|
||||
* Server components:
|
||||
+ Connection manager
|
||||
+ Client manager
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Log.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||
// $Id: Log.java,v 1.2 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher;
|
||||
|
||||
@@ -10,7 +10,7 @@ package com.samskivert.cocktail.cher;
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("robodj");
|
||||
new com.samskivert.util.Log("cher");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Client.java,v 1.2 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: Client.java,v 1.3 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.client;
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.List;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.net.Credentials;
|
||||
import com.samskivert.cocktail.cher.net.Registry;
|
||||
|
||||
/**
|
||||
* Through the client object, a connection to the system is established
|
||||
@@ -194,4 +195,9 @@ public class Client
|
||||
static final int CLIENT_CONNECTION_FAILED = 2;
|
||||
static final int CLIENT_WILL_LOGOFF = 3;
|
||||
static final int CLIENT_DID_LOGOFF = 4;
|
||||
|
||||
// register our shared objects
|
||||
static {
|
||||
Registry.registerTypedObjects();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Communicator.java,v 1.3 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: Communicator.java,v 1.4 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.client;
|
||||
|
||||
@@ -139,6 +139,8 @@ class Communicator
|
||||
*/
|
||||
protected synchronized void logonSucceeded (AuthResponseData data)
|
||||
{
|
||||
Log.info("Logon succeeded: " + data);
|
||||
|
||||
// extract bootstrap information
|
||||
|
||||
// create a new writer thread and start it up
|
||||
@@ -147,6 +149,9 @@ class Communicator
|
||||
}
|
||||
_writer = new Writer();
|
||||
_writer.start();
|
||||
|
||||
// let the client know that logon succeeded
|
||||
_client.notifyObservers(Client.CLIENT_DID_LOGON, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,6 +166,8 @@ class Communicator
|
||||
return;
|
||||
}
|
||||
|
||||
Log.info("Connection failed: " + ioe);
|
||||
|
||||
// let the client know that things went south
|
||||
_client.notifyObservers(Client.CLIENT_CONNECTION_FAILED, ioe);
|
||||
|
||||
@@ -212,10 +219,15 @@ class Communicator
|
||||
protected DownstreamMessage receiveMessage ()
|
||||
throws IOException
|
||||
{
|
||||
// read in the next message frame
|
||||
_fin.clearAndReadFrame(_in);
|
||||
// then use the typed object factory to read and decode the proper
|
||||
// downstream message instance
|
||||
// read in the next message frame (readFrame() can return false
|
||||
// meaning it only read part of the frame from the network, in
|
||||
// which case we simply call it again because we can't do anything
|
||||
// until it has a whole frame; it will throw an exception if it
|
||||
// hits EOF or if something goes awry)
|
||||
while (!_fin.readFrame(_in));
|
||||
|
||||
// then use the typed object factory to read and decode the
|
||||
// proper downstream message instance
|
||||
return (DownstreamMessage)TypedObjectFactory.readFrom(_din);
|
||||
}
|
||||
|
||||
@@ -225,6 +237,7 @@ class Communicator
|
||||
*/
|
||||
protected void processMessage (DownstreamMessage msg)
|
||||
{
|
||||
Log.info("Process msg: " + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +260,8 @@ class Communicator
|
||||
logon();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.info("Logon failed: " + e);
|
||||
Log.logStackTrace(e);
|
||||
// let the observers know that we've failed
|
||||
_client.notifyObservers(Client.CLIENT_FAILED_TO_LOGON, e);
|
||||
// and terminate our communicator thread
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: DObject.java,v 1.2 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: DObject.java,v 1.3 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.dobj;
|
||||
|
||||
@@ -7,25 +7,17 @@ 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 implements TypedObject
|
||||
public class DObject
|
||||
{
|
||||
public static short TYPE = 400;
|
||||
|
||||
public short getType ()
|
||||
{
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public void writeTo (DataOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
// nothing doing!
|
||||
}
|
||||
|
||||
public void readFrom (DataInputStream in)
|
||||
throws IOException
|
||||
{
|
||||
// nothing doing!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id: DObjectFactory.java,v 1.1 2001/05/29 03:27:59 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.ObjectStreamException;
|
||||
|
||||
/**
|
||||
* The distributed object factory is responsible for marshalling and
|
||||
* unmarshalling distributed objects to and from streams so that they can
|
||||
* be communicated between the client and server.
|
||||
*/
|
||||
public class DObjectFactory
|
||||
{
|
||||
public static void writeTo (DataOutputStream out, DObject dobj)
|
||||
throws IOException
|
||||
{
|
||||
// first we write the class of the object to the stream
|
||||
out.writeUTF(dobj.getClass().getName());
|
||||
// then we write the object itself
|
||||
dobj.writeTo(out);
|
||||
}
|
||||
|
||||
public static DObject readFrom (DataInputStream in)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
Class clazz = Class.forName(in.readUTF());
|
||||
DObject dobj = (DObject)clazz.newInstance();
|
||||
dobj.readFrom(in);
|
||||
return dobj;
|
||||
|
||||
} catch (Exception e) {
|
||||
String errmsg = "Unable to unserialize dobj: " + e;
|
||||
throw new ObjectStreamException(errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: FramedInputStream.java,v 1.2 2001/05/22 22:01:08 mdb Exp $
|
||||
// $Id: FramedInputStream.java,v 1.3 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.io;
|
||||
|
||||
@@ -17,19 +17,15 @@ import com.samskivert.util.StringUtil;
|
||||
* 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 undecoded frame remaining in the input stream.
|
||||
*
|
||||
* <p>The framed input stream reads an entire frame worth of data into its
|
||||
* internal buffer when <code>readFrame()</code> 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 <code>clear()</code> can be called in the
|
||||
* event of a frame decoding failure without clearing out the data from
|
||||
* subsequent frames).
|
||||
* end of the stream has been reached). The buffer can only contain a
|
||||
* single frame at a time, so any data left over from a previous frame
|
||||
* will disappear when <code>readFrame()</code> is called again.
|
||||
*
|
||||
* <p><em>Note:</em> The framing input stream does not synchronize reads
|
||||
* from its internal buffer. It is intended to only be accessed from a
|
||||
@@ -48,65 +44,68 @@ public class FramedInputStream extends InputStream
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Reads a frame from the provided input stream, or appends to a
|
||||
* partially read frame. Appends the read data to the existing data
|
||||
* available via the framed input stream's read methods. If the entire
|
||||
* frame data is not yet available, <code>readFrame</code> will return
|
||||
* false, otherwise true.
|
||||
*
|
||||
* @return the length of the read frame in bytes.
|
||||
* <p> The code assumes that it will be able to read the entire frame
|
||||
* header in a single read. The header is only four bytes and should
|
||||
* always arrive at the beginning of a packet, so unless something is
|
||||
* very funky with the networking layer, this should be a safe
|
||||
* assumption.
|
||||
*
|
||||
* @return true if the entire frame has been read, false if the buffer
|
||||
* contains only a partial frame.
|
||||
*/
|
||||
public synchronized int readFrame (InputStream source)
|
||||
public boolean readFrame (InputStream source)
|
||||
throws IOException
|
||||
{
|
||||
// first read in the frame length
|
||||
if (source.read(_header, 0, HEADER_SIZE) < HEADER_SIZE) {
|
||||
// if the buffer currently contains a complete frame, that means
|
||||
// we're not halfway through reading a frame and that we can start
|
||||
// anew.
|
||||
if (_count == _length) {
|
||||
// clear out any prior data
|
||||
_pos = 0;
|
||||
_count = 0;
|
||||
|
||||
// read in the frame length
|
||||
int got = source.read(_header, 0, HEADER_SIZE);
|
||||
if (got < 0) {
|
||||
throw new EOFException();
|
||||
|
||||
} else if (got == 0) {
|
||||
return false;
|
||||
|
||||
} else if (got < HEADER_SIZE) {
|
||||
String errmsg = "FramedInputStream does not support " +
|
||||
"partially reading the header. Needed " + HEADER_SIZE +
|
||||
" bytes, got " + got + " bytes.";
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
|
||||
// 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;
|
||||
// decode the frame length
|
||||
_length = (_header[0] << 24) & 0xFF;
|
||||
_length += (_header[1] << 16) & 0xFF;
|
||||
_length += (_header[2] << 8) & 0xFF;
|
||||
_length += _header[3] & 0xFF;
|
||||
|
||||
// expand our buffer to accomodate the frame data
|
||||
int newcount = _count + flength;
|
||||
if (newcount > _buffer.length) {
|
||||
// if necessary, expand our buffer to accomodate the frame
|
||||
if (_length > _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;
|
||||
_buffer = new byte[Math.max(_buffer.length << 1, _length)];
|
||||
}
|
||||
}
|
||||
|
||||
// read the data into the buffer
|
||||
if (source.read(_buffer, _count, flength) < flength) {
|
||||
int got = source.read(_buffer, _count, _length);
|
||||
if (got < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
_count = newcount;
|
||||
_count += got;
|
||||
|
||||
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;
|
||||
return (_count == _length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,6 +239,8 @@ public class FramedInputStream extends InputStream
|
||||
}
|
||||
|
||||
protected byte[] _header;
|
||||
protected int _length;
|
||||
|
||||
protected byte[] _buffer;
|
||||
protected int _pos;
|
||||
protected int _count;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: FramingOutputStream.java,v 1.1 2001/05/22 21:51:29 mdb Exp $
|
||||
// $Id: FramingOutputStream.java,v 1.2 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.io;
|
||||
|
||||
@@ -91,7 +91,7 @@ public class FramingOutputStream extends OutputStream
|
||||
* 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)
|
||||
public void writeFrameAndReset (OutputStream target)
|
||||
throws IOException
|
||||
{
|
||||
// prefix the frame with the byte count in network byte order (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: AuthResponse.java,v 1.2 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: AuthResponse.java,v 1.3 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.net;
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.io.IOException;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
||||
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||
import com.samskivert.cocktail.cher.dobj.DObjectFactory;
|
||||
|
||||
/**
|
||||
* The auth response communicates authentication success or failure as
|
||||
@@ -51,14 +51,14 @@ public class AuthResponse extends DownstreamMessage
|
||||
throws IOException
|
||||
{
|
||||
super.writeTo(out);
|
||||
TypedObjectFactory.writeTo(out, _data);
|
||||
DObjectFactory.writeTo(out, _data);
|
||||
}
|
||||
|
||||
public void readFrom (DataInputStream in)
|
||||
throws IOException
|
||||
{
|
||||
super.readFrom(in);
|
||||
_data = (AuthResponseData)TypedObjectFactory.readFrom(in);
|
||||
_data = (AuthResponseData)DObjectFactory.readFrom(in);
|
||||
}
|
||||
|
||||
protected AuthResponseData _data;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
//
|
||||
// $Id: AuthResponseData.java,v 1.1 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: AuthResponseData.java,v 1.2 2001/05/29 03:27: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.DObject;
|
||||
|
||||
/**
|
||||
@@ -18,4 +22,18 @@ public class AuthResponseData extends DObject
|
||||
* failed.
|
||||
*/
|
||||
public String code;
|
||||
|
||||
public void writeTo (DataOutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
super.writeTo(out);
|
||||
out.writeUTF(code);
|
||||
}
|
||||
|
||||
public void readFrom (DataInputStream in)
|
||||
throws IOException
|
||||
{
|
||||
super.readFrom(in);
|
||||
code = in.readUTF();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Credentials.java,v 1.2 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: Credentials.java,v 1.3 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.net;
|
||||
|
||||
@@ -50,10 +50,4 @@ public abstract class Credentials implements TypedObject
|
||||
{
|
||||
// we don't do anything here, but we may want to some day
|
||||
}
|
||||
|
||||
// register our credential classes
|
||||
static {
|
||||
TypedObjectFactory.registerClass(UsernamePasswordCreds.TYPE,
|
||||
UsernamePasswordCreds.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: DownstreamMessage.java,v 1.3 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: DownstreamMessage.java,v 1.4 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.net;
|
||||
|
||||
@@ -66,18 +66,4 @@ public abstract class DownstreamMessage implements TypedObject
|
||||
{
|
||||
// we don't do anything here, but we may want to some day
|
||||
}
|
||||
|
||||
// register our downstream message classes
|
||||
static {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Registry.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.net;
|
||||
|
||||
import com.samskivert.cocktail.cher.dobj.DObject;
|
||||
import com.samskivert.cocktail.cher.io.TypedObject;
|
||||
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||
|
||||
/**
|
||||
* The registry provides a single place where all typed objects that are
|
||||
* exchanged between the client and the server can be registered with the
|
||||
* typed object factory.
|
||||
*/
|
||||
public class Registry
|
||||
{
|
||||
/**
|
||||
* Must be called once by the client and the server to ensure that all
|
||||
* typed objects are registered with the typed object system.
|
||||
*/
|
||||
public static void registerTypedObjects ()
|
||||
{
|
||||
// register our upstream message classes
|
||||
TypedObjectFactory.registerClass(AuthRequest.TYPE,
|
||||
AuthRequest.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);
|
||||
|
||||
// register our downstream message classes
|
||||
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);
|
||||
|
||||
// register our credential classes
|
||||
TypedObjectFactory.registerClass(UsernamePasswordCreds.TYPE,
|
||||
UsernamePasswordCreds.class);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: UpstreamMessage.java,v 1.3 2001/05/23 04:03:40 mdb Exp $
|
||||
// $Id: UpstreamMessage.java,v 1.4 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.net;
|
||||
|
||||
@@ -82,22 +82,4 @@ public abstract class UpstreamMessage implements TypedObject
|
||||
* the client as new messages are generated.
|
||||
*/
|
||||
protected static short _nextMessageId;
|
||||
|
||||
// register our upstream message classes
|
||||
static {
|
||||
TypedObjectFactory.registerClass(AuthRequest.TYPE,
|
||||
AuthRequest.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,26 @@
|
||||
//
|
||||
// $Id: Authenticator.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import com.samskivert.cocktail.cher.net.AuthRequest;
|
||||
import com.samskivert.cocktail.cher.net.AuthResponse;
|
||||
|
||||
/**
|
||||
* The authenticator is a pluggable component of the authentication
|
||||
* framework. It is provided with an auth request object and is expected
|
||||
* to return an auth response object indicating whether or not the client
|
||||
* is authenticated. <code>authenticate()</code> is invoked on the authmgr
|
||||
* thread which means that it can access databases or other external
|
||||
* (slow) information sources without concern for unduly blocking the
|
||||
* normal operation of the server. Of course, authentication requests are
|
||||
* processed serially, so they shouldn't take <em>too</em> long.
|
||||
*/
|
||||
public interface Authenticator
|
||||
{
|
||||
/**
|
||||
* Requests that the authenticator process the supplied request and
|
||||
* provide a response object indicating success or failure.
|
||||
*/
|
||||
public AuthResponse process (AuthRequest request);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// $Id: DummyAuthenticator.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.net.*;
|
||||
import com.samskivert.cocktail.cher.server.net.Authenticator;
|
||||
import com.samskivert.cocktail.cher.util.Codes;
|
||||
|
||||
public class DummyAuthenticator implements Authenticator
|
||||
{
|
||||
/**
|
||||
* We just accept all authentication requests.
|
||||
*/
|
||||
public AuthResponse process (AuthRequest req)
|
||||
{
|
||||
Log.info("Accepting request: " + req);
|
||||
AuthResponseData rdata = new AuthResponseData();
|
||||
rdata.code = Codes.SUCCESS;
|
||||
return new AuthResponse(rdata);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// $Id: PresentsServer.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.server.net.AuthManager;
|
||||
import com.samskivert.cocktail.cher.server.net.ConnectionManager;
|
||||
|
||||
/**
|
||||
* The cher server provides a central point of access to the various
|
||||
* facilities that make up the cher layer of the system.
|
||||
*/
|
||||
public class CherServer
|
||||
{
|
||||
/** The authentication manager. */
|
||||
public static AuthManager authmgr;
|
||||
|
||||
/** The manager of network connections. */
|
||||
public static ConnectionManager cmgr;
|
||||
|
||||
/**
|
||||
* Initializes all of the server services and prepares for operation.
|
||||
*/
|
||||
public static void init ()
|
||||
{
|
||||
try {
|
||||
// create our authentication manager
|
||||
authmgr = new AuthManager(new DummyAuthenticator());
|
||||
// create our connection manager
|
||||
cmgr = new ConnectionManager(authmgr);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Unable to initialize server.");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up all of the server services and enters the main server
|
||||
* event loop.
|
||||
*/
|
||||
public static void start ()
|
||||
{
|
||||
// start up the auth manager
|
||||
authmgr.start();
|
||||
// start up the connection manager
|
||||
cmgr.start();
|
||||
|
||||
// for now, just block because we've nothing to do
|
||||
try {
|
||||
synchronized (CherServer.class) {
|
||||
CherServer.class.wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
init();
|
||||
start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// $Id: AuthManager.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import com.samskivert.util.LoopingThread;
|
||||
import com.samskivert.util.Queue;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.net.AuthRequest;
|
||||
import com.samskivert.cocktail.cher.net.AuthResponse;
|
||||
|
||||
/**
|
||||
* The authentication manager takes care of the authentication process.
|
||||
* Authentication happens on multiple threads. The conmgr thread parses
|
||||
* the authentication request and passes it on to the authmgr thread. The
|
||||
* authmgr thread invokes the (pluggable) authenticator to do the actual
|
||||
* authentication. Then the response message is queued up to be delivered
|
||||
* by the conmgr thread.
|
||||
*
|
||||
* <p> This structure prevents authentication to take place
|
||||
* asynchronously, but in a controlled manner. Only one authentication
|
||||
* will be processed at a time, but the dobj and conmgr threads will
|
||||
* continue to operate independent of the authentication process.
|
||||
*/
|
||||
public class AuthManager extends LoopingThread
|
||||
{
|
||||
public AuthManager (Authenticator author)
|
||||
{
|
||||
_author = author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts an authenticating connection on the queue. The connection will
|
||||
* be authenticated and an auth response delivered.
|
||||
*/
|
||||
public void postAuthingConnection (AuthingConnection aconn)
|
||||
{
|
||||
_authq.append(aconn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process auth requests.
|
||||
*/
|
||||
protected void iterate ()
|
||||
{
|
||||
// grab the next authing connection from the queue
|
||||
AuthingConnection aconn = (AuthingConnection)_authq.get();
|
||||
|
||||
try {
|
||||
// instruct the authenticator to process the auth request
|
||||
AuthResponse rsp = _author.process(aconn.getAuthRequest());
|
||||
|
||||
// now ship the response back
|
||||
aconn.postMessage(rsp);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failure processing authreq [conn=" + aconn + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Authenticator _author;
|
||||
protected Queue _authq = new Queue();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// $Id: AuthingConnection.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import ninja2.core.io_core.nbio.NonblockingSocket;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.net.UpstreamMessage;
|
||||
import com.samskivert.cocktail.cher.net.AuthRequest;
|
||||
|
||||
/**
|
||||
* The authing connection manages the client connection until
|
||||
* authentication has completed (for better or for worse).
|
||||
*/
|
||||
public class AuthingConnection extends Connection
|
||||
{
|
||||
/**
|
||||
* Creates a new authing connection object that will manage the
|
||||
* authentication process for the suppled client socket.
|
||||
*/
|
||||
public AuthingConnection (ConnectionManager cmgr,
|
||||
NonblockingSocket socket)
|
||||
throws IOException
|
||||
{
|
||||
super(cmgr, socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new message has arrived from the client.
|
||||
*/
|
||||
public void handleMessage (UpstreamMessage msg)
|
||||
{
|
||||
try {
|
||||
// keep a handle on our auth request
|
||||
_authreq = (AuthRequest)msg;
|
||||
|
||||
// post ourselves for processing by the authmgr
|
||||
_cmgr.getAuthManager().postAuthingConnection(this);
|
||||
|
||||
} catch (ClassCastException cce) {
|
||||
Log.warning("Received non-authreq message during " +
|
||||
"authentication process [conn=" + this +
|
||||
", msg=" + msg + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the auth request currently being processed
|
||||
* by this authing connection.
|
||||
*/
|
||||
public AuthRequest getAuthRequest ()
|
||||
{
|
||||
return _authreq;
|
||||
}
|
||||
|
||||
protected int _state = AWAITING_AUTH_REQUEST;
|
||||
protected AuthRequest _authreq;
|
||||
|
||||
protected static final int AWAITING_AUTH_REQUEST = 0;
|
||||
protected static final int PROCESSING_AUTH_REQUEST = 1;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// $Id: Connection.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import java.io.*;
|
||||
import ninja2.core.io_core.nbio.*;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.io.FramedInputStream;
|
||||
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||
import com.samskivert.cocktail.cher.net.UpstreamMessage;
|
||||
import com.samskivert.cocktail.cher.net.DownstreamMessage;
|
||||
|
||||
/**
|
||||
* The base connection class implements the net event handler interface
|
||||
* and processes raw incoming network data into a stream of parsed
|
||||
* <code>UpstreamMessage</code> objects. It also provides the means to
|
||||
* send messages to the client and facilities for checking delinquency.
|
||||
*/
|
||||
public abstract class Connection implements NetEventHandler
|
||||
{
|
||||
/**
|
||||
* Constructs a connection object that is associated with the supplied
|
||||
* socket.
|
||||
*
|
||||
* @param cmgr The connection manager with which this connection is
|
||||
* associated.
|
||||
* @param socket The socket from which we'll be reading messages.
|
||||
*/
|
||||
public Connection (ConnectionManager cmgr, NonblockingSocket socket)
|
||||
throws IOException
|
||||
{
|
||||
_cmgr = cmgr;
|
||||
_socket = socket;
|
||||
|
||||
// create a select item that will allow us to be incorporated into
|
||||
// the main event polling loop
|
||||
_selitem = new SelectItem(_socket, this, Selectable.READ_READY);
|
||||
|
||||
// create our input streams
|
||||
_in = _socket.getInputStream();
|
||||
_out = _socket.getOutputStream();
|
||||
_fin = new FramedInputStream();
|
||||
_din = new DataInputStream(_fin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the select item associated with this connection.
|
||||
*/
|
||||
public SelectItem getSelectItem ()
|
||||
{
|
||||
return _selitem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output stream associated with this connection. This
|
||||
* should only be used by the connection manager.
|
||||
*/
|
||||
public OutputStream getOutputStream ()
|
||||
{
|
||||
return _out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when our client socket has data available for reading.
|
||||
*/
|
||||
public void handleEvent (Selectable source, short events)
|
||||
{
|
||||
try {
|
||||
// read the available data and see if we have a whole frame
|
||||
if (_fin.readFrame(_in)) {
|
||||
// parse the message and pass it on
|
||||
handleMessage((UpstreamMessage)
|
||||
TypedObjectFactory.readFrom(_din));
|
||||
}
|
||||
|
||||
} catch (EOFException eofe) {
|
||||
// let the connection manager know that we done went away
|
||||
_cmgr.connectionClosed(this);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Error reading message from socket " +
|
||||
"[socket=" + _socket + ", error=" + ioe + "].");
|
||||
// let the connection manager know that something when awry
|
||||
_cmgr.connectionFailed(this, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a complete message has been parsed from incoming
|
||||
* network data.
|
||||
*/
|
||||
public abstract void handleMessage (UpstreamMessage msg);
|
||||
|
||||
/**
|
||||
* Posts a downstream message for delivery to this connection. The
|
||||
* message will be delivered by the conmgr thread as soon as it gets
|
||||
* to it.
|
||||
*/
|
||||
public void postMessage (DownstreamMessage msg)
|
||||
{
|
||||
// pass this along to the connection manager
|
||||
_cmgr.postMessage(this, msg);
|
||||
}
|
||||
|
||||
protected ConnectionManager _cmgr;
|
||||
protected NonblockingSocket _socket;
|
||||
protected SelectItem _selitem;
|
||||
|
||||
protected InputStream _in;
|
||||
protected OutputStream _out;
|
||||
protected FramedInputStream _fin;
|
||||
protected DataInputStream _din;
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
//
|
||||
// $Id: ConnectionManager.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import ninja2.core.io_core.nbio.*;
|
||||
|
||||
import com.samskivert.util.LoopingThread;
|
||||
import com.samskivert.util.Tuple;
|
||||
import com.samskivert.util.Queue;
|
||||
|
||||
import com.samskivert.cocktail.cher.Log;
|
||||
import com.samskivert.cocktail.cher.io.FramingOutputStream;
|
||||
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||
import com.samskivert.cocktail.cher.net.DownstreamMessage;
|
||||
import com.samskivert.cocktail.cher.net.Registry;
|
||||
|
||||
/**
|
||||
* The connection manager manages the socket on which connections are
|
||||
* received. It creates connection objects to manage each individual
|
||||
* connection, but those connection objects interact closely with the
|
||||
* connection manager because network I/O is done via a poll()-like
|
||||
* mechanism rather than via threads.
|
||||
*/
|
||||
public class ConnectionManager extends LoopingThread
|
||||
{
|
||||
/**
|
||||
* Constructs and initialized a connection manager (binding the socket
|
||||
* on which it will listen for client connections).
|
||||
*
|
||||
* @param authmgr The authentication manager to use when
|
||||
* authenticating client connections.
|
||||
*/
|
||||
public ConnectionManager (AuthManager authmgr)
|
||||
throws IOException
|
||||
{
|
||||
int port = DEFAULT_CM_PORT;
|
||||
|
||||
// keep a handle on our authentication manager
|
||||
_authmgr = authmgr;
|
||||
|
||||
// we use this to wait for activity on our sockets
|
||||
_selset = new SelectSet();
|
||||
|
||||
// create our listening socket and add it to the select set
|
||||
_listener = new NonblockingServerSocket(port);
|
||||
_litem = new SelectItem(_listener, Selectable.ACCEPT_READY);
|
||||
// when an ACCEPT_READY event happens, we do this:
|
||||
_litem.obj = new NetEventHandler() {
|
||||
public void handleEvent (Selectable item, short events) {
|
||||
acceptConnection();
|
||||
}
|
||||
};
|
||||
_selset.add(_litem);
|
||||
|
||||
// we'll use these for sending messages to clients
|
||||
_framer = new FramingOutputStream();
|
||||
_dout = new DataOutputStream(_framer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the auth manager being used to authenticate
|
||||
* connections.
|
||||
*/
|
||||
public AuthManager getAuthManager ()
|
||||
{
|
||||
return _authmgr;
|
||||
}
|
||||
|
||||
protected void willStart ()
|
||||
{
|
||||
Log.info("Connection Manager thread running.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the select loop. This is the body of the conmgr thread.
|
||||
*/
|
||||
protected void iterate ()
|
||||
{
|
||||
// send any messages that are waiting on the outgoing queue
|
||||
Tuple tup;
|
||||
while ((tup = (Tuple)_outq.getNonBlocking()) != null) {
|
||||
Connection conn = (Connection)tup.left;
|
||||
DownstreamMessage outmsg = (DownstreamMessage)tup.right;
|
||||
try {
|
||||
// first flatten the message (and frame it)
|
||||
TypedObjectFactory.writeTo(_dout, outmsg);
|
||||
// then write framed message to real output stream
|
||||
_framer.writeFrameAndReset(conn.getOutputStream());
|
||||
|
||||
} catch (IOException ioe) {
|
||||
connectionFailed(conn, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
// check for incoming network events
|
||||
int ecount = _selset.select(SELECT_LOOP_TIME);
|
||||
if (ecount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// process those events
|
||||
SelectItem[] active = _selset.getEvents();
|
||||
for (int i = 0; i < active.length; i++) {
|
||||
try {
|
||||
SelectItem item = active[i];
|
||||
NetEventHandler handler = (NetEventHandler)item.obj;
|
||||
handler.handleEvent(item.getSelectable(), item.revents);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error processing network data.");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void didShutdown ()
|
||||
{
|
||||
Log.info("Connection Manager thread exited.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by our net event handler when a new connection is ready to
|
||||
* be accepted on our listening socket.
|
||||
*/
|
||||
protected void acceptConnection ()
|
||||
{
|
||||
NonblockingSocket socket = null;
|
||||
|
||||
try {
|
||||
socket = _listener.nbAccept();
|
||||
if (socket == null) {
|
||||
// in theory this shouldn't happen because we got an
|
||||
// ACCEPT_READY event, but better safe than sorry
|
||||
Log.info("Psych! Got ACCEPT_READY, but no connection.");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a new authing connection object to manage the
|
||||
// authentication of this client connection
|
||||
AuthingConnection acon = new AuthingConnection(this, socket);
|
||||
|
||||
// wire this connection into the select set
|
||||
_selset.add(acon.getSelectItem());
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Failure accepting new connection: " + ioe);
|
||||
|
||||
// make sure we don't leak a socket if something went awry
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ioe2) {
|
||||
Log.warning("Failed closing aborted connection: " + ioe2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a connection when it has a downstream message that needs
|
||||
* to be delivered.
|
||||
*/
|
||||
void postMessage (Connection conn, DownstreamMessage msg)
|
||||
{
|
||||
// sanity check
|
||||
if (conn == null || msg == null) {
|
||||
Log.warning("Bogosity.");
|
||||
Thread.dumpStack();
|
||||
|
||||
} else {
|
||||
// slap both these suckers onto the outgoing message queue
|
||||
_outq.append(new Tuple(conn, msg));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a connection if it experiences a network failure.
|
||||
*/
|
||||
void connectionFailed (Connection conn, IOException ioe)
|
||||
{
|
||||
// remove this connection from the select set
|
||||
_selset.remove(conn.getSelectItem());
|
||||
|
||||
// let our observers know what's up
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a connection when it discovers that it's closed.
|
||||
*/
|
||||
void connectionClosed (Connection conn)
|
||||
{
|
||||
Log.info("Removing closed connection: " + conn);
|
||||
|
||||
// remove this connection from the select set
|
||||
_selset.remove(conn.getSelectItem());
|
||||
|
||||
// let our observers know what's up
|
||||
}
|
||||
|
||||
protected AuthManager _authmgr;
|
||||
protected SelectSet _selset;
|
||||
|
||||
protected NonblockingServerSocket _listener;
|
||||
protected SelectItem _litem;
|
||||
|
||||
protected Queue _outq = new Queue();
|
||||
protected FramingOutputStream _framer;
|
||||
protected DataOutputStream _dout;
|
||||
|
||||
/** The default port on which we listen for connections. */
|
||||
protected static final int DEFAULT_CM_PORT = 4007;
|
||||
|
||||
/**
|
||||
* How long we wait for network events before checking our running
|
||||
* flag to see if we should still be running.
|
||||
*/
|
||||
protected static final int SELECT_LOOP_TIME = 30 * 1000;
|
||||
|
||||
// register our shared objects
|
||||
static {
|
||||
Registry.registerTypedObjects();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// $Id: ConnectionObserver.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A connection observer can be registered with the connection manager to
|
||||
* hear about new connections and to be notified when connections fail or
|
||||
* are closed. Only fully authenticated connections will be passed on to
|
||||
* the connection observer. Connections that fail to authenticate will be
|
||||
* handled entirely within the confines of the connection manager.
|
||||
*
|
||||
* @see ConnectionManager
|
||||
* @see Connection
|
||||
*/
|
||||
public interface ConnectionObserver
|
||||
{
|
||||
/**
|
||||
* Called when a new connection is established with the connection
|
||||
* manager.
|
||||
*
|
||||
* @param conn The newly established connection.
|
||||
*/
|
||||
public void connectionEstablished (Connection conn);
|
||||
|
||||
/**
|
||||
* Called if a connection fails for any reason. If a connection fails,
|
||||
* <code>connectionClosed</code> will not be called. This call to
|
||||
* <code>connectionFailued</code> is the last the observers will hear
|
||||
* about it.
|
||||
*
|
||||
* @param conn The connection in that failed.
|
||||
* @param fault The exception associated with the failure.
|
||||
*/
|
||||
public void connectionFailed (Connection conn, IOException fault);
|
||||
|
||||
/**
|
||||
* Called when a connection has been closed in an orderly manner.
|
||||
*
|
||||
* @param conn The recently closed connection.
|
||||
*/
|
||||
public void connectionClosed (Connection conn);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// $Id: NetEventHandler.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import ninja2.core.io_core.nbio.Selectable;
|
||||
|
||||
/**
|
||||
* When a network event arrives on a particular <code>Selectable</code>,
|
||||
* the connection manager calls the net event handler associated with that
|
||||
* selectable to process the event. There are only a few handlers (and
|
||||
* probably only ever will be): the one that accepts new connections, the
|
||||
* one that deals with a connection while the client is authenticating and
|
||||
* the one that processes messages from authenticated connections.
|
||||
* Providing this interface prevents us from having to do a bunch of
|
||||
* inefficient and ugly comparisons; instead we can simply call through an
|
||||
* interface method to the proper code.
|
||||
*/
|
||||
public interface NetEventHandler
|
||||
{
|
||||
/**
|
||||
* Called when a network event has occurred on the supplied source.
|
||||
* The <code>events</code> parameter indicates which event or events
|
||||
* have occurred.
|
||||
*/
|
||||
public void handleEvent (Selectable source, short events);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id: RunningConnection.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.server.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import ninja2.core.io_core.nbio.NonblockingSocket;
|
||||
import com.samskivert.cocktail.cher.net.UpstreamMessage;
|
||||
|
||||
/**
|
||||
* A running connection object represents a fully operational client
|
||||
* connection to the server.
|
||||
*/
|
||||
public class RunningConnection extends Connection
|
||||
{
|
||||
/**
|
||||
* Constructs a new running connection object to manage the supplied
|
||||
* client socket.
|
||||
*/
|
||||
public RunningConnection (ConnectionManager cmgr,
|
||||
NonblockingSocket socket)
|
||||
throws IOException
|
||||
{
|
||||
super(cmgr, socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new message has arrived from the client.
|
||||
*/
|
||||
public void handleMessage (UpstreamMessage msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// $Id: TestClient.java,v 1.1 2001/05/29 03:27:59 mdb Exp $
|
||||
|
||||
package com.samskivert.cocktail.cher.client.test;
|
||||
|
||||
import com.samskivert.cocktail.cher.net.*;
|
||||
import com.samskivert.cocktail.cher.client.*;
|
||||
|
||||
/**
|
||||
* A standalone test client.
|
||||
*/
|
||||
public class TestClient
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
UsernamePasswordCreds creds =
|
||||
new UsernamePasswordCreds("test", "test");
|
||||
Client client = new Client(creds);
|
||||
client.setServer("localhost", 4007);
|
||||
client.logon();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user