Starting to wire up client/server dobj stuff.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@22 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2001-06-02 01:30:37 +00:00
parent 2c3e4d9528
commit 1c6b292edc
18 changed files with 320 additions and 64 deletions
@@ -1,11 +1,13 @@
//
// $Id: ClientManager.java,v 1.1 2001/06/01 22:12:03 mdb Exp $
// $Id: ClientManager.java,v 1.2 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server;
import java.io.IOException;
import java.util.HashMap;
import com.threerings.cocktail.cher.Log;
import com.threerings.cocktail.cher.net.Credentials;
import com.threerings.cocktail.cher.server.net.*;
/**
@@ -13,6 +15,11 @@ import com.threerings.cocktail.cher.server.net.*;
* surprise) which are slightly more than just connections. Clients
* persist in the absence of connections in case a user goes bye bye
* unintentionally and wants to reconnect and continue their session.
*
* <p> The client manager operates with thread safety because it is called
* both from the conmgr thread (to notify of connections showing up or
* going away) and from the dobjmgr thread (when clients are given the
* boot for application-defined reasons).
*/
public class ClientManager implements ConnectionObserver
{
@@ -29,9 +36,22 @@ public class ClientManager implements ConnectionObserver
*
* @param conn The newly established connection.
*/
public void connectionEstablished (Connection conn)
public synchronized
void connectionEstablished (Connection conn, Credentials creds)
{
Log.info("Connection established: " + conn);
String username = creds.getUsername();
// see if there's a client already registered with this username
Client client = (Client)_clients.get(username);
if (client != null) {
Log.info("Session resumed [username=" + username +
", conn=" + conn + "].");
} else {
Log.info("Session initiated [username=" + username +
", conn=" + conn + "].");
}
}
/**
@@ -43,7 +63,8 @@ public class ClientManager implements ConnectionObserver
* @param conn The connection in that failed.
* @param fault The exception associated with the failure.
*/
public void connectionFailed (Connection conn, IOException fault)
public synchronized
void connectionFailed (Connection conn, IOException fault)
{
Log.info("Connection failed: " + conn + ": " + fault);
}
@@ -53,8 +74,10 @@ public class ClientManager implements ConnectionObserver
*
* @param conn The recently closed connection.
*/
public void connectionClosed (Connection conn)
public synchronized void connectionClosed (Connection conn)
{
Log.info("Connection closed: " + conn);
}
protected HashMap _clients = new HashMap();
}
@@ -0,0 +1,97 @@
//
// $Id: PresentsClient.java,v 1.1 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server;
import com.threerings.cocktail.cher.Log;
import com.threerings.cocktail.cher.dobj.*;
import com.threerings.cocktail.cher.net.*;
import com.threerings.cocktail.cher.server.net.*;
/**
* A client object represents a client session in the server. It is
* associated with a connection instance (while the client is connected)
* and acts as the intermediary for the remote client in terms of passing
* along events forwarded by the client, ensuring that subscriptions are
* maintained on behalf of the client and that events are forwarded to the
* client.
*
* <p><em>A note on synchronization:</em> the client object is structured
* so that its <code>Subscriber</code> implementation (which is called
* from the dobjmgr thread) can proceed without synchronization. This does
* not overlap with its other client duties which are called from the
* conmgr thread and therefore also need not be synchronized.
*/
public class Client implements Subscriber, MessageHandler
{
/**
* Constructs a new client instance bound to the specified username
* and initially associated with the specified connection instance.
*/
public Client (String username, Connection conn)
{
_username = username;
setConnection(conn);
}
/**
* Called by the client manager when a new connection arrives that
* authenticates as this already established client. This must only be
* called from the congmr thread.
*/
public void resumeSession (Connection conn)
{
// check to see if we've already got a connection object, in which
// case it's probably stale
if (_conn != null) {
Log.info("Closing stale connection [old=" + _conn +
", new=" + conn + "].");
// close the old connection (which results in everything being
// properly unregistered)
_conn.close();
}
// start using the new connection
setConnection(conn);
Log.info("Session resumed [client=" + this + "].");
}
protected void setConnection (Connection conn)
{
// keep a handle to the new connection
_conn = conn;
// tell the connection to pass messages on to us
_conn.setMessageHandler(this);
}
// documentation inherited from interface
public void handleMessage (UpstreamMessage message)
{
}
// documentation inherited from interface
public void objectAvailable (DObject object)
{
// queue up an object response
_conn.postMessage(new ObjectResponse(object));
}
// documentation inherited from interface
public void requestFailed (int oid, ObjectAccessException cause)
{
_conn.postMessage(new FailureResponse(oid));
}
// documentation inherited from interface
public boolean handleEvent (DEvent event, DObject target)
{
// forward the event to the client
_conn.postMessage(new EventNotification(event));
return true;
}
protected String _username;
protected transient Connection _conn;
}
@@ -1,5 +1,5 @@
//
// $Id: PresentsDObjectMgr.java,v 1.2 2001/06/01 20:35:39 mdb Exp $
// $Id: PresentsDObjectMgr.java,v 1.3 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server;
@@ -175,10 +175,11 @@ public class CherDObjectMgr implements DObjectManager
public boolean applyToObject (DObject target)
throws ObjectAccessException
{
int oid = getNextOid();
try {
// create a new instance of this object
DObject obj = (DObject)_class.newInstance();
int oid = getNextOid();
// initialize this object
obj.init(oid, CherDObjectMgr.this);
@@ -205,7 +206,8 @@ public class CherDObjectMgr implements DObjectManager
// let the subscriber know shit be fucked
if (_target != null) {
String errmsg = "Object instantiation failed: " + e;
_target.requestFailed(new ObjectAccessException(errmsg));
_target.requestFailed(
oid, new ObjectAccessException(errmsg));
}
}
@@ -243,14 +245,14 @@ public class CherDObjectMgr implements DObjectManager
// if it don't exist, let them know
if (obj == null) {
_target.requestFailed(new NoSuchObjectException(_oid));
_target.requestFailed(_oid, new NoSuchObjectException(_oid));
return false;
}
// check permissions
if (!obj.checkPermissions(_target)) {
String errmsg = "m.access_denied\t" + _oid;
_target.requestFailed(new ObjectAccessException(errmsg));
_target.requestFailed(_oid, new ObjectAccessException(errmsg));
return false;
}
@@ -1,5 +1,5 @@
//
// $Id: AuthingConnection.java,v 1.2 2001/05/30 23:58:31 mdb Exp $
// $Id: AuthingConnection.java,v 1.3 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server.net;
@@ -14,7 +14,8 @@ import com.threerings.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
public class AuthingConnection
extends Connection implements MessageHandler
{
/**
* Creates a new authing connection object that will manage the
@@ -25,6 +26,8 @@ public class AuthingConnection extends Connection
throws IOException
{
super(cmgr, socket);
// we are our own message handler
setMessageHandler(this);
}
/**
@@ -55,6 +58,11 @@ public class AuthingConnection extends Connection
return _authreq;
}
public String toString ()
{
return "[mode=AUTHING, addr=" + _socket.getInetAddress() + "]";
}
protected int _state = AWAITING_AUTH_REQUEST;
protected AuthRequest _authreq;
@@ -1,5 +1,5 @@
//
// $Id: Connection.java,v 1.3 2001/06/01 22:12:03 mdb Exp $
// $Id: Connection.java,v 1.4 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server.net;
@@ -45,6 +45,16 @@ public abstract class Connection implements NetEventHandler
_din = new DataInputStream(_fin);
}
/**
* Instructs the connection to pass parsed messages on to this handler
* for processing. This should be done before the connection is turned
* loose to process messages.
*/
public void setMessageHandler (MessageHandler handler)
{
_handler = handler;
}
/**
* Returns the select item associated with this connection.
*/
@@ -71,6 +81,33 @@ public abstract class Connection implements NetEventHandler
return _socket;
}
/**
* Closes this connection and unregisters it from the connection
* manager. This should only be called from the conmgr thread.
*/
public void close ()
{
// we shouldn't be closed twice
if (_socket == null) {
Log.warning("Attempted to re-close connection.");
return;
}
// unregister from the select set
_cmgr.connectionClosed(this);
// close our socket
try {
_socket.close();
} catch (IOException ioe) {
Log.warning("Error closing connection [conn=" + this +
", error=" + ioe + "].");
}
// clear out our socket reference to prevent repeat closings
_socket = null;
}
/**
* Called when our client socket has data available for reading.
*/
@@ -80,8 +117,8 @@ public abstract class Connection implements NetEventHandler
// 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));
_handler.handleMessage((UpstreamMessage)
TypedObjectFactory.readFrom(_din));
}
} catch (EOFException eofe) {
@@ -96,12 +133,6 @@ public abstract class Connection implements NetEventHandler
}
}
/**
* 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
@@ -121,4 +152,6 @@ public abstract class Connection implements NetEventHandler
protected OutputStream _out;
protected FramedInputStream _fin;
protected DataInputStream _din;
protected MessageHandler _handler;
}
@@ -1,5 +1,5 @@
//
// $Id: ConnectionManager.java,v 1.3 2001/06/01 22:12:03 mdb Exp $
// $Id: ConnectionManager.java,v 1.4 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server.net;
@@ -15,6 +15,7 @@ import com.samskivert.util.Queue;
import com.threerings.cocktail.cher.Log;
import com.threerings.cocktail.cher.io.FramingOutputStream;
import com.threerings.cocktail.cher.io.TypedObjectFactory;
import com.threerings.cocktail.cher.net.Credentials;
import com.threerings.cocktail.cher.net.DownstreamMessage;
import com.threerings.cocktail.cher.net.Registry;
@@ -102,8 +103,7 @@ public class ConnectionManager extends LoopingThread
* Notifies the connection observers of a connection event. Used
* internally.
*/
protected void notifyObservers (int code, Connection conn,
IOException cause)
protected void notifyObservers (int code, Connection conn, Object arg)
{
synchronized (_observers) {
for (int i = 0; i < _observers.size(); i++) {
@@ -111,10 +111,10 @@ public class ConnectionManager extends LoopingThread
(ConnectionObserver)_observers.get(i);
switch (code) {
case CONNECTION_ESTABLISHED:
obs.connectionEstablished(conn);
obs.connectionEstablished(conn, (Credentials)arg);
break;
case CONNECTION_FAILED:
obs.connectionFailed(conn, cause);
obs.connectionFailed(conn, (IOException)arg);
break;
case CONNECTION_CLOSED:
obs.connectionClosed(conn);
@@ -154,8 +154,8 @@ public class ConnectionManager extends LoopingThread
}
// check for connections that have completed authentication
Connection conn;
while ((conn = (Connection)_authq.getNonBlocking()) != null) {
AuthingConnection conn;
while ((conn = (AuthingConnection)_authq.getNonBlocking()) != null) {
// remove the old connection from the select set
_selset.remove(conn.getSelectItem());
@@ -168,7 +168,8 @@ public class ConnectionManager extends LoopingThread
_selset.add(rconn.getSelectItem());
// and let our observers know about our new connection
notifyObservers(CONNECTION_ESTABLISHED, rconn, null);
notifyObservers(CONNECTION_ESTABLISHED, rconn,
conn.getAuthRequest().getCredentials());
} catch (IOException ioe) {
Log.warning("Failure upgrading authing connection to " +
@@ -1,9 +1,10 @@
//
// $Id: ConnectionObserver.java,v 1.3 2001/06/01 22:12:03 mdb Exp $
// $Id: ConnectionObserver.java,v 1.4 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server.net;
import java.io.IOException;
import com.threerings.cocktail.cher.net.Credentials;
/**
* A connection observer can be registered with the connection manager to
@@ -23,8 +24,10 @@ public interface ConnectionObserver
* the connection observer.
*
* @param conn The newly established connection.
* @param creds The credentials with which this connection
* (successfully) authenticated.
*/
public void connectionEstablished (Connection conn);
public void connectionEstablished (Connection conn, Credentials creds);
/**
* Called if a connection fails for any reason. If a connection fails,
@@ -0,0 +1,19 @@
//
// $Id: MessageHandler.java,v 1.1 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server.net;
import com.threerings.cocktail.cher.net.UpstreamMessage;
/**
* After the connection object has parsed an entire upstream message, it
* passes it on to its message handler.
*/
public interface MessageHandler
{
/**
* Called when a complete message has been parsed from incoming
* network data.
*/
public void handleMessage (UpstreamMessage message);
}
@@ -1,5 +1,5 @@
//
// $Id: RunningConnection.java,v 1.2 2001/05/30 23:58:31 mdb Exp $
// $Id: RunningConnection.java,v 1.3 2001/06/02 01:30:37 mdb Exp $
package com.threerings.cocktail.cher.server.net;
@@ -30,4 +30,9 @@ public class RunningConnection extends Connection
public void handleMessage (UpstreamMessage msg)
{
}
public String toString ()
{
return "[mode=RUNNING, addr=" + _socket.getInetAddress() + "]";
}
}