Getting started

Access to sources and documentation can be found here in the repository of the MSRP project. Build versions are stored in the Maven central repository and can be included in any Maven build by specifying the dependency in a pom.xml . Check the website to find the latest version or search for “msrp” on the central repository site:

...
<dependencies>
  <dependency>
    <artifactId>msrp</artifactId>
    <groupId>net.java.msrp</groupId>
    <version>1.3.0</version>
  </dependency>
</dependencies>
...

Introduction

Here is a brief introduction into the MSRP protocol and how you can use this Java library to incorporate that protocol into your own programs.

MSRP (or Message Session Relay Protocol) is an instant messaging or chat protocol, defined by the IETF in RFC4975. Basically, (instant-) messages are sent to and from MSRP peers, using SEND and REPORT requests. These messages are sent over an MSRP session. One or more MSRP sessions can share a TCP/IP-link from one node [AKA peer] to another (this can be achieved by any transport protocol, provided it is connection-oriented and reliable, in the sense of not changing the content. MSRP can be used with protocols that provide no assurance on data delivery, as MSRP has its own mechanisms to ensure that the data has been delivered. Here, only TCP/IP is used).

The protocol is text based, much like HTTP and SIP. Using chunking and MIME-conforming message content (payload), thus, MSRP messages can transfer any content of any size, supporting -for instance- file transfer.

Establishing a session

To establish the afore-mentioned sessions between peers, which form the basis of communication in the MSRP protocol, the MSRP session must be negotiated by some 'rendezvous method', external to the MSRP protocol itself (e.g. SIP + SDP). An example of such a negotiation can be seen in MSRP's RFC, on section 4, and a more detailed explanation on how to conduct such a negotiation in section 8. For more details on SDP's offer-answer mode, click here.

The end result of the negotiation are the MSRP URI's, that define the endpoints of the session. Such a URI contains a scheme, authority (domain, port), session identifier, transport- and other parameters.

You can now use the library to establish a session, like this:

import java.net.InetAddress;
import javax.net.msrp.Session;
import java.net.URI;

InetAddress myAddress = InetAddress.getByName("myHostnameThatDoesMsrp");
Session activeSession = Session.create(false, false, myAddress)
URI myMsrpUri = activeSession.getURI();

You now have a session with a local MSRP URI, which has to be transmitted to the other peer through the rendezvous mechanism. This session is an active session. It means that as soon as you feed it the target URI, which should be transmitted to you using the rendezvous protocol, a connection will be established (if not already present, as MSRP allows connection re-utilization), and a first request is immediately sent (to bind the session):

import java.util.ArrayList;

ArrayList toList = new ArrayList();
URI targetURI = new URI('TheNegotiatedTargetNodeMsrpUri');
toList.add(targetURI);
activeSession.setToPath(toList);

The target can be a list of URI's to support relaying

At the other end, there is a passive session, one that knows what node will contact it. A network socket is put in place to listen for the connect-request.

URI hisMsrpUri = <MSRP URI negotiated with active node...>;
Session passiveSession = Session.create(false, false, hisMsrpUri, myAddress);

The booleans isSecure and isRelay are not implemented yet.

Sending and receiving

Using a session, one is now able to send and receive messages using MSRP. Sending is easy, using the sendMessage() methods:

import javax.net.msrp.Message;

Message sendMsg = activeSession.sendMessage("text/plain", "Hello world\n".getBytes());

The message will be sent as soon as a connection is established.

Receiving a message is somewhat more involved and requires the creation of a SessionListener that can be hooked into the session:

mySession.setListener(myListener);

This listener will be triggered when messages are received, or status updates are relevant. The first method to be called on receiving a message is acceptHook(). This tells your application that an incoming message has been received. Two things should be done here: A DataContainer should be attached to the message-object to be able to store any content, and the application should decide whether you want this message in the first place. If so, one should return true, otherwise return false. In that case, an error-response is automatically returned.

import javax.net.msrp.SessionListener;
import javax.net.msrp.IncomingMessage;
import javax.net.msrp.MemoryDataContainer;

public class MyListener implements SessionListener {
  public boolean acceptHook(Session session, IncomingMessage message) {
    MemoryDataContainer mdc = new MemoryDataContainer((int) message.getSize());
    message.setDataContainer(mdc);
    return true;
  }
}
						

