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.