DEV Community

loading...

WebSockets with ActiveJ

activej profile image valerialistratova ・3 min read

In this tutorial we'll create a basic client-server application with a WebSocket connection using ActiveJ framework.

You need to import the following dependencies:

<dependencies>
    <dependency>
        <groupId>io.activej</groupId>
        <artifactId>activej-launchers-http</artifactId>
        <version>3.0</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

The Logback dependency is optional.

In this tutorial we'll explicitly use the following ActiveJ technologies:

  • ActiveJ HTTP - high-performance asynchronous client and server implementations.
  • ActiveInject - lightweight and powerful dependency injection library.
  • ActiveJ Launcher - highly generalized main method implementation for managing application lifecycle.
  • ActiveJ Eventloop - provides efficient management of asynchronous operations without multithreading overhead.
  • ActiveJ Promise - high-performance and handy Java Future alternative.
  • ActiveJ Service Graph - designed to be used in combination with ActiveInject and ActiveJ Launcher as a means to start/stop application services according to their dependency graph.

WebSocket Pong Server

Let’s create a “Pong” WebSocket server. For this purpose we need to provide a RoutingServlet and use mapWebSocket method to map a Consumer of WebSocket as a servlet on /path. Our server will simply accept messages, print them out, and stream back a “Pong” message.

public final class WebSocketPongServerExample extends HttpServerLauncher {

    @Provides
    AsyncServlet servlet() {
        return RoutingServlet.create()
                .mapWebSocket("/", webSocket -> webSocket.readMessage()
                        .whenResult(message -> System.out.println("Received:" + message.getText()))
                        .then(() -> webSocket.writeMessage(Message.text("Pong")))
                        .whenComplete(webSocket::close));
    }

    public static void main(String[] args) throws Exception {
        WebSocketPongServerExample launcher = new WebSocketPongServerExample();
        launcher.launch(args);
    }
}
Enter fullscreen mode Exit fullscreen mode

WebSocket Ping Client

Now let’s create a client that will send a “Ping” message to server via WebSocket connection.

public final class WebSocketPingClientExample extends Launcher {
    @Inject
    AsyncHttpClient httpClient;

    @Inject
    Eventloop eventloop;

    @Provides
    Eventloop eventloop() {
        return Eventloop.create();
    }

    @Provides
    AsyncHttpClient client(Eventloop eventloop) {
        return AsyncHttpClient.create(eventloop);
    }

    @Override
    protected Module getModule() {
        return ServiceGraphModule.create();
    }

    @Override
    protected void run() throws ExecutionException, InterruptedException {
        String url = args.length != 0 ? args[0] : "ws://127.0.0.1:8080/";
        System.out.println("\nWeb Socket request: " + url);
        CompletableFuture<?> future = eventloop.submit(() -> {
            System.out.println("Sending: Ping");
            return httpClient.webSocketRequest(HttpRequest.get(url))
                    .then(webSocket -> webSocket.writeMessage(Message.text("Ping"))
                            .then(webSocket::readMessage)
                            .whenResult(message -> System.out.println("Received: " + message.getText()))
                            .whenComplete(webSocket::close));
        });
        future.get();
    }

    public static void main(String[] args) throws Exception {
        WebSocketPingClientExample example = new WebSocketPingClientExample();
        example.launch(args);
    }
}
Enter fullscreen mode Exit fullscreen mode

First, we provide the required dependencies: AsyncHttpClient and an Eventloop for it.
Next, we need to override Launcher's getModule and run methods. getModule supplies application with basic business logic module, while run represents the main Launcher method.
In run method we create a supplier and override its get method via lambda. Here we call AsyncHttpClient.webSocketRequest that sends a request and returns a Promise of a WebSocket. Then we create a Function that sends a “Ping” message and receives a response from server.

And that's it, your WebSocket application is ready to be used. First, launch the server implementation, and then client.
You can find the source code of the examples on GitHub.

Discussion

pic
Editor guide