Web Development

Creating High-Performance Web Applications with ActiveJ

In this article, we will create several web applications using a single full-stack ActiveJ framework.

1. Why using ActiveJ?

Most of the Java frameworks have common drawbacks:

  • Excessive layers of abstractions that hide the legacy staff
  • To much overhead, and thus lack of performance and flexibility
  • Framework specifications are elevated over business logic 

ActiveJ’s main concept is to overcome these drawbacks. For this purpose the framework was built from the ground up without third-party dependencies. This made the technology flexible, lightweight, and extremely concise. Now it’s your turn to test it out!

ActiveJ has a lot of components: from async core to high-level abstractions for developing distributed storage. But in this tutorial we will only consider the core components of the framework, particularly HTTP and ActiveInject.

2. What is ActiveJ HTTP and ActiveInject?

ActiveJ HTTP component provides high-performance HTTP clients, servers and servlets. This component is fully asynchronous and has minimum overhead. Moreover, it is flexible and suitable for developing both microservices architecture and complex web applications. In some microservices scenarios ActiveJ is 30% faster than highly-specialized multi-threaded Vert.x even on a single core and with 50% less overall CPU load. You can find benchmark sources here.

Yet, if your microservices architecture needs even more performance to process dozens of millions of requests per second, you should check out the ActiveJ RPC component.

ActiveInject is a lightweight and powerful DI library. It has a wide range of features: support of nested scopes, singletons and transient bindings, modules, optimized multi-threaded and single-threaded Injectors, etc. At the same time, ActiveInject is 6 times faster than Guice and hundreds of times faster than Spring DI. 

3. Creating single/multi-threaded web applications

Let’s start with a simple “Hello World” HTTP server example. First, you’ll need to import a Maven ActiveJ component to your project:

<dependencies>
    <dependency>
      <groupId>io.activej</groupId>
      <artifactId>activej-launchers-http</artifactId>
      <version>2.2</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>
  </dependencies>

logback-classic dependency is optional. activej-launchers-http includes pre-defined launchers for HTTP servers. They take care of application lifecycle, dependencies and logging. The activej-launchers-http component contains an HttpServerLauncher class which we’ll use as a superclass of our sample server. This dependency will automatically import HTTP component and ActiveInject to your project as well.

Now the whole code of your server will look as follows:

public final class HttpHelloWorldExample extends HttpServerLauncher {
    @Provides
    AsyncServlet servlet() {
        return request -> HttpResponse.ok200().withPlainText("Hello World");
    }

    public static void main(String[] args) throws Exception {
        Launcher launcher = new HttpHelloWorldExample();
        launcher.launch(args);
    }
}

That’s it,  no additional configurations are required. Extending the HttpServerLauncher allows us to use ActiveInject to specify the root HTTP listener AsyncServlet via @Provides annotation. The launch method of the superclass takes care of managing the app’s lifecycle. That’s how simply you create a fully-functioning HTTP server that is ready for highload scenarios out of the box.

This and all the subsequent examples can be tested by running the main method and going to http://localhost:8080.

Another predefined HTTP launcher is named MultithreadedHttpServerLauncher. It allows us to simply create multi-threaded servers. Similarly to the previous example, we only need to extend the launcher and provide a servlet:

public final class MultithreadedHttpServerExample extends MultithreadedHttpServerLauncher {
  @Provide
  @Worker
  AsyncServlet servlet(@WorkerId int workerId) {
     return request -> HttpResponse.ok200()
           .withPlainText("Hello from worker server #" + workerId + "\n");
  }

  public static void main(String[] args) throws Exception {
     MultithreadedHttpServerExample example = new MultithreadedHttpServerExample();
     example.launch(args);
  }
}

Now, your server will automatically distribute requests between threads. Note the @Worker annotation. It defines the components you wish to put into a separate worker thread. By default, MultithreadedHttpServerLauncher creates 4 workers. Yet, you can use this launcher as a reference and extend or modify it according to your business logic needs.

4. More specific use cases

In both examples we used a basic AsyncServlet class for setting up servlets. But ActiveJ HTTP has other more specific solutions, for example:

Let’s test out some of them. 

RoutingServlet allows to easily create trees of servlets to route requests between them by the HTTP paths:

RoutingServletExample extends HttpServerLauncher {
@Provides
 AsyncServlet servlet() {
     return RoutingServlet.create()
           .map(GET, "/", request ->
                 HttpResponse.ok200()
                       .withHtml("<h1>Go to some pages</h1>" +
                             "<a href=\"/path1\"> Path 1 </a><br>" +
                             "<a href=\"/path2\"> Path 2 </a><br>"))
           .map(GET, "/path1", request ->
                 HttpResponse.ok200()
                       .withHtml("<h1>Hello from Path 1!</h1>" +
                             "<a href=\"/\">Go home</a>"))
           .map("/*", request ->
                 HttpResponse.ofCode(404)
                       .withHtml("<h1>404</h1><p>Path '" + request.getRelativePath() + "' not found</p>" +
                         "<a href=\"/\">Go home</a>"));
  }

  public static void main(String[] args) throws Exception {
     Launcher launcher = new RoutingServletExample();
     launcher.launch(args);
  }
}

We use method map to add a route to the RoutingServlet and set up the following parameters:

  • method (optional) – one of the HTTP methods (GET, POST, etc)
  • path – the path on the server
  • servlet – defines the logic of request processing.

Another AsyncServlet implementation is StaticServlet. It allows us to use static content from a predefined source, for example, local filestorage:

public final class StaticServletExample extends HttpServerLauncher {
  @Provides
  Executor executor() {
     return newSingleThreadExecutor();
  }

  @Provides
  AsyncServlet servlet(Executor executor) {
     return StaticServlet.ofClassPath(executor, "static/site")
           .withIndexHtml();
  }

  public static void main(String[] args) throws Exception {
     Launcher launcher = new StaticServletExample();
     launcher.launch(args);
  }
}

You can place any HTML code in your index.html file in the static/site directory and it will be used as a homepage when you launch the example.

ActiveJ HTTP component has support for WebSocket connection. Just like in the previous examples, the implementation is pretty straightforward, you need to extend HttpServerLauncher and provide an AsyncServlet:

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

mapWebSocket method maps a given Consumer of a WebSocket as a WebSocketServlet on path '/'. You can find an example of ActiveJ WebSocket client implementation here.

5. Summing up

We’ve covered only a tiny scope of ActiveJ features yet they show the streamlined programming approach of this framework. ActiveJ has much more to offer: lightning-fast bytecode generation, serialization, optimizational tools, RPC implementation, and others.

Basically, you can create almost any application using a single high-performance and holistic framework. 

Valeria Listratova

Valeria is a content manager at Active LLC who started her career as a Java developer. She has been working on ActiveJ project for more than 2 years, participating in the evolution of the platform and creation of ground-breaking technologies. Now she’s eager to share the results of the team’s work with the community.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
ruurd
ruurd
3 years ago

You win some, you lose some.

Back to top button