GraalVM native images
Quick overview
- GraalVM native images can be built with no additional configuration starting with driver 4.13.0.
- But extra configurations are required in a few cases:
- When using reactive programming;
- When using Jackson;
- When using LZ4 compression;
- Depending on the logging backend in use.
- DSE-specific features:
- Geospatial types are supported.
- DSE Graph is not officially supported, although it may work.
- The shaded jar is not officially supported, although it may work.
Concepts
Starting with version 4.13.0, the driver ships with embedded GraalVM configuration files that allow GraalVM native images including the driver to be built without hassle, barring a few exceptions and caveats listed below.
Classes instantiated by reflection
The driver instantiates its components by reflection. The actual classes that will be instantiated
in this way need to be registered for reflection. All built-in implementations of various driver
components, such as LoadBalancingPolicy
or TimestampGenerator
, are automatically registered for
reflection, along with a few other internal components tha are also instantiated by reflection.
You don’t need to manually register any of these built-in implementations.
But if you intend to use a custom implementation in lieu of a driver built-in class, then it is your responsibility to register that custom implementation for reflection.
For example, assuming that you have the following load balancing policy implementation:
package com.example.app;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.metadata.Node;
import com.datastax.oss.driver.internal.core.loadbalancing.DefaultLoadBalancingPolicy;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class CustomLoadBalancingPolicy extends DefaultLoadBalancingPolicy {
public CustomLoadBalancingPolicy(DriverContext context, String profileName) {
super(context, profileName);
}
// rest of class omitted for brevity
}
And assuming that you declared the above class in your application.conf file as follows:
datastax-java-driver.basic{
load-balancing-policy.class = com.example.app.CustomLoadBalancingPolicy
}
Then you will have to register that class for reflection:
- Create the following reflection.json file, or add the entry to an existing file:
[
{ "name": "com.example.app.CustomLoadBalancingPolicy", "allPublicConstructors": true }
]
- When invoking the native image builder, add a
-H:ReflectionConfigurationFiles=reflection.json
flag and point it to the file created above.
Note: some frameworks allow you to simplify the registration process. For example, Quarkus offers
the io.quarkus.runtime.annotations.RegisterForReflection
annotation that you can use to annotate
your class:
@RegisterForReflection
public class CustomLoadBalancingPolicy extends DefaultLoadBalancingPolicy {
//...
}
In this case, no other manual configuration is required for the above class to be correctly registered for reflection.
Configuration resources
The default driver configuration mechanism is based on the TypeSafe Config
library. TypeSafe Config looks for a few classpath resources when initializing the configuration:
reference.conf
, application.conf
, application.json
, application.properties
. These classpath
resources are all automatically included in the native image: you should not need to do it
manually. See Accessing Resources in Native Images for more information on how classpath
resources are handled in native images.
Configuring the logging backend
When configuring logging, the choice of a backend must be considered carefully, as most logging backends resort to reflection during their configuration phase.
By default, GraalVM native images provide support for the java.util.logging (JUL) backend. See this page for more information.
For other logging backends, please refer to the logging library documentation to find out if GraalVM native images are supported.
Using reactive-style programming
The reactive execution model is compatible with GraalVM native images, but the following configurations must be added:
- Create the following reflection.json file, or add the entry to an existing file:
[
{ "name": "org.reactivestreams.Publisher" }
]
- When invoking the native image builder, add a
-H:ReflectionConfigurationFiles=reflection.json
flag and point it to the file created above.
Using the Jackson JSON library
Jackson is used in a few places in the driver, but is an optional dependency; if you intend to use Jackson, the following configurations must be added:
- Create the following reflection.json file, or add these entries to an existing file:
[
{ "name": "com.fasterxml.jackson.core.JsonParser" },
{ "name": "com.fasterxml.jackson.databind.ObjectMapper" }
]
Important: when using the shaded jar – which is not officially supported on GraalVM native images, see below for more details – replace the above entries with the below ones:
[
{ "name": "com.datastax.oss.driver.shaded.fasterxml.jackson.core.JsonParser" },
{ "name": "com.datastax.oss.driver.shaded.fasterxml.jackson.databind.ObjectMapper" }
]
- When invoking the native image builder, add a
-H:ReflectionConfigurationFiles=reflection.json
flag and point it to the file created above.
Enabling compression
When using compression, only LZ4 can be enabled in native images. Snappy compression is not supported.
In order for LZ4 compression to work in a native image, the following additional GraalVM configuration is required:
- Create the following reflection.json file, or add these entries to an existing file:
[
{ "name" : "net.jpountz.lz4.LZ4Compressor" },
{
"name" : "net.jpountz.lz4.LZ4JNICompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4JavaSafeCompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4JavaUnsafeCompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4HCJavaSafeCompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4HCJavaUnsafeCompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4JavaSafeSafeDecompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4JavaSafeFastDecompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4JavaUnsafeSafeDecompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
},
{
"name" : "net.jpountz.lz4.LZ4JavaUnsafeFastDecompressor",
"allDeclaredConstructors": true,
"allPublicFields": true
}
]
- When invoking the native image builder, add a
-H:ReflectionConfigurationFiles=reflection.json
flag and point it to the file created above.
Native calls
The driver performs a few native calls using JNR.
Starting with driver 4.7.0, native calls are also possible in a GraalVM native image, without any extra configuration.
Using DataStax Enterprise (DSE) features
DSE Geospatial types
DSE Geospatial types are supported on GraalVM native images; the following configurations must be added:
- Create the following reflection.json file, or add the entry to an existing file:
[
{ "name": "com.esri.core.geometry.ogc.OGCGeometry" }
]
Important: when using the shaded jar – which is not officially supported on GraalVM native images, as stated above – replace the above entry with the below one:
[
{ "name": "com.datastax.oss.driver.shaded.esri.core.geometry.ogc.OGCGeometry" }
]
- When invoking the native image builder, add a
-H:ReflectionConfigurationFiles=reflection.json
flag and point it to the file created above.
DSE Graph
DSE Graph is not officially supported on GraalVM native images.
The following configuration can be used as a starting point for users wishing to build a native image for a DSE Graph application. DataStax does not guarantee however that the below configuration will work in all cases. If the native image build fails, a good option is to use GraalVM’s Tracing Agent to understand why.
- Create the following reflection.json file, or add these entries to an existing file:
[
{ "name": "org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0" },
{ "name": "org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal" },
{ "name": "org.apache.tinkerpop.gremlin.structure.Graph",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
},
{ "name": "org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
},
{ "name": " org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
},
{ "name": "org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
}
]
- When invoking the native image builder, add the following flags:
-H:ReflectionConfigurationFiles=reflection.json
--initialize-at-build-time=org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0
--initialize-at-build-time=org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer
Using the shaded jar
The shaded jar is not officially supported in a GraalVM native image.
However, it has been reported that the shaded jar can be included in a GraalVM native image as a drop-in replacement for the regular driver jar for simple applications, without any extra GraalVM configuration.