When accepted, the receivedMessage() callback is triggered, containing the entire message and content.

public void receivedMessage(Session session, IncomingMessage message) {
  System.out.print(message.getContent());
}						

The above code would make sense to any message that had text-content. The above call also unwraps any "message/cpim" content that might have been sent.

For sending large messages and more efficient use of existing connections, MSRP defines a chunking mechanism that allows messages to be sent in multiple chunks.
A chunk size can be set in the session you just set up, as in:

mySession.setChunkSize(20480);

This will avtivate the chunking of all outgoing data messages into the specified size.
The default value of 0 will deactivate chunking.

Reports

In the same listener interface, trigger receivedReport() is defined to handle any received reports as a result of send-actions:

import javax.net.msrp.ResponseCode;
...

  public void receivedReport(Session session, Transaction report) {
    int rc = report.getStatusHeader().getStatusCode();
    if (rc == ResponseCode.RC200)
      System.out.println("Success report");
    else
      System.out.println("Failure report: " + ResponseCode.toString(rc));
}	

If you want to more thoroughly control the reporting mechanism, implement the abstract class ReportMechanism and install it in the session:

  import javax.net.mypackage.MyReportMechanism;

  MyReportMechanism mrm = new MyReportMechanism(<whatever>);
  mySession.setReportMechanism(mrm);
Refer to Javadoc for more details on how this should work.

Wrapping messages

The library supports a message-wrapping mechanism. By default, a wrapping mechanism for message/CPIM is installed. Thus message.getContent(); will retrieve the wrapped content of the message, whereas message.getRawContent(); will retrieve the complete content, including the wrapping.

Likewise, there is a

Session.sendWrappedMessage(<wrapType>, <from>, <to>, <contentType>, <content>);

that will activate the wrapType wrapper mechanism for wrapping the given content in a message to send.
Currently, the wrapType message/cpim is supported.

Rolling your own wrapper mechanism and installing can be done with

javax.net.msrp.wrap.Wrap.getInstance().registerWrapper(<contentType, <wrapImplementor>);

Refer to the source code for more details.

Nickname support

Support for the NICKNAME request as specified in draft-ietf-simple-chat is built in. See:

Session.requestNickname();
SessionListener.receivedNickNameResult();

and

SessionListener.receivedNickname();
Session.sendNickResult();

Indication of message composition

Within the MSRP message stream, it is possible to send a status to the other chatter, indicating that a message is actively being composed. This is specified in RFC3994. Indicating that you are in the middle of composing a message and what type you are actually composing can be done by calling:

Session.setActive(contentType);

and the counterpart:

Session.setIdle();

The library will send a status message containing the application/im-iscomposing+xml document over the session. Beware that it will do this only once within a refresh period.

To accomodate connections to a conference (chatroom), these calls are also available with a from and to argument. When given, the status message is automatically wrapped using message/CPIM so that participant information can be retained:

Session.setActive(contenttype, from to);
Session.setIdle(from, to);

A received status message can be detected by testing for:

if (message instanceof StatusMessage)

or

if (message instanceof IncomingStatusMessage)

when true, further information on the composing can be obtained through:

StatusMessage.getState();
StatusMessage.getComposeContentType();

etc.
Likewise, any incoming messages are auto-scanned for wrapped status indications. When appropriate, the from and to fields will be filled with the received information.

Keep alive messages

An empty MSRP SEND message is defined as a possible keepalive mechanism to support any NAT mechanisms or others.
Empty, meaning having no content-type and containing no data. In the library, this is supported by 2 seperate message types:

IncomingAliveMessage
OutgoingAliveMessage

and the session method:

Session.sendAliveMessage();

An incoming bodiless SEND will be passed to the sessionlistener, the acceptHook() however, will not be called.

Log integration

This library uses the SLF4J logging facade for logging messages. Simply put the slf4j log binding of your choice in your class path to immediately have the library use that logging system.
When building and testing the library from source, it by default uses the slf4j-log4j12.jar binding.

Use the settings in src/test/resources/log4j.properties to change settings like log output, format, log-level, etc.