Getting Started v0.9.0

The Portal for Java is a server implementation of the Portal written in Java, JavaScript library for real-time web application development.


Installing

Portal for Java is distributed through Maven Central, only one artifact: io.github.flowersinthesand:portal:0.9.0 and is a server-side wes application so that it can run on any framework wes supports. See wes documentation for what frameworks are supported and how to install portal on them. The following example shows how to install a portal server in Java Servlet with the help of Atmosphere framework.

@WebListener
public class Bootstrap implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        // Create a portal server
        Server server = new DefaultServer();

        // Adds a socket action
        server.socketAction(new Action<Socket>() {
            @Override
            public void on(final Socket socket) {
                socket.on("echo", new Action<Object>() {
                    @Override
                    public void on(Object data) {
                        socket.send("echo", data);
                    }
                });
            }
        });

        // Install server by specifying path and attaching its wes actions to wes bridge
        new AtmosphereBridge(event.getServletContext(), "/test").httpAction(server.httpAction()).websocketAction(server.websocketAction());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {}

}

API

Let's take a brief look at API usage.

Server

Server provides and manages socket processing HTTP message and WebSocket.

Handling Socket

When a socket is opened, actions added via socketAction are executed with it. It's allowed to add several actions before and after installation, so you don't need to centralize all your code to one class.

server.socketAction(new Action<Socket>() {
    @Override
    public void on(Socket socket) {
        // Your logic here
    }
});

Selecting Sockets

It's a common use case to select some sockets and do something with them like dealing with persistence entity or HTML elements. When a socket has been closed, it is evicted from the server, so socket being passed to action is always in open state where I/O operations are available.

All

all executes the given action finding all of the socket in this server.

server.all(new Action<Socket>() {
    @Override
    public void on(Socket socket) {
        // Your logic here
    }
});
By Id

Every socket has a unique id. byId finds socket by id and executes the given action only once or not if no socket is found.

server.byId("59f3e826-3684-4e0e-813d-8394ac7fb7c0", new Action<Socket>() {
    @Override
    public void on(Socket socket) {
        // Your logic here
    }
});
By Tag

A socket may have several tags and a tag may have several sockets like many-to-many relationship. byTag find socket accepting one or more tag names and executes the given action. It is desirable to use tag when dealing with a specific concept in the real world (e.g. person and topic). Tag set is managed only by server and unknown to client.

server.byTag("room#201", new Action<Socket>() {
    @Override
    public void on(Socket socket) {
        // Your logic here
    }
});

Writing Sentence

Sentence is a series of predicates that a group of sockets have to follow. Finder methods return a sentence when being called without action. Use of sentence is preferred to that of action if the goal is same. Because, it enables to write one-liner action and uses an action implementing Serializable in execution, which is picky to use in anonymous class and typically needed in clustering.

server.all().send("foo", "bar");
server.byTag("room#201").send("message", "time to say goodbye").close();

Socket

Socket is a connectivity between the two portal endpoints where event occurs. Do not hold a reference on socket unless the reference shares the same life cycle of socket. It makes things complicated since it is stateful and also may result in a problem in clustered environment. Always create a socket action and pass it to server to access socket.

Properties

These are read only.

Id

A unique identifier in the form of UUID generated by client by default.

socket.id();
URI

A URI used to connect. To work with URI parts, use java.net.URI or something like that.

URI.create(socket.uri()).getQuery();
Tags

A set of tag names. It's modifiable, deal with it as a plain set.

Set<String> tags = socket.tags();
tags.add("account#flowersinthesand");

Receiving Events

on attaches an event handler. Allowed Java types for data corresponding to JSON types. As reserved event, when a socket has been closed, close event is fired without data.

socket.on("event", new Action<Map<String, Object>>() {
    @Override
    public void on(Map<String, Object> object) {
        // Your logic here
    }
});

In the case where client sends an event with callback like Remote Procedure Call, you can handle it using Reply in an asynchronous manner.

socket.on("account:findById", new Action<Reply<String>>() {
    @Override
    public void on(Reply<String> reply) {
        try {
            reply.done(Account.find.byId(reply.data()));
        } catch(EntityNotFoundException e) {
            reply.fail();
        }
    }
});

Sending Events

send sends an event with or without data. Unlike when receiving event, when sending event you can use any type of data and it will be stringified by Jackson. Don't rely on features of Jackson too much, i.e. annotation. There is no restriction on event name but to avoid confusion don't use connecting, waiting, open and close as event name, which are reserved event in client.

socket.send("event").send("event", "data");

Server also can send an event with callback to client as Remote Procedure Call. Allowed Java types are the same when action added via on receives data.

socket.send("event", "data", new Action<Object>() {
    @Override
    public void on(Object result) {
        // Your logic here
    }
});

Test Suite

Portal for Java provides a server for Portal test suite. It's available on Maven Central. This module's a goal is to provide a complete form of test suite in Java and it will be done as soon as wes starts to support client-side frameworks.

With that server, you can test your client implementation. It comes with test suite from the portal repository so you can see how Portal for Java passes the test suite in your browser. For how to start the server, see ServerBootstrap.


Examples

The followings are brief introduction of working example applications.

Platform

Portal for Java is an embeddable web application built on top of wes and it can run on as many platform as wes supports.

Clustering

ClusteredServer follows the publish and subscribe model to support clustering. All of the message oriented middleware supporting that model can be used to cluster multiple Portal for Java applications.

Dependency Injection

With dependency injection support by framework or container, you can make Server as component and inject it wherever you need to handle sockets cleanly without static keyword.

Data Type Conversion

The corresponding Java type of JSON object is Map<String, Object> that probably is the most used type to receive event data. If you want to convert Map to Java bean, there are many ways you can try. e.g. reflection and serialization.