Quick start
A quick guide to getting the project set up and running a project.
The Development Environment
The minimum JVM requirement for the library is Java 8.
Additionally, for the Native Library option a minimum of Linux kernel 3.9 is required.
Using Maven
To include the library in a maven project a similar entry in the pom.xml should be used.
<dependency>
<groupId>rubris</groupId>
<artifactId>rubris-io</artifactId>
<version>${rubris-io.version}</version>
</dependency>
Instantiating the Server
The Server class is a simple Class intended to be embedded into your application and is used to register your own Endpoints and supply configuration options to the library:
// List of My handlers List<Object> userHandlers = new ArrayList<>(); .... // create the server Server s = new Server(); // add the user handlers s.setUserHandlers(userHandlers); // initialise and start the server component s.init(); // ensure it does not exit CountDownLatch l = new CountDownLatch(1); l.await(); // when we are interrupted close the server to free up resources s.close();
This is not much use as we have not controlled what socket configuration it uses, what the paths will be and any handlers to receive messages.
We have 2 choices:
- We can override the default config an attribute at a time (simpler for small changes). e.g:
Server s = new Server();
s.getConfig().getSocketConfig().setListenPort(port);
- or we can replace the entire socketConfig Object (or any of the other config entries see Configuration Guide). e.g:
SocketConfig socketConfig = new SocketConfig(); socketConfig.setBindAddress("127.0.0.1"); socketConfig.setListenPort(8099); ModuleConfig config = new ModuleConfig(); config.setSocketConfig(socketConfig) Server s = new Server(); s.setConfig(config)
Set up the bind address and listening port
The listening port defines what port the app will accept connections on. The HTTP and WS port is the same in Rubris-IO and it is the protocol request type that determines which is used. This enables functionality such as upgrading an existing HTTP connection to WS without needing to reconnect the socket.
Server s = new Server(); ModuleConfig config = s.getConfig(); config.getSocketConfig().setListenPort(8091); config.getSocketConfig().setBindAddress("127.0.0.1");
The bind address works in the same way that one normally sets the bind address in Java.
The Service Path
Server s = new Server(); ModuleConfig config = s.getConfig(); config.getSocketConfig().setBindAddress("127.0.0.1"); config.getSocketConfig().setListenPort(8091); ((HttpConfig) config.getTransportConfig(ModuleConfig.HTTP)) .setServicePath("mypath");
This will result in a url which forms the base of the protocol path as:
http://127.0.0.1:8091/mypath
The service path is the base url that the server uses to identify the root of all the Engine-IO requests used for polling and messaging. All the requests starting with this url (controlled on the client by the Engine-IO library) are assumed to either be a POST of messaging data or a GET that acts as a poll for outstanding data.
URLs that are different from this are classified as direct url requests. Direct requests are handled outside the Rubris message processing pipeline and are handled as raw http requests (although the responses are still asynchronous - see the Handler’s guide.
Adding handlers
Adding handlers is covered in more depth in the Handler’s guide. However, a simple set up for a request response handler would be:
@Endpoint(name = "meta") class MetaHandler implements AsynchronousHandler { @Override public void onMessage(User user, Object message, DataHandle handle) { LOGGER.debug("Meta Message received " + message); handle.onMessage(s + " response META"); } } ... List<Object> userHandlers = new ArrayList<>(); userHandlers.add(new MetaHandler()); .... s.setUserHandlers(userHandlers);
This will create an RPC channel called meta
which all users can send messages to.
Each defined handler represents a Channel.
A Channel can be either a Request/Reply Channel, A Subscription channel, or a Private Subscription channel. The different types of channels are entirely controlled by the config. There is no special naming convention and the developer chooses how these manifest.
Sample Launcher
A simple launcher class would then look like: See the ServiceLaucnher in the test package as a sample.
public class ServerLauncher { public static void main(String[] args) throws Exception { ServerLauncher launcher = new ServerLauncher(); int port = 0; if (args.length > 0) { port = Integer.parseInt(args[0]); } String path = "meta"; if (args.length > 1) { path = args[1]; } launcher.run(port, path); } public void run(int port, String path) throws Exception { Server s = new Server(); List<Object> userHandlers = new ArrayList<>(); createAPI(userHandlers); s.setUserHandlers(userHandlers); s.getConfig().getSocketConfig().setListenPort(port); ((HttpConfig)s.getConfig().getTransportConfig(ModuleConfig.HTTP)) .setServicePath(path); s.init(); CountDownLatch l = new CountDownLatch(1); l.await(); s.close(); } protected void createAPI(List<Object> userHandlers) { userHandlers.add(new MetaHandler()); } @Endpoint(name = "meta") static class MetaHandler implements AsynchronousHandler { @Override public void onMessage(User user, Object message, DataHandle handle) { String s = null; if (message instanceof byte[]) { s = new String((byte[]) message); } LOGGER.debug("Meta Message received " + message); handle.onMessage(s + " response META"); } } }
The Client
For the client side we know want to communicate with our server.
The Client libraries
<script src="/path/js/rubris-io.js"></script>
<script src="/path/js/engine-io/1.6.8/engine.io.js"></script>
For IE11 you will need to use the TextEncoder polyfill as it is not supported natively:
<script src="/path/js/text-encoding/encoding.js"></script>
Initialising
Multiple instances of the rubris object can be held on a page or on different tabs and are constructed like so:
var rubris = new Rubris({});
Callback APIs
Rather than wrapping EngineIO, the Rubris library plugs itself into the EngineIO library to try and provide a small amount of intrusion into the natural style of using EngineIO.
/ create engine socket as normal socket = eio.Socket({ path : mypath , port: 8091, protocol: "http", host: 127.0.0.1 }); socket.binaryType = 'arraybuffer'; // on socket open set socket on rubris to enbale use of socket event emitter socket.once('open', function(){ rubris.setSocket(socket) // make the initial channels call and set up the library // this must be done first as all messages require a channelid rubris.initChannels(); .... }
Once the root object is initialise then we can register callback functions to receive the response data:
IMPORTANT: the “message” callback MUST be set in order for Rubris to parse the response messages.
// The engineIO callback for messages.
// Register Rubris library to parse or wrap with own function
socket.on('message', function(data){
rubris.parseMessage(data);
});
// Following callback functions will be called with above message registration
// callback resulting from init message with the all channels and status
socket.on('channels', function(data){
....
});
// RPC responses
socket.on('rpc', function(data){
...
});
As Engine-io is asynchronous it is only after the channels message has been processed that messages can be sent (as you require the channelId to create a message. Therefore all RPC and subscribe message can only be created after the on(“channels”…) callback has been called.
Sending data
Sending data utilises the engineIO socket library with a few methods provided to generate the appropriate message formats for the Rubris protocol itself
The general format is:
ChannelId is the numeric id of the channel you want to send on as returned from the channels callback.
MessageId is the sequence number of the message.
socket.send(rubris.rpcMessage($PAYLOAD, channelId, messageid));
To handle the message for example we extract the payload
function displayRPC(message){
// get the length of the payload
var length = message.payload.end - message.payload.start;
// get the payload unit8 array as a string and display
$('#messageId').text( message.id+ ': time'+
((new Date).getTime()-message.timestamp)+" "
+rubris.getPayloadAsText(message.payload));
}
Response message format
The response messages in the callback are defined in the following prototype:
function Message(type, timestamp, channel, id, status, payload) {
this.type = type;
this.timestamp = timestamp;
this.id = id;
this.channel = channel;
this.status = status;
this.payload = payload;
};
The payload object is:
function Payload(data, type, start, end) {
this.data = data;
this.start = start;
this.end = end;
this.type = type;
}
See the ClientGuide for further details.