# DRM Protocol version 1

## Introduction

This document describes only the lowest layer of the architecture. This means of course that compatibility at this level does not mean compatibility at higher levels, if the semantics of the data that is transmitted changes. The semantics is described in the API documentation of the java classes that are mentioned here. The API and this low level protocol have therefore a different versioning.

The protocol is based on java object serialization streams using the serialization stream format of JDKTM 1.2. This document does not define the object serialization protocol. The reader should refer to `http://java.sun.com/j2se/1.3/docs/guide/serialization/index.html.` Accordingly, in the description of the protocol java types will be used as primitives. These types should be written and read using the above mentioned java serialization streams through a socket connection. The objects in the stream must not be null except when it is explicitly allowed.

A session always starts with an initiation when the protocol versions understood by the peers and the group names are exchanged and the type of the session is identified by the initiator. This is followed by the actual data exchange. In the following the initiation will be described followed by the detailed description of the different types of sessions.

The following table contains definitions of terms used throughout the document.

Client | The initiator of the communication. Only temporary role, the same application can be server too.
Server | The passive party that accepts communication requests. Only temporary role, the same application can be client too.

This document contains strictly technical information, further documentation of the classes mentioned here can be found elsewhere. All constants are given in unsigned decimal format.

## Initiation

The connection starts with establishing a socket connection between the peers (initiated by the client). This means that an IP address and a port identifies a peer. The default port we use is 10101, but any other port is allowed. Typically if this number is not available the sequential ports can be tried, e.g. 10102, 10103, etc. The following table shows the communication steps.

Client | `int VERSION` | `String GROUP` | `byte TYPE` | -
Server | - | - | - | `int ANSWER`

For this version the `VERSION` value is 1. If `ANSWER` equals `GROUP_MISMATCH(=-1)` then the peer is from a different group and the communication will be closed. Otherwise `ANSWER` equals the protocol version spoken by the peer. The following table gives the values of the known session types:

`TYPE` | meaning
--- | ---
`0` | `MESSAGE (message sending)`
`1` | `AGENT (agent sending)`
`123` | `ISALIVE (are you alive?)`

## ISALIVE

Let's start with the simplest session, the ISALIVE request.

Client | `-`
Server | `java.lang.String BASE-NAME`

In words: the server simply replies by sending a String object. This string contains the name of the server.

## MESSAGE

When the message has no reply, the communication looks like this:

Client | Server
--- | ---
`String RECIPIENT` | `-`
`-` | `byte STATUS_1`
`drm.agentbase.Message MESSAGE` | `-`
`-` | `byte STATUS_2`

When the message has a reply, it looks like this:

Client | Server
--- | ---
`String RECIPIENT` | `-`
`-` | `byte STATUS_1`
`drm.agentbase.Message MESSAGE` | `-`
`-` | `byte STATUS_2=0`
`-` | `Object REPLY`

The interpretation of the status info is the following:

`STATUS_1` | meaning
--- | ---
`101` | `OK:` The recipient is here, send message.
`102` | `NOT_OK:` The recipient is not here, don't send message. The connection is interrupted by the server.

`STATUS_2` | meaning
--- | ---
`101` | `OK:` The message was delivered to the recipient successfully, and the recipient does not send a reply.
`102` | `NOT_OK:` There was a problem with delivering or handling the message.
`0` | `REPLY:` The message was handled and a reply is on the way.

`drm.agentbase.Message` is a class with serial version uid

    static final long serialVersionUID = 49323201182294530L;

Its serialized fields are

    drm.agentbase.Address sender;
    drm.agentbase.Address recipient;
    java.lang.String type;
    byte[] contbin;

The serial version of drm.agentbase.Address is

    static final long serialVersionUID = 2145788465305611890L;

Its serialized fields are

    java.net.InetAddress host
    int port
    java.lang.String name

It is also part of the low level protocol that the `host` field of the `sender` address in the message must be set to the client side IP of the socket after deserialization. The reason for this is that the sender entity is not required to know its own IP address. For this reason the sender IP must be determined on the recipient side.

## AGENT

The agent has its own class type that must implement `drm.agentbase.IAgent` and it may need several other classes to run. For the API of `drm.agentbase.IAgent` please refer to version 2 of the DRM (see http://sourceforge.net/projects/dr-ea-m/). The agent also belongs to a job that has a name. When loading the agent the server checks if it has the class files for running the agent using the job name. Thus the job name identifies the resources used by the agent.

Here we can have two scenarios. In the first case the class files needed to run the agent are already at the node. In the second case they are not. The first table shows the communication in the first case.

Client | Server
--- | ---
`java.lang.String JOBNAME` | `-`
`-` | `byte STATUS=101`
`drm.agentbase.IAgent AGENT` | `-`
`drm.agentbase.Address SENDER_BASE` | `-`
`-` | `byte STATUS`

`JOBNAME` is the name of the job the agent is a part of. It can be used by the server to check if the resources for the agent are accessible locally. This is why it is important that every job must have a unique name (at least with a high probability) that is suitable to identify the presence of specific optional classes.

`AGENT` can be of any type but it must implement the `drm.agentbase.IAgent` interface from DRM. The agentbase will communicate with the agent trough this interface.

Just like in the case of MESSAGE the IP address in the `addr` field of `SENDER-BASE` must be set to the client side IP of the socket by the server.

The second table shows the communication if the class files of the agent are not at the server.

Client | Server
--- | ---
`java.lang.String JOBNAME` | `-`
`-` | `byte STATUS=100`
`int SIZE` | `-`
`DATA` | `-`
`-` | `byte ACK`
`drm.agentbase.IAgent AGENT` | `-`
`drm.agentbase.Address SENDER_BASE` | `-`
`-` | `byte STATUS`

Everything is the same only here we also have an uploading phase inserted. If `SIZE` is positive than `DATA` contains a jar file of `SIZE` bytes. These bytes are simply written to the stream sequentially. If `SIZE` is negative then `DATA` is an object of type `java.util.File` describing an absolute directory name. The resources of the job must be in that directory on the server.

`ACK` can have any value. It indicates that the jar file has been read.

The interpretation of the closing status report of the server is the following:

`STATUS` | meaning
--- | ---
`101` | `OK. The agent was successfully installed.`
`102` | `NOT_OK. There was a problem with installing the agent.`

## Evolvability

Finally some notes on the evolvability of the protocol. Even though the possibility of extension of the protocol is not strictly part of the protocol definition (it is mostly an implementation issue), we would like to give some ideas.

The most problems come from the fact that we are dealing with mobile code (agents). There are two sides of this question: the DRM-agent and the agent-DRM interface. In other words the agent should be able to talk to the system and the way round. And the versions of the agent and the DRM may not match. (see `drm.agentbase.IAgent.version()` and `Base.RELEASE_VERSION` in the DRM release).

The system talks to the agent through the `drm.agentbase.IAgent` interface. If methods are deleted from it then the old system version will not be able to call these methods, and if methods are added then the new system version will miss them when talking to an old agent. (Changing a method signature is equivalent to an addition and a removal.) Removing methods is unlikely and also unnecessary (since they can be given an empty implementation). The case when we add methods can be solved by wrapping the lower version agent into an object that propagates the old method calls to the agent and gives default implementations to the new ones. When sending such an agent on of course only the original wrapped object should be sent.

The agent can use any publicly accessible part of the DRM. The problems come when an agent of a newer version goes to a base which implements an old DRM version. The agent will miss the methods that are present only in its version. There is no real solution to this beside the clear documentation of new methods and avoiding deleting old ones just like it is done in the standard java packages. Indicating that a field or method is present only from a given version will allow the developer of the agent to decide whether the agent should remain compatible. Another possibility is reflection, i.e. the agent can first check if a given method is implemented before calling it.

Of course the compatibility of the system classes w.r.t. serialization can also be maintained without great difficulties: if we only add new fields to the classes they remain compatible: this is guaranteed by the serialization stream specification.

Changes in the protocol can also be handled by later versions as the protocol itself is versioned too (see the initiation section).