Initial revision of cocktail multiplayer networked gaming platform. (Far
from complete.) git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
<!-- build configuration -->
|
||||||
|
<project name="cocktail" default="compile" basedir=".">
|
||||||
|
|
||||||
|
<!-- configuration parameters -->
|
||||||
|
<property name="app.name" value="cocktail"/>
|
||||||
|
<property name="deploy.home" value="dist"/>
|
||||||
|
<property name="dist.home" value="${deploy.home}"/>
|
||||||
|
<property name="dist.jar" value="${app.name}.jar"/>
|
||||||
|
<property name="javadoc.home" value="${deploy.home}/docs"/>
|
||||||
|
<property name="build.compiler" value="jikes"/>
|
||||||
|
<property name="java.libraries" value="/usr/share/java"/>
|
||||||
|
|
||||||
|
<!-- prepares the application directories -->
|
||||||
|
<target name="prepare">
|
||||||
|
<mkdir dir="${deploy.home}"/>
|
||||||
|
<mkdir dir="${deploy.home}/classes"/>
|
||||||
|
<mkdir dir="${javadoc.home}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- cleans out the installed application -->
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="${deploy.home}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- build the java class files -->
|
||||||
|
<target name="compile" depends="prepare">
|
||||||
|
<javac srcdir="src/java" destdir="${deploy.home}/classes"
|
||||||
|
debug="on" optimize="off" deprecation="off">
|
||||||
|
<classpath>
|
||||||
|
<fileset dir="${java.libraries}" includes="**/*.jar"/>
|
||||||
|
<fileset dir="lib" includes="**/*.jar"/>
|
||||||
|
<pathelement location="${deploy.home}/classes"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- build the javadoc documentation -->
|
||||||
|
<target name="javadoc" depends="prepare">
|
||||||
|
<javadoc sourcepath="src/java"
|
||||||
|
packagenames="robodj.*"
|
||||||
|
classpath="${deploy.home}/classes"
|
||||||
|
destdir="${javadoc.home}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- the default target is to rebuild everything -->
|
||||||
|
<target name="all" depends="clean,prepare,compile,javadoc"/>
|
||||||
|
|
||||||
|
<!-- builds our distribution files (war and jar) -->
|
||||||
|
<target name="dist" depends="prepare,compile">
|
||||||
|
<jar jarfile="${dist.home}/${dist.jar}"
|
||||||
|
basedir="${deploy.home}/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
Cocktail Mk3 -*- outline -*-
|
||||||
|
|
||||||
|
* What is it?
|
||||||
|
Cocktail is a platform on which multiplayer networked games may be
|
||||||
|
developed. The platform is (extremely) loosely based around the idea of a
|
||||||
|
cocktail party. For the most part, this just gives us a useful context
|
||||||
|
from which to choose names. The basic service of the Cocktail platform is
|
||||||
|
a simple information sharing mechanism based on the concept of distributed
|
||||||
|
objects. This layer is called "Cher" for reasons outlined in the Cher
|
||||||
|
design notes.
|
||||||
|
|
||||||
|
A distributed object has a set of subscribers. Whenever a modification is
|
||||||
|
made to that object, all of that object's subscribers are notified. This
|
||||||
|
has the beneficial effect of providing a framework in which to conceive a
|
||||||
|
distributed application based on who needs to know what. If information
|
||||||
|
need be shared among a set of clients, a distributed object can be created
|
||||||
|
to represent that information and those clients would subscribe. Then
|
||||||
|
modifications to that object (as well as simple notifications) can be
|
||||||
|
easily delivered to those clients and those clients only.
|
||||||
|
|
||||||
|
As one discovers after further use of the system, the distributed object
|
||||||
|
concept turns out to be a useful one for other reasons when designing
|
||||||
|
distributed applications (a subset of which are multiplayer networked
|
||||||
|
games). The distributed objects fit nicely as the model in the model,
|
||||||
|
view, controller pattern as well as into other useful patterns.
|
||||||
|
|
||||||
|
The primary value of the design is to bring the level of abstraction up
|
||||||
|
from network connections and packets, to objects and events.
|
||||||
|
|
||||||
|
** Getting the party started
|
||||||
|
Atop the distributed object layer, we further develop the concept of the
|
||||||
|
cocktail party. Parties tend to take place in rooms and be attended by
|
||||||
|
groups of people. This is the essence of the next layer of the system: a
|
||||||
|
framework for providing rooms, with occupants and mechanisms for the
|
||||||
|
people to move between those rooms. Within the rooms, we provide some
|
||||||
|
useful basic services like the ability to chat among the occupants of the
|
||||||
|
room, as well as some non-room-specific facilities like person to person
|
||||||
|
messaging from anywhere in the system and a location directory.
|
||||||
|
|
||||||
|
Not all games developed with the platform will want to use the room
|
||||||
|
concept, therefore we attempt at this layer and in all subsequent layers
|
||||||
|
to decouple our services as much as possible. This allows a game with
|
||||||
|
special needs or for whom our half-baked analogies don't apply, to
|
||||||
|
leverage some of the useful services without having to bend their design
|
||||||
|
in uncomfortable ways or hack up some additional interface to the services
|
||||||
|
we provide.
|
||||||
|
|
||||||
|
** Let's play
|
||||||
|
From here, we branch off into all sorts of interesting directions based on
|
||||||
|
the different kinds of games that are implemented with the system. We
|
||||||
|
provide matchmaking lobbies, an extension of the room concept to the game
|
||||||
|
room, on top of which is provided a framework for managing generic
|
||||||
|
turn-based games, and various other useful services. Again the philosophy
|
||||||
|
is to provide consistently designed, but decoupled services that can be
|
||||||
|
used within and along side whatever design works best for your game.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
Cher Mk3 Design -*- outline -*-
|
||||||
|
|
||||||
|
* Why Cher?
|
||||||
|
The basic function of this layer is to allow the sharing (Cher-ing) of
|
||||||
|
information among different nodes in the network. Plus, I don't think Cher
|
||||||
|
has ever had a software system named after her and it's high time. Imagine
|
||||||
|
Cher as the social lubricant that allows the party goers to communicate.
|
||||||
|
|
||||||
|
* Client components
|
||||||
|
|
||||||
|
** DObjectManager
|
||||||
|
Manages object proxies; converts value change requests into events,
|
||||||
|
forwards them via the iomgr; dispatches events on incoming queue; reaps
|
||||||
|
proxies when last subscriber goes away
|
||||||
|
|
||||||
|
** UI (AWT/Swing)
|
||||||
|
Standard AWT/Swing UI
|
||||||
|
|
||||||
|
** UI (Controller)?
|
||||||
|
Provides a paradigm of controllers and commands; code can post commands
|
||||||
|
back to the controller queue for later execution; UI elements structured
|
||||||
|
to automatically generate commands; will probably opt not to use this in
|
||||||
|
favor of Swing's built-in paradigms
|
||||||
|
|
||||||
|
** I/O Manager
|
||||||
|
*** Reader
|
||||||
|
Reads incoming data from the socket; decodes messages; posts events to
|
||||||
|
domgr queue; notifies object subscription penders (this should be done
|
||||||
|
asychronously)
|
||||||
|
|
||||||
|
*** Writer
|
||||||
|
Encodes object subscription and event forwarding requests; writes them to
|
||||||
|
the outgoing socket
|
||||||
|
|
||||||
|
** Client object
|
||||||
|
Informs exo-client about connection state changes; provides interface to
|
||||||
|
connection + authentication (logon) and disconnection (logoff); provides
|
||||||
|
access to omgr and client dobj
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
Cher Mk3 Notes -*- outline -*-
|
||||||
|
|
||||||
|
* Server components:
|
||||||
|
+ Connection manager
|
||||||
|
+ Client manager
|
||||||
|
+ Event dispatch thread (DObjectManager)
|
||||||
|
+ Server side services?
|
||||||
|
+ Room/Game managers
|
||||||
|
+ Info repository for shared info?
|
||||||
|
|
||||||
|
* Client components:
|
||||||
|
+ Network client (auth + DObject services)
|
||||||
|
+ Applet shell
|
||||||
|
+ Panel management
|
||||||
|
+ Components: chatbox, occupant info, turn indicator?
|
||||||
|
+ Room/Game panels, controllers, etc.
|
||||||
|
|
||||||
|
* Client detail:
|
||||||
|
+ Supply credentials (username, password)
|
||||||
|
+ Establishes, maintains connection:
|
||||||
|
login (connect), logout (disconnect)
|
||||||
|
+ Creates, manages client-side DObjectManager
|
||||||
|
|
||||||
|
* Events in the client will be dispatched on the AWT thread
|
||||||
|
- Provide pluggable "event dispatcher" on the client
|
||||||
|
- Allow extension of Event as DispatchableEvent so that event objects
|
||||||
|
themselves can extend Runnable and be popped right into
|
||||||
|
SwingUtil.invokeLater().
|
||||||
|
|
||||||
|
* Client perform all network ops on own thread, will call back to main
|
||||||
|
code through Observer interface to notify of state changes in the
|
||||||
|
authentication process/connectedness:
|
||||||
|
|
||||||
|
public interface ClientObserver
|
||||||
|
{
|
||||||
|
public void didConnect ();
|
||||||
|
public void connectionFailed ();
|
||||||
|
public void didLogon ();
|
||||||
|
public void logonFailed ();
|
||||||
|
public void didDisconnect ();
|
||||||
|
public void didLogoff ();
|
||||||
|
}
|
||||||
|
|
||||||
|
* Three Rings Client flowlist:
|
||||||
|
|
||||||
|
Instantiate client
|
||||||
|
Obtain auth info
|
||||||
|
Obtain room info
|
||||||
|
Authenticate
|
||||||
|
|
||||||
|
* Distributed objects are defined like a class with a set of public data
|
||||||
|
members which is then converted into an actual class with get/set
|
||||||
|
methods for each member.
|
||||||
|
|
||||||
|
public dclass GameObject
|
||||||
|
{
|
||||||
|
public int[] players;
|
||||||
|
public String description;
|
||||||
|
}
|
||||||
|
|
||||||
|
becomes
|
||||||
|
|
||||||
|
public class GameObject extends DObject
|
||||||
|
{
|
||||||
|
public void setPlayers (int[] players);
|
||||||
|
public void setPlayersAt (int index, int value);
|
||||||
|
public int[] getPlayers ();
|
||||||
|
public int getPlayersAt (int index);
|
||||||
|
|
||||||
|
public void setDescription (String description);
|
||||||
|
public String getDescription ();
|
||||||
|
}
|
||||||
|
|
||||||
|
* Classes:
|
||||||
|
|
||||||
|
DObjectManager
|
||||||
|
DObject
|
||||||
|
DEvent
|
||||||
|
DTransaction
|
||||||
|
Subscriber
|
||||||
|
|
||||||
|
* Events:
|
||||||
|
|
||||||
|
ATTRIBUTE_CHANGED
|
||||||
|
ATTRIBUTES_CHANGED
|
||||||
|
ATTRIBUTES_CHANGED_SPLIT
|
||||||
|
ELEMENT_CHANGED
|
||||||
|
OBJECT_DESTROYED
|
||||||
|
|
||||||
|
ELEMENT_ADDED
|
||||||
|
ELEMENT_REMOVED
|
||||||
|
|
||||||
|
OID_ADDED
|
||||||
|
OID_REMOVED
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// $Id: Log.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder class that contains a reference to the log object used by
|
||||||
|
* the Spine package.
|
||||||
|
*/
|
||||||
|
public class Log
|
||||||
|
{
|
||||||
|
public static com.samskivert.util.Log log =
|
||||||
|
new com.samskivert.util.Log("robodj");
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void debug (String message)
|
||||||
|
{
|
||||||
|
log.debug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void info (String message)
|
||||||
|
{
|
||||||
|
log.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void warning (String message)
|
||||||
|
{
|
||||||
|
log.warning(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void logStackTrace (Throwable t)
|
||||||
|
{
|
||||||
|
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
//
|
||||||
|
// $Id: Client.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.client;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.samskivert.cocktail.cher.Log;
|
||||||
|
import com.samskivert.cocktail.cher.net.Credentials;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Through the client object, a connection to the system is established
|
||||||
|
* and maintained. The client object maintains two separate threads (a
|
||||||
|
* reader and a writer) through which all network traffic is managed.
|
||||||
|
*/
|
||||||
|
public class Client
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs a client object with the supplied credentials. These
|
||||||
|
* creds will be used to authenticate with any server to which this
|
||||||
|
* client attempts to connect.
|
||||||
|
*/
|
||||||
|
public Client (Credentials creds)
|
||||||
|
{
|
||||||
|
_creds = creds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the supplied observer with this client. While registered
|
||||||
|
* the observer will receive notifications of state changes within the
|
||||||
|
* client. The function will refuse to register an already registered
|
||||||
|
* observer.
|
||||||
|
*
|
||||||
|
* @see ClientObserver
|
||||||
|
*/
|
||||||
|
public void addObserver (ClientObserver observer)
|
||||||
|
{
|
||||||
|
synchronized (_observers) {
|
||||||
|
// disallow multiple instances of the same observer
|
||||||
|
if (!_observers.contains(observer)) {
|
||||||
|
_observers.add(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the supplied observer. Upon return of this function,
|
||||||
|
* the observer will no longer receive notifications of state changes
|
||||||
|
* within the client.
|
||||||
|
*/
|
||||||
|
public void removeObserver (ClientObserver observer)
|
||||||
|
{
|
||||||
|
synchronized (_observers) {
|
||||||
|
_observers.remove(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the client to communicate with the server on the
|
||||||
|
* supplied hostname/port combination.
|
||||||
|
*
|
||||||
|
* @see #logon
|
||||||
|
*/
|
||||||
|
public void setServer (String hostname, int port)
|
||||||
|
{
|
||||||
|
_hostname = hostname;
|
||||||
|
_port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname of the server to which this client is
|
||||||
|
* currently configured to connect.
|
||||||
|
*/
|
||||||
|
public String getHostname ()
|
||||||
|
{
|
||||||
|
return _hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the port on which this client is currently configured to
|
||||||
|
* connect to the server.
|
||||||
|
*/
|
||||||
|
public int getPort ()
|
||||||
|
{
|
||||||
|
return _port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the credentials with which this client is currently
|
||||||
|
* configured to connect to the server.
|
||||||
|
*/
|
||||||
|
public Credentials getCredentials ()
|
||||||
|
{
|
||||||
|
return _creds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that this client connect and logon to the server with
|
||||||
|
* which it was previously configured.
|
||||||
|
*/
|
||||||
|
public synchronized void logon ()
|
||||||
|
{
|
||||||
|
// if we have a communicator reference, we're already logged on
|
||||||
|
if (_comm != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise create a new communicator instance and start it up.
|
||||||
|
// this will initiate the logon process
|
||||||
|
_comm = new Communicator(this);
|
||||||
|
_comm.logon();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the client log off of the server to which it is
|
||||||
|
* connected.
|
||||||
|
*
|
||||||
|
* @param abortable If true, the client will call
|
||||||
|
* <code>clientWillDisconnect</code> on all of the client observers
|
||||||
|
* and abort the logoff process if any of them return false. If false,
|
||||||
|
* <code>clientWillDisconnect</code> will not be called at all.
|
||||||
|
*
|
||||||
|
* @return true if the logoff succeeded, false if it failed due to a
|
||||||
|
* disagreeable observer.
|
||||||
|
*/
|
||||||
|
public boolean logoff (boolean abortable)
|
||||||
|
{
|
||||||
|
// if the request is abortable, let's run it past the observers
|
||||||
|
// before we act upon it
|
||||||
|
if (abortable && notifyObservers(CLIENT_WILL_LOGOFF, null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask the communicator to send a logoff message and disconnect
|
||||||
|
// from the server
|
||||||
|
_comm.logoff();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean notifyObservers (int code, Exception cause)
|
||||||
|
{
|
||||||
|
boolean rejected = false;
|
||||||
|
synchronized (_observers) {
|
||||||
|
for (int i = 0; i < _observers.size(); i++) {
|
||||||
|
ClientObserver obs = (ClientObserver)_observers.get(i);
|
||||||
|
switch (code) {
|
||||||
|
case CLIENT_DID_LOGON:
|
||||||
|
obs.clientDidLogon(this);
|
||||||
|
break;
|
||||||
|
case CLIENT_FAILED_TO_LOGON:
|
||||||
|
obs.clientFailedToLogon(this, cause);
|
||||||
|
break;
|
||||||
|
case CLIENT_CONNETION_FAILED:
|
||||||
|
obs.clientConnectionFailed(this, cause);
|
||||||
|
break;
|
||||||
|
case CLIENT_WILL_LOGOFF:
|
||||||
|
if (!obs.clientWillLogoff(this)) {
|
||||||
|
rejected = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CLIENT_DID_LOGOFF:
|
||||||
|
obs.clientDidLogoff(this);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Invalid code supplied to " +
|
||||||
|
"notifyObservers: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void communicatorDidExit ()
|
||||||
|
{
|
||||||
|
// clear out our communicator reference
|
||||||
|
_comm = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Credentials _creds;
|
||||||
|
protected String _hostname;
|
||||||
|
protected int _port;
|
||||||
|
|
||||||
|
/** Our list of client observers. */
|
||||||
|
protected List _observers = new ArrayList();
|
||||||
|
|
||||||
|
/** The entity that manages our network communications. */
|
||||||
|
protected Communicator _comm;
|
||||||
|
|
||||||
|
// client observer codes
|
||||||
|
static final int CLIENT_DID_LOGON = 0;
|
||||||
|
static final int CLIENT_FAILED_TO_LOGON = 1;
|
||||||
|
static final int CLIENT_CONNETION_FAILED = 2;
|
||||||
|
static final int CLIENT_WILL_LOGOFF = 3;
|
||||||
|
static final int CLIENT_DID_LOGOFF = 4;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// $Id: ClientObserver.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client observer is registered with the client instance to be notified
|
||||||
|
* when state changes happen within the client. Specifically, logon
|
||||||
|
* sucess/failure and logoff.
|
||||||
|
*
|
||||||
|
* <p> These callbacks happen on the client thread and should therefore
|
||||||
|
* not be used to perform any complex action or do much more than relay
|
||||||
|
* the signal to some other thread (like the AWT thread) to act more fully
|
||||||
|
* on the notice.
|
||||||
|
*
|
||||||
|
* <p> In the normal course of affairs, <code>clientDidLogon</code> will
|
||||||
|
* be called after the client successfully logs on to the server and
|
||||||
|
* <code>clientDidLogoff</code> will be called after the client logs off
|
||||||
|
* of the server. If logon fails for any reson,
|
||||||
|
* <code>clientFailedToLogon</code> will be called to explain the failure.
|
||||||
|
*
|
||||||
|
* <p> <code>clientWillLogoff</code> will only be called when an abortable
|
||||||
|
* logoff is requested (like when the user clicks on a logoff button of
|
||||||
|
* some sort). It will not be called during non-abortable logoff requests
|
||||||
|
* (like when the browser calls stop on the applet and is about to yank
|
||||||
|
* the rug out from under us). If an observer aborts the logoff request,
|
||||||
|
* it should notify the user in some way why the request was aborted
|
||||||
|
* (<em>but it shouldn't do so on the thread that calls
|
||||||
|
* <code>clientWillLogoff</code></em>).
|
||||||
|
*
|
||||||
|
* <p> If the client connection fails unexpectedly,
|
||||||
|
* <code>clientConnectionFailed</code> will be called to let the
|
||||||
|
* observers know that we lost our connection to the
|
||||||
|
* server. <code>clientDidLogoff</code> will be called immediately
|
||||||
|
* afterwards as a normal logoff procedure is effected.
|
||||||
|
*/
|
||||||
|
public interface ClientObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called after the client successfully connected to and authenticated
|
||||||
|
* with the server. The entire object system is up and running by the
|
||||||
|
* time this method is called.
|
||||||
|
*/
|
||||||
|
public void clientDidLogon (Client client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if anything fails during the logon attempt. This could be a
|
||||||
|
* network failure, authentication failure or otherwise. The exception
|
||||||
|
* provided will indicate the cause of the failure.
|
||||||
|
*/
|
||||||
|
public void clientFailedToLogon (Client client, Exception cause);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the connection to the server went away for some
|
||||||
|
* unexpected reason. This will be followed by a call to
|
||||||
|
* <code>clientDidLogoff</code>.
|
||||||
|
*/
|
||||||
|
public void clientConnectionFailed (Client client, Exception cause);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an abortable logoff requrest is made. If the observer
|
||||||
|
* returns false from this method, the client will abort the logoff
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
public boolean clientWillLogoff (Client client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the client has been logged off of the server and has
|
||||||
|
* disconnected.
|
||||||
|
*/
|
||||||
|
public void clientDidLogoff (Client client);
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
//
|
||||||
|
// $Id: Communicator.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.client;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import com.samskivert.util.Queue;
|
||||||
|
import com.samskivert.cocktail.cher.Log;
|
||||||
|
import com.samskivert.cocktail.cher.net.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The client performs all network I/O on separate threads (one for
|
||||||
|
* reading and one for writing). The communicator class encapsulates that
|
||||||
|
* functionality.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Logon synopsis:
|
||||||
|
*
|
||||||
|
* Client.logon():
|
||||||
|
* - Calls Communicator.start()
|
||||||
|
* Communicator.start():
|
||||||
|
* - spawn Reader thread
|
||||||
|
* Reader.run():
|
||||||
|
* { - connect
|
||||||
|
* - authenticate
|
||||||
|
* } if either fail, notify observers of failed logon
|
||||||
|
* - start writer thread
|
||||||
|
* - notify observers that we're logged on
|
||||||
|
* - read loop
|
||||||
|
* Writer.run():
|
||||||
|
* - write loop
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
class Communicator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new communicator instance which is associated with the
|
||||||
|
* supplied client.
|
||||||
|
*/
|
||||||
|
public Communicator (Client client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs on to the server and initiates our full-duplex message
|
||||||
|
* exchange.
|
||||||
|
*/
|
||||||
|
public void logon ()
|
||||||
|
{
|
||||||
|
// make sure things are copacetic
|
||||||
|
if (_reader != null) {
|
||||||
|
throw new RuntimeException("Communicator already started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// start up the reader thread. it will connect to the server and
|
||||||
|
// start up the writer thread if everything went successfully
|
||||||
|
_reader = new Reader();
|
||||||
|
_reader.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delivers a logoff notification to the server and shuts down the
|
||||||
|
* network connection. Also causes all communication threads to
|
||||||
|
* terminate.
|
||||||
|
*/
|
||||||
|
public void logoff ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues up the specified message for delivery to the server.
|
||||||
|
*/
|
||||||
|
public void postMessage (UpstreamMessage msg)
|
||||||
|
{
|
||||||
|
// simply append the message to the queue
|
||||||
|
_msgq.append(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void startWriter ()
|
||||||
|
{
|
||||||
|
if (_writer != null) {
|
||||||
|
throw new RuntimeException("Writer already started!?");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new writer thread and start it up
|
||||||
|
_writer = new Writer();
|
||||||
|
_writer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void readerDidExit ()
|
||||||
|
{
|
||||||
|
// clear out our reader reference
|
||||||
|
_reader = null;
|
||||||
|
|
||||||
|
// let the client know when we finally go away
|
||||||
|
_client.communicatorDidExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class Reader extends Thread
|
||||||
|
{
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// first we connect and authenticate with the server
|
||||||
|
try {
|
||||||
|
// connect to the server
|
||||||
|
connect();
|
||||||
|
|
||||||
|
// then authenticate
|
||||||
|
logon();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// let the observers know that we've failed
|
||||||
|
_client.notifyObservers(Client.CLIENT_FAILED_TO_LOGON, e);
|
||||||
|
// and terminate our communicator thread
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// once authenticated, we go into full-duplex mode,
|
||||||
|
// starting up another thread to listen for messages while
|
||||||
|
// we handle the delivery of messages
|
||||||
|
startWriter();
|
||||||
|
listen();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// let the communicator know when we finally go away
|
||||||
|
readerDidExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void connect ()
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// if we're already connected, we freak out
|
||||||
|
if (_socket != null) {
|
||||||
|
throw new IOException("Already connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up the address of the target server
|
||||||
|
InetAddress host = InetAddress.getByName(_client.getHostname());
|
||||||
|
|
||||||
|
// establish a socket connection to said server
|
||||||
|
_socket = new Socket(host, _client.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void logon ()
|
||||||
|
throws LogonException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void listen ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class Writer extends Thread
|
||||||
|
{
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Client _client;
|
||||||
|
protected Reader _reader;
|
||||||
|
protected Writer _writer;
|
||||||
|
|
||||||
|
protected Socket _socket;
|
||||||
|
protected DataInputStream _din;
|
||||||
|
protected DataInputStream _dout;
|
||||||
|
protected Queue _msgq = new Queue();
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// $Id: LogonException.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A logon exception is used to indicate a failure to log on to the
|
||||||
|
* server. The reason for failure is encoded as a string and stored in the
|
||||||
|
* message field of the exception.
|
||||||
|
*/
|
||||||
|
public class LogonException extends Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs a logon exception with the supplied logon failure code.
|
||||||
|
*/
|
||||||
|
public LogonException (String code)
|
||||||
|
{
|
||||||
|
super(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// $Id: Event.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.dobj;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
public class Event
|
||||||
|
{
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// $Id: ObjectStreamException.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object stream exception is used to communicate an error in
|
||||||
|
* processing a typed object stream. As any error is fatal, we don't
|
||||||
|
* differentiate exceptions with separate classes, but instead communicate
|
||||||
|
* the nature of the error in the exception message with the assumption
|
||||||
|
* that the caller will want to raise holy hell in every case.
|
||||||
|
*/
|
||||||
|
public class ObjectStreamException extends Exception
|
||||||
|
{
|
||||||
|
public ObjectStreamException (String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// $Id: TypedObject.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A typed object is one that is associated with a particular type code.
|
||||||
|
* The type code can be communicated on the wire and used by the receiving
|
||||||
|
* end to instantiate the proper typed object class for decoding.
|
||||||
|
*/
|
||||||
|
public abstract class TypedObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Each typed object class must associate itself with a type value via
|
||||||
|
* the <code>TypedObjectFactory</code> so that the factory can
|
||||||
|
* instantiate the proper <code>TypedObject</code> derived class when
|
||||||
|
* decoding an incoming message.
|
||||||
|
*
|
||||||
|
* @return The type code associated with this object.
|
||||||
|
*/
|
||||||
|
public abstract short getType ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each typed object class must be able to write itself to a stream.
|
||||||
|
* It should first call <code>super.writeTo()</code> before writing
|
||||||
|
* its own fields to the stream.
|
||||||
|
*/
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
out.writeShort(getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each typed object class must be able to read itself from a stream.
|
||||||
|
* It should first call <code>super.readFrom()</code> before reading
|
||||||
|
* its own fields from the stream.
|
||||||
|
*/
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// nothing to do because the TypedObjectFactory already read our
|
||||||
|
// type value from the stream
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// $Id: TypedObjectFactory.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The encodable factory is used to unserialize encodable object instances
|
||||||
|
* from a stream. It maintains a mapping of encodable object classes to
|
||||||
|
* identifier codes which it uses to determine what sort of object is on
|
||||||
|
* the stream. The encodable mechanism is used to associate a particular
|
||||||
|
* class with some compact code that can be transmitted on the wire to
|
||||||
|
* identify that class.
|
||||||
|
*/
|
||||||
|
public class TypedObjectFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Reads (unserializes) a typed object from the supplied data input
|
||||||
|
* stream.
|
||||||
|
*
|
||||||
|
* @return The unserialized typed object instance.
|
||||||
|
*/
|
||||||
|
public static TypedObject readFrom (DataInputStream din)
|
||||||
|
throws IOException, ObjectStreamException
|
||||||
|
{
|
||||||
|
// first determine the type of the incoming object
|
||||||
|
short type = din.readShort();
|
||||||
|
|
||||||
|
// now instantiate the proper object and decode the remainder
|
||||||
|
TypedObject msg = newObjectByType(type);
|
||||||
|
msg.readFrom(din);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static TypedObject newObjectByType (short type)
|
||||||
|
throws ObjectStreamException
|
||||||
|
{
|
||||||
|
Class clazz = (Class)_classes.get(new Short(type));
|
||||||
|
if (clazz == null) {
|
||||||
|
String errmsg = "Unknown object type: " + type;
|
||||||
|
throw new ObjectStreamException(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (TypedObject)clazz.newInstance();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
String errmsg = "Unable to instantiate typed object " +
|
||||||
|
"[class=" + clazz.getName() + ", error=" + t + "].";
|
||||||
|
throw new ObjectStreamException(errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void registerClass (short type, Class clazz)
|
||||||
|
{
|
||||||
|
Short key = new Short(type);
|
||||||
|
|
||||||
|
// make sure no funny business is afoot
|
||||||
|
if (_classes.containsKey(key)) {
|
||||||
|
Class incumbent = (Class)_classes.get(key);
|
||||||
|
String errmsg = "Cannot register " + clazz.getName() +
|
||||||
|
" as type " + type + " because " + incumbent.getName() +
|
||||||
|
" is already registered with that type.";
|
||||||
|
throw new RuntimeException(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the mapping
|
||||||
|
_classes.put(key, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Our type to class mapping table. */
|
||||||
|
protected static HashMap _classes = new HashMap();
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// $Id: AuthRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||||
|
|
||||||
|
public class AuthenticationRequest extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for an object subscription request. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public AuthenticationRequest ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a authentication request with the supplied credentials.
|
||||||
|
*/
|
||||||
|
public AuthenticationRequest (Credentials creds)
|
||||||
|
{
|
||||||
|
_creds = creds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
_creds.writeTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
_creds = (Credentials)TypedObjectFactory.readFrom(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object id of the distributed object to which we are
|
||||||
|
* subscribing.
|
||||||
|
*/
|
||||||
|
protected Credentials _creds;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// $Id: Credentials.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObject;
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Credentials are supplied by the client implementation and sent along to
|
||||||
|
* the server during the authentication process. To provide support for a
|
||||||
|
* variety of authentication methods, the credentials class is meant to be
|
||||||
|
* subclassed for the particular method (ie. username + password) in use
|
||||||
|
* in a given system.
|
||||||
|
*
|
||||||
|
* <p> All derived classes should provide a no argument constructor so
|
||||||
|
* that they can be instantiated prior to reconstruction from a data input
|
||||||
|
* stream.
|
||||||
|
*/
|
||||||
|
public abstract class Credentials extends TypedObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* All credential derived classes should base their typed object code
|
||||||
|
* on this base value.
|
||||||
|
*/
|
||||||
|
public static final short TYPE_BASE = 300;
|
||||||
|
|
||||||
|
// register our credential classes
|
||||||
|
static {
|
||||||
|
TypedObjectFactory.registerClass(UsernamePasswordCreds.TYPE,
|
||||||
|
UsernamePasswordCreds.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// $Id: DownstreamMessage.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObject;
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <code>DownstreamMessage</code> class encapsulates a message in the
|
||||||
|
* Distributed Object Protocol that flows from the server to the
|
||||||
|
* client. Downstream messages include object subscription, event
|
||||||
|
* forwarding and session management.
|
||||||
|
*/
|
||||||
|
public class DownstreamMessage
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* All downstream message derived classes should base their typed
|
||||||
|
* object code on this base value.
|
||||||
|
*/
|
||||||
|
public static final short TYPE_BASE = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The message id of the upstream message with which this downstream
|
||||||
|
* message is associated (or -1 if it is not associated with any
|
||||||
|
* upstream message). Because not every downstream message class cares
|
||||||
|
* to provide an upstream message id, this field is not serialized
|
||||||
|
* when the base downstream message class is serialized. Thus derived
|
||||||
|
* classes that care about message id should take care to initialize,
|
||||||
|
* serialize and unserialize the value theirselves.
|
||||||
|
*/
|
||||||
|
public short messageId = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each downstream message derived class must provide a zero argument
|
||||||
|
* constructor so that the <code>TypedObjectFactory</code> can create
|
||||||
|
* a new instance of said class prior to unserializing it.
|
||||||
|
*/
|
||||||
|
public DownstreamMessage ()
|
||||||
|
{
|
||||||
|
// nothing to do...
|
||||||
|
}
|
||||||
|
|
||||||
|
// register our downstream message classes
|
||||||
|
static {
|
||||||
|
TypedObjectFactory.registerClass(AuthenticationRequest.TYPE,
|
||||||
|
AuthenticationRequest.class);
|
||||||
|
TypedObjectFactory.registerClass(SubscribeRequest.TYPE,
|
||||||
|
SubscribeRequest.class);
|
||||||
|
TypedObjectFactory.registerClass(FetchRequest.TYPE,
|
||||||
|
FetchRequest.class);
|
||||||
|
TypedObjectFactory.registerClass(UnsubscribeNotification.TYPE,
|
||||||
|
UnsubscribeNotification.class);
|
||||||
|
TypedObjectFactory.registerClass(ForwardEventNotification.TYPE,
|
||||||
|
ForwardEventNotification.class);
|
||||||
|
TypedObjectFactory.registerClass(PingNotification.TYPE,
|
||||||
|
PingNotification.class);
|
||||||
|
TypedObjectFactory.registerClass(LogoffNotification.TYPE,
|
||||||
|
LogoffNotification.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The code for an event notification. */
|
||||||
|
public static final byte EVENT = 0x00;
|
||||||
|
/** The code for a fetch/subscribe object response. */
|
||||||
|
public static final byte OBJECT = 0x01;
|
||||||
|
/** The code for a request failure. */
|
||||||
|
public static final byte FAILURE = 0x02;
|
||||||
|
/** The code for a pong response. */
|
||||||
|
public static final byte PONG = 0x03;
|
||||||
|
/** The code for an end of transmission notification. */
|
||||||
|
public static final byte EOT = 0x04;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// $Id: EventNotification.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
public class EventNotification extends DownstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for an event notification. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public EventNotification ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an event notification for the supplied event.
|
||||||
|
*/
|
||||||
|
public EventNotification (Event event)
|
||||||
|
{
|
||||||
|
_event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
_event.writeTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
_event = EventFactory.readFrom(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The event which we are forwarding. */
|
||||||
|
protected Event _event;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// $Id: FetchRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
public class FetchRequest extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for an object fetch request. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public FetchRequest ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a fetch request for the distributed object with the
|
||||||
|
* specified object id.
|
||||||
|
*/
|
||||||
|
public FetchRequest (int oid)
|
||||||
|
{
|
||||||
|
_oid = oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
out.writeInt(_oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
_oid = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object id of the distributed object which we are fetching.
|
||||||
|
*/
|
||||||
|
protected int _oid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// $Id: ForwardEventRequest.java,v 1.1 2001/05/22 06:07:59 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
import com.samskivert.cocktail.cher.dobj.Event;
|
||||||
|
import com.samskivert.cocktail.cher.dobj.EventFactory;
|
||||||
|
|
||||||
|
public class ForwardEventNotification extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for a forward event notification. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public ForwardEventNotification ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a forward event notification for the supplied event.
|
||||||
|
*/
|
||||||
|
public ForwardEventNotification (Event event)
|
||||||
|
{
|
||||||
|
_event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
_event.writeTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
_event = EventFactory.readFrom(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The event which we are forwarding. */
|
||||||
|
protected Event _event;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// $Id: LogoffRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
public class LogoffNotification extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for a logoff notification. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public LogoffNotification ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// $Id: PingRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
public class PingNotification extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for a ping notification. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public PingNotification ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// $Id: SubscribeRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
public class SubscribeRequest extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for an object subscription request. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public SubscribeRequest ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a subscribe request for the distributed object with the
|
||||||
|
* specified object id.
|
||||||
|
*/
|
||||||
|
public SubscribeRequest (int oid)
|
||||||
|
{
|
||||||
|
_oid = oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
out.writeInt(_oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
_oid = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object id of the distributed object to which we are
|
||||||
|
* subscribing.
|
||||||
|
*/
|
||||||
|
protected int _oid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// $Id: UnsubscribeRequest.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
public class UnsubscribeNotification extends UpstreamMessage
|
||||||
|
{
|
||||||
|
/** The code for an unsubscribe notification. */
|
||||||
|
public static final short TYPE = TYPE_BASE + 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public UnsubscribeNotification ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a unsubscribe notification for the distributed object
|
||||||
|
* with the specified object id.
|
||||||
|
*/
|
||||||
|
public UnsubscribeNotification (int oid)
|
||||||
|
{
|
||||||
|
_oid = oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
out.writeInt(_oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
_oid = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object id of the distributed object from which we are
|
||||||
|
* unsubscribing.
|
||||||
|
*/
|
||||||
|
protected int _oid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
//
|
||||||
|
// $Id: UpstreamMessage.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObject;
|
||||||
|
import com.samskivert.cocktail.cher.io.TypedObjectFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <code>UpstreamMessage</code> class encapsulates a message in the
|
||||||
|
* Distributed Object Protocol that flows from the client to the server.
|
||||||
|
* Upstream messages include object subscription, event forwarding and
|
||||||
|
* session management.
|
||||||
|
*/
|
||||||
|
public abstract class UpstreamMessage extends TypedObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* All upstream message derived classes should base their typed object
|
||||||
|
* code on this base value.
|
||||||
|
*/
|
||||||
|
public static final short TYPE_BASE = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a unique (within the context of a reasonable period of
|
||||||
|
* time) identifier assigned to each upstream message. The message ids
|
||||||
|
* are used to correlate a downstream response message to the
|
||||||
|
* appropriate upstream request message.
|
||||||
|
*/
|
||||||
|
public short messageId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each upstream message derived class must provide a zero argument
|
||||||
|
* constructor so that the <code>TypedObjectFactory</code> can create
|
||||||
|
* a new instance of said class prior to unserializing it.
|
||||||
|
*/
|
||||||
|
public UpstreamMessage ()
|
||||||
|
{
|
||||||
|
// automatically generate a valid message id; on the client, this
|
||||||
|
// will be used, on the server it will be overwritten by the
|
||||||
|
// unserialized value
|
||||||
|
this.messageId = nextMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived classes should override this function to write their fields
|
||||||
|
* out to the supplied data output stream. They <em>must</em> be sure
|
||||||
|
* to first call <code>super.writeTo()</code>.
|
||||||
|
*/
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.writeTo(out);
|
||||||
|
out.writeShort(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived classes should override this function to read their fields
|
||||||
|
* from the supplied data input stream. They <em>must</em> be sure to
|
||||||
|
* first call <code>super.readFrom()</code>.
|
||||||
|
*/
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super.readFrom(in);
|
||||||
|
messageId = in.readShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next message id suitable for use by an upstream
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
protected static synchronized short nextMessageId ()
|
||||||
|
{
|
||||||
|
_nextMessageId = (short)((_nextMessageId + 1) % Short.MAX_VALUE);
|
||||||
|
return _nextMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to generate monotonically increasing message ids on
|
||||||
|
* the client as new messages are generated.
|
||||||
|
*/
|
||||||
|
protected static short _nextMessageId;
|
||||||
|
|
||||||
|
// register our upstream message classes
|
||||||
|
static {
|
||||||
|
TypedObjectFactory.registerClass(AuthenticationRequest.TYPE,
|
||||||
|
AuthenticationRequest.class);
|
||||||
|
TypedObjectFactory.registerClass(SubscribeRequest.TYPE,
|
||||||
|
SubscribeRequest.class);
|
||||||
|
TypedObjectFactory.registerClass(FetchRequest.TYPE,
|
||||||
|
FetchRequest.class);
|
||||||
|
TypedObjectFactory.registerClass(UnsubscribeNotification.TYPE,
|
||||||
|
UnsubscribeNotification.class);
|
||||||
|
TypedObjectFactory.registerClass(ForwardEventNotification.TYPE,
|
||||||
|
ForwardEventNotification.class);
|
||||||
|
TypedObjectFactory.registerClass(PingNotification.TYPE,
|
||||||
|
PingNotification.class);
|
||||||
|
TypedObjectFactory.registerClass(LogoffNotification.TYPE,
|
||||||
|
LogoffNotification.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// $Id: UsernamePasswordCreds.java,v 1.1 2001/05/22 06:08:00 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.cocktail.cher.net;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class UsernamePasswordCreds
|
||||||
|
{
|
||||||
|
public static final short TYPE = TYPE_BASE + 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zero argument constructor used when unserializing an instance.
|
||||||
|
*/
|
||||||
|
public UsernamePasswordCreds ()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct credentials with the supplied username and password.
|
||||||
|
*/
|
||||||
|
public UsernamePasswordCreds (String username, String password)
|
||||||
|
{
|
||||||
|
_username = username;
|
||||||
|
_password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getType ()
|
||||||
|
{
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo (DataOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
out.writeUTF(_username);
|
||||||
|
out.writeUTF(_password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFrom (DataInputStream in)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
_username = in.readUTF();
|
||||||
|
_password = in.readUTF();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String _username;
|
||||||
|
protected String _password;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user