-
Notifications
You must be signed in to change notification settings - Fork 104
Relay Server
This document provides a high level view of the Relay Server for the CodeU Chat project.
The Relay Server provides a simple message-based interconnection between CodeU teams’ servers and applications. A CodeU Server may send a Message Object to the Relay Server, and the Relay Server will deliver that message to any Server that is reading from the Relay Server’s broadcast stream. Messages sent to the Relay Server will be retained for an indeterminate time (but not forever), so they can be read multiple times if desired.
CodeU teams are encouraged to incorporate the Relay Server into their designs. The primary use of the Relay Server will be to share messages between Codeu team applications, but other uses are possible.
There is a single centralized Relay Server that is always running, or teams may run their own Relay Server locally for testing purposes. Individual Chat Servers, as they start up, can optionally connect to the Relay Server. This is controlled by command line parameters to the command that starts the Server. The default behavior of the Server (if it is connected to the Relay Server) is to automatically send every new Message object to the Relay. The receiving side of the Relay interface is implemented, but is not currently used by the Server. It will be up to the individual Server project teams to incorporate the Relay into their designs and implement the handling of objects received through the Relay.
Each Message sent through the Relay Server is packaged into a Bundle. Each Bundle includes the Message, the Conversation that contains the Message, and the User who authored the Message. The unique identifier (Uuid) that is associated with each object is preserved through the Relay Server, so servers can easily detect and handle duplicate copies of objects.
In addition to the basic Write/Read of Messages through the Relay, the Relay Server enforces a simple authentication scheme to identify and authorize individual Servers. Each team is given a short secret string (referred to as Secret in this document) that must be used in the communications with the Relay Server. If this string is not passed as part of the Relay Server call, the Relay Server will reject the connection from the server. Please note that the Secret, although it does not protect sensitive data, should be kept private. In particular, it must not be placed in any file that will get checked in to your git repository. If this does happen, notify your advisor so we can invalidate it and assign you a new one.
codeu.chat.common.Relay
There are three functions that make up the interface; pack()
, write()
, and
read()
. They use nested interface classes Bundle (Relay.Bundle
) and
Component (Relay.Bundle.Component
).
codeu.chat.common.Relay.Bundle.Component
Component is a common way of representing Users, Conversations, and Messages to the Relay. It always contains a Uuid, a String, and a Time. A Component is created from an object, and then used to regenerate that object after transmission.
codeu.chat.common.Relay.Bundle
Bundle is a way of packaging a Message with its related objects - Users and Conversations. Each Bundle contains a component for one of each of these objects, plus a Time object (when the Bundle was created), the Uuid of the Team Server where the object originated, and an additional Uuid for the Bundle itself.
This is a convenience function for creating a Component object from the individual fields of a Message, Conversation, or User.
Send a Message (and its associated User and Conversation) to the Relay Server.
The objects are passed in as Component objects, and write()
will further
package them as a Bundle for transmission to the server.
The write()
call also requires a Team Uuid and the Team Secret. They must
match the Relay Server’s database or the write will be rejected by the Relay
Server.
Receive zero or more Message Bundles from the Relay Server. This function takes a Uuid indicating the last Bundle received by the Server, and an integer indicating the maximum number of Bundles that the Server will accept. A Uuid that is null or a Uuid that does not reference a known Bundle will cause the Relay Server to supply Bundles starting from the earliest that it holds. With a valid Uuid, the Relay Server returns a collection of Bundles (Collection<Relay.Component.Bundle>) that starts with the Bundle following the Bundle that matches the Uuid. In other words, the Uuid tells the Relay Server the last Bundle that the Server received.
The read()
call, like the write()
call, requires a Team Uuid and the Team
Secret.
The Team Server implements the client side of the Relay interface. This is done in class codeu.chat.server.RemoteRelay. The RemoteRelay class performs the serialization/deserialization of the Bundle objects and send/receives the bytes over the Relay Server connection.
The actual connection to the Relay Server is maintained through a ClientConnectionSource object (codeu.chat.util.connections.ClientConnectionSource). A ClientConnectionSource object is created with the host address and port number of the Relay Server. It is then passed into the RemoteRelay constructor, and the RemoteRelay object is passed to the Server’s Server object (codeu.chat.server.Server) through its constructor.
The Server class implements two methods that interact with the Relay Server (through the RemoteRelay and ClientConnectionSource objects):
Server.sendToRelay()
accepts the Uuids for a User, a Conversation, and a
Message. It creates the Bundle from the objects and performs the network call
to the Relay Server.
Server.syncWithRelay()
accepts an integer argument for the number of
Bundles to receive, and performs the network call to the Relay Server. It calls
onBundle(Bundle)
for each Bundle received from the Relay Server.
Server.onBundle()
unpackages the Bundle and adds the newly received objects
(a User, a Conversation, and a Message) to its internal database.
The front end of the Relay Server uses the same design as the CodeU Team
Server. In codeu.chat.RelayMain.main()
, a ServerConnectionSource is
instantiated with the server’s port number, and passed into startRelay()
.
The startRelay()
method creates a Server object
(codeu.chat.relay.Server, which implements Relay), then creates a
ServerFrontEnd object (codeu.chat.relay.ServerFrontEnd) with the new
Relay object. The ServerFrontEnd’s handleConnection()
method is
called in a new thread each time a new connect arrives. The
handleConnection()
method dispatches to handleReadmessage()
or
handleWriteMessage()
to handle a read or write from the client. They call
into the Server object (through its Relay interface) to apply the read
or write to the Relay Server’s database.
The Relay Server holds its received Message Bundles in a single
LinkedList called history. Each team’s server keeps track of
the messages it has received, and uses the Uuid of the last Bundle received to
set the starting point for a new Relay.read()
call, so the Relay Server does
not have to keep track of any per-server state information.