Skip to main content

Building a GraalVM native image of an ActiveJ HTTP Server

In this tutorial we will build the HTTP Server from the getting started tutorial as a GraalVM native image.

Download and run the server

First, clone ActiveJ locally:

git clone -b examples-6.0-beta2 https://github.com/activej/activej.git

Then open the project in your IDE of choice. Before running the example, build the project (Ctrl + F9 for IntelliJ IDEA).

Navigate to a HelloWorldHttpServer class, which is located at activej/examples/tutorials/native-image and run its main method. Open your favourite browser and go to localhost:8080

You can see that it is a simple "Hello world" web server:

public class HelloWorldHttpServer extends HttpServerLauncher {

@Provides
AsyncServlet servlet() {
return request -> HttpResponse.ok200()
.withPlainText("Hello, world!")
.toPromise();
}

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

Actually, the server is the same as in a getting started tutorial. To build it as a native image we would need a few more things.

Native image prerequisites

First things first, you need to have GraalVM and native-image tool installed as described here.

Maven plugin configuration

We will use the native image maven plugin to simplify build process.

For that we will add the native image plugin to a custom native profile of our pom.xml :

<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.17</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>build</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<mainClass>HelloWorldHttpServer</mainClass>
<imageName>hello-world-server</imageName>
<buildArgs>
<buildArg>--no-fallback</buildArg>
<buildArg>--allow-incomplete-classpath</buildArg>
<buildArg>-H:ReflectionConfigurationFiles=${project.basedir}/config/reflectionconfig.json
</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

You can read more about plugin configuration at the official plugin documentation page

Although we use a plugin to simplify a build process, a native-image tool can be used directly if needed.

Reflection configuration

In order to allow reflective operations (required to process ActiveJ Inject annotations) we have to define a reflection configuration. We did this in a reflectionconfig.json file inside the config directory. A file describes a few classes with a required reflection access for fields and methods:

[
{
"name": "HelloWorldHttpServer",
"allDeclaredMethods": true
},
{
"name": "io.activej.launchers.http.HttpServerLauncher",
"allDeclaredFields": true,
"allDeclaredMethods": true
},
{
"name": "io.activej.service.ServiceGraphModule",
"allDeclaredMethods": true
}
]

Building a native image

To build a native image execute mvn -Pnative package command from within the 'native-image' module directory.

Running a native image

To run a native image execute ./target/hello-world-server command from within 'native-image' module directory.

You should see that the server is launched and that it is listening on port 8080. Open your favourite browser and go to localhost:8080.