The Portal for Java is a server implementation of the Portal written in Java, JavaScript library for real-time web application development.
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) {}
}
Let's take a brief look at API usage.
Server provides and manages socket processing HTTP message and WebSocket.
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
}
});
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
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
}
});
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
}
});
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
}
});
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 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.
These are read only.
A unique identifier in the form of UUID generated by client by default.
socket.id();
A URI used to connect. To work with URI parts, use java.net.URI
or something like that.
URI.create(socket.uri()).getQuery();
A set of tag names. It's modifiable, deal with it as a plain set.
Set<String> tags = socket.tags();
tags.add("account#flowersinthesand");
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();
}
}
});
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
}
});
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.
The followings are brief introduction of working example applications.
Portal for Java is an embeddable web application built on top of wes and it can run on as many platform as wes supports.
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.
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.
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.