Flowable + Spring Boot in a GraalVM Native Image

Introduction

The GraalVM project has been something we’ve kept a close eye on since its very beginning. It was two years ago that we did two Youtube videos on running Flowable “serverless” and building a native image with GraalVM. It was very early days at that time – the first real production GraalVM release followed a couple of months later.

In a nutshell: GraalVM is a JDK from Oracle that has some really interesting technology, with Community and Enterprise editions. Faster runtime on certain workloads, interesting ways of doing polyglot programming and, the focus of this article: compiling a Java application to a native image. Such a native image is an optimized binary that contains everything to run the Java application on a specific environment – it’s compiled towards a specific system to run natively. It contains the minimal set of classes, which have been computed at build time, and starts up very fast as it doesn’t suffer the typical slowness of initializing a regular JDK.

This becomes very significant when a) services need to be booted up very quickly and b) you pay for resource consumption. As one of the core Flowable architectural principles is to be lightweight, this capability fits us like a glove.

In the previous work we did, the following points remained open:

  • We were running without persistence, using an in-memory implementation of the Flowable persistence interfaces – getting JDBC to work at that time was simply impossible.
  • We compiled the BPMN 2.0 model to a Java representation using ahead-of-time-compilation (AOT) to skip the XML parsing and deployment phase. This is still a valuable thing to do for specific services that need models to be baked in out-of-the-box. However, we never got the XML models working on GraalVM and for general use cases this is a must.
  • We were able to use Spring Fu to build a native image, but had no luck with Spring Boot. This was acknowledged by the Spring Boot team back in the day. Truth be told, the amount of work to make Spring Boot compatible with GraalVM was a massive undertaking.

So, you can imagine our excitement when we saw that Spring recently announced Spring Native Beta. Reading the documentation and strolling through the source code of Spring Native convinced us this was a major and very interesting piece of engineering from the Spring Boot team.

And the respectful response to it was to make sure that Flowable could run in this native environment. So that’s exactly what we did.

Let’s buckle up for a story about a bumpy, yet technically interesting, ride!

The Path to a Native Flowable

The goal of what we wanted to achieve:

  • Deploy a simple BPMN 2.0 process model with one user task
  • Start a process instance every second
  • Print out the amount of tasks
  • All of this with a persistent database

In this section, we’ll look at the various steps we needed to do to make it work. All these are very technical and quite dry to read. However, we spent many (many!) hours to understand all the various bits involved and how the GraalVM build system actually works … so writing it down here is also a kind of therapy for us in some way 😉

If the details don’t matter to you, you can find the end-result of it all here:

https://github.com/flowable/flowable-examples/tree/master/blog/04-2021-flowable-spring-boot-native

First of all, we started by following the Spring Native documentation to set up a basic Maven project. Picking Postgresql as the database (because that driver works on GraalVM) and including Flowable is a matter of adding the following dependencies:

<dependency>
   <groupId>org.postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <scope>runtime</scope>
</dependency>

<dependency>
   <groupId>org.flowable</groupId>
   <artifactId>flowable-spring-boot-starter-process</artifactId>
   <version>6.6.0</version>
</dependency>

The Spring Boot application is quite standard, using the Flowable process engine and services to deploy and start process instances:

@SpringBootApplication
public class DemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }

   @Bean
   public CommandLineRunner commandLineRunner(ProcessEngine processEngine) {
      return args -> {
         processEngine.getRepositoryService().createDeployment()
            .disableSchemaValidation()
            .addClasspathResource("org/flowable/test.bpmn20.xml")
            .deploy();

         while (true) {
            processEngine.getRuntimeService().startProcessInstanceByKey("oneTaskProcess");
            System.out.println("Number of tasks: " 
                + processEngine.getTaskService().createTaskQuery().count());
            Thread.sleep(1000);
         }
      };
   }

}

When building a native image, GraalVM will introspect the source code and determine which classes need to be included in the image. Of course, as many Java libraries use reflection, many classes will be missed. Those classes need to be added to a GraalVM configuration file, which means that there’s a lot of trial-and error that goes into determining which classes to include.

Spring Native has a neat way of generating this configuration file based on annotations. It uses a Maven plugin that inspects the annotations and generates the configuration files at build time. We spent quite a few hours trying to make this work, using classes in a new Maven module that look like this (simplified here):

@NativeHint(
    trigger = ProcessEngine.class,
    types = {
        @TypeHint(types = AppEngineServicesAutoConfiguration.class, access = AccessBits.ALL),
    },
    initialization = {
        @InitializationHint(
            types = {
                org.flowable.spring.boot.app.AppEngineServicesAutoConfiguration.class
            }
            , initTime = InitializationTime.BUILD)
    },
    resources = {
        @ResourceHint(patterns = "org/flowable/db/*")
    }
)
public class FlowableNativeHints implements NativeConfiguration {

    public FlowableNativeHints() {
    }

}

However, we couldn’t make this approach work for us and resorted to configuring the JSON files ourselves. We will look into this again in the near future, as having a separate module to depend upon is much easier for custom projects than having to add configuration by hand.

The first hurdle was the following exception:

Parsing context:
parsing org.graalvm.polyglot.Engine.getVersion(Engine.java:203)
parsing com.oracle.truffle.js.scriptengine.GraalJSEngineFactory.getEngineVersion(GraalJSEngineFactory.java:132)
parsing com.oracle.truffle.js.scriptengine.GraalJSEngineFactory.getParameter(GraalJSEngineFactory.java:168)

Luckily, we found a solution to that on Github. Adding the following to the GraalVM native image Maven plugin did the trick:

--features=org.graalvm.home.HomeFinderFeature

It’s hard to describe the type and amount of trial and error that happened at this point. Basically, every time we built a new native image we would see the next class that would fail, typically due to reflection references. Every build would take a few minutes … which adds up quickly. After a few iterations of doing this and adding Flowable classes manually to that file, like this:

{
"name" : "org.flowable.spring.boot.app.AppEngineServicesAutoConfiguration",
"allPublicMethods" : true,
"allDeclaredConstructors" : true
}

… we decided to do what every developer would do at that point: automate it. We wrote a few lines of code that went through all Flowable packages and generated the JSON config urationfor all Flowable classes. Yes, this would include potentially unused classes in the image, but it certainly got us far quicker to what we wanted to achieve. We did remove the classes that had unresolvable references (for example, Junit, Apache Http). In the future, we do want to optimize this of course.

From this, we got a configuration file looking like:

https://github.com/flowable/flowable-examples/blob/master/blog/04-2021-flowable-spring-boot-native/src/main/resources/META-INF/native-image/reflect-config.json

At this point, we were out of “ClassNotFoundException-Land”. Running the native image now failed due to missing resource files that Flowable uses. This was easily fixed by including them in the native image with a custom configuration file for GraalVM:

https://github.com/flowable/flowable-examples/blob/master/blog/04-2021-flowable-spring-boot-native/src/main/resources/META-INF/native-image/resource-config.json

We did similar work for MyBatis and added the relevant classes to the reflect-config.json file.

The next failure was that Liquibase, which we use for database schema management, was failing. To circumvent this until this works on GraalVM natively, we created a schema beforehand in PostgreSQL and configured the Spring Boot application to not create any schema automatically. We also had to disable XSD validation at deploy time as we couldn’t yet get that working on GraalVM (although according to some Github issues it may now work on the current GraalVM release).

Lastly, we also had to add a few extra getters and setters to the source code on the Flowable master branch, because MyBatis will actually fall back to reflection if you use a field in the XML mapping file, and this doesn’t work on GraalVM.

Building and Running the Example

To build the example project you can clone the project at https://github.com/flowable/flowable-examples/tree/master/blog/04-2021-flowable-spring-boot-native

You’ll need to have GraalVM on your system. On a Mac, using SDKMan, this can be done as follows:

sdk install java 21.0.0.2.r11-grl
sdk use java 21.0.0.2.r11-grl
gu install native-image

On a Mac, you also need the bits of xcode to compile to a Mac binary:

xcode-select --install

You’ll also need a snapshot build of the current Flowable master branch, because of the missing getters/setters. Clone https://github.com/flowable/flowable-engine and execute the following to install that snapshot locally:

mvn -Pdistro clean install -DskipTests

You can now build a native image using the following command:

mvn -Pnative-image clean package

This runs for a few minutes, depending on your hardware and how aggressively your anti-virus likes to scan files:

In the target folder there will now be a binary, ‘org.flowable.demo.demoapplication’, which can be executed directly:

From the screenshot, you can see that executing this binary boots up a Spring Boot application with a Flowable process engine embedded in 0.655 seconds. This is including a database connection pool and a Tomcat webserver (something for a future post).

How Low Can You Go?

A fun thing to try now is to see the minimal amount of RAM you can run this with. Our unscientific tests showed that 19 MB of memory was the lowest we could go that still would start process instances:

The startup time was now 10 seconds – although most likely 99% was spent in constant garbage collecting!

Yet, it’s pretty awesome to see what Java + Spring Boot + Flowable can do with a minimum of memory.

Conclusion

The conclusion is pretty simple and sweet: we made a fully functioning GraalVM native image with Flowable running embedded in Spring Boot with a persistent relational database. Being able to build native images opens up all kind of innovative use cases for the future. With both Spring and Flowable working natively, a new milestone has surely been reached.

Of course, there’s still a lot of work and investigation to do, for example, we’re looking into a flowable-native maven module much like https://github.com/spring-projects-experimental/spring-native/tree/main/spring-native-configuration/src/main/java/org, trimming the reflection configuration file.

Stay tuned for more soon!