graalvm-hint

GraalVM Hint Processor

Minimum required Java version Maven Central GitHub Action Coverage Maintainability Rating Lines of Code

GraalVM Hint Processor helps generate GraalVM hints for building native-image applications.

Fully AOT processing library, no dependencies, no runtime side-affects.

Features:

Dependency :rocket:

Gradle

annotationProcessor "io.goodforgod:graalvm-hint-processor:1.2.0"
compileOnly "io.goodforgod:graalvm-hint-annotations:1.2.0"

Maven

<dependencies>
    <dependency>
        <groupId>io.goodforgod</groupId>
        <artifactId>graalvm-hint-annotations</artifactId>
        <version>1.2.0</version>
        <scope>compile</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>io.goodforgod</groupId>
        <artifactId>graalvm-hint-processor</artifactId>
        <version>1.2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.10.1</version>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>io.goodforgod</groupId>
                        <artifactId>graalvm-hint-processor</artifactId>
                        <version>1.2.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

Content

@ReflectionHint

You can read more about GraalVM reflection configuration in official documentation here.

There are available access hints:

Generating reflection access, most used cases is DTOs that are used for serialization/deserialization in any format (JSON for example).

Reflection Self Config

Simple case for single Java class:

@ReflectionHint
public class RequestOnly {

    private String name;
}

Generated reflection-config.json:

[{
  "name": "io.goodforgod.graalvm.hint.processor.RequestOnly",
  "allDeclaredConstructors": true,
  "allDeclaredFields": true,
  "allDeclaredMethods": true
}]

Reflection Multi Config

There may be more different cases, like generating hints for classes that are package private or just private, also there hint can be used for whole package.

Complex example generating reflection access:

@ReflectionHint(types = { Response.class, Request.class }, value = ReflectionHint.AccessType.ALL_DECLARED_FIELDS)
@ReflectionHint(typeNames = { "io.goodforgod.graalvm.hint.processor"})
public class ReflectionConfig {

    private String name;
}

Generated reflection-config.json:

[{
  "name": "io.goodforgod.graalvm.hint.processor",
  "allDeclaredConstructors": true,
  "allDeclaredFields": true,
  "allDeclaredMethods": true
},
{
  "name": "io.goodforgod.example.Response",
  "allDeclaredFields": true
},
{
  "name": "io.goodforgod.example.Request",
  "allDeclaredFields": true
}]

@ResourceHint

You can read more about GraalVM resource configuration in official documentation here.

Hint allows generating config for resource files to be included/excluded when building native application. You can also include bundles into native image using this Hint.

Include Patterns

Resource patterns specified with Java regexp to Include during native-image generation into the final application.

Include Hint:

@ResourceHint(include = { "simplelogger.properties", "application.yml", "*.xml" })
public class ResourceNames {

}

Generated resource-config.json:

{
  "resources": {
    "includes": [
      { "pattern" : "*.xml" },
      { "pattern": "application.yml" },
      { "pattern": "simplelogger.properties" }
    ]
  }
}

Exclude Patterns

Resource patterns specified with Java regexp to Exclude during native-image generation into the final application.

Exclude Hint:

@ResourceHint(exclude = { "*.xml" })
public class ResourceNames {

}

Generated resource-config.json:

{
  "resources": {
    "excludes": [
      { "pattern": "*.xml" }
    ]
  }
}

Include Bundles

Native Image needs ahead-of-time knowledge of the resource bundles your application needs so that it can load and store the appropriate bundles for usage in the generated binary.

Bundle Hint:

@ResourceHint(bundles = { "your.pkg.Bundle" })
public class ResourceNames {

}

Generated resource-config.json:

{
  "bundles": [
    { "name": "your.pkg.Bundle" }
  ]
}

@NativeImageHint

You can read more about GraalVM native-image options in official documentation here.

Hint allows generating config for native-image options and initial application entrypoint.

Entrypoint

Simple hint configuration:

@NativeImageHint(entrypoint = EntrypointOnly.class)
public class EntrypointOnly {

    public static void main(String[] args) {}
}

Generated native-image.properties:

Args = -H:Class=io.goodforgod.graalvm.hint.processor.EntrypointOnly

Entrypoint and Options

Complex hint configuration with options:

@NativeImageHint(entrypoint = Entrypoint.class, name = "myapp", options = { NativeImageOptions.PRINT_INITIALIZATION, NativeImageOptions.INLINE_BEFORE_ANALYSIS })
public class Entrypoint {

    public static void main(String[] args) {}
}

Generated native-image.properties:

Args = -H:Class=io.goodforgod.graalvm.hint.processor.Entrypoint -H:Name=myapp \
       -H:+PrintClassInitialization \
       -H:+InlineBeforeAnalysis

@InitializationHint

You can read more about GraalVM initialization configuration in official documentation here.

Hint allows generating config for what classes to instantiate in runtime and what classes to instantiate in compile time.

Runtime and Compile Time Config

Initialization hint configuration:

@InitializationHint(value = InitializationHint.InitPhase.BUILD, types = HintOptions.class)
@InitializationHint(value = InitializationHint.InitPhase.RUNTIME, typeNames = "io.goodforgod.graalvm.hint.processor")
public class EntrypointOnly {

}

Generated native-image.properties:

Args = --initialize-at-build-time=io.goodforgod.graalvm.hint.processor.HintOrigin.class \
       --initialize-at-run-time=io.goodforgod.graalvm.hint.processor

Initialization Self Config

Simple case for single Java class:

@InitializationHint
public class Self {

}

Generated native-image.properties:

Args = --initialize-at-build-time=io.goodforgod.graalvm.hint.processor.Self

@DynamicProxyHint

You can read more about GraalVM DynamicProxyHint configuration in official documentation here.

Resources and Files Config

Use can pass dynamic proxy resources (-H:DynamicProxyConfigurationResources) or files (-H:DynamicProxyConfigurationFiles) using corresponding options:

@DynamicProxyHint(resources = {"proxy-resource.json"}, files = {"proxy-file.json"})
public class Resource {

}

Generated native-image.properties:

Args = -H:DynamicProxyConfigurationFiles=proxy-file.json \
       -H:DynamicProxyConfigurationResources=proxy-resource.json

Interfaces Multi Config

You can fully configure proxy yourself using annotations only, without the need for manually creating JSON configurations.

@DynamicProxyHint(value = {
        @DynamicProxyHint.Configuration(interfaces = {OptionParser.class, HintOrigin.class}),
        @DynamicProxyHint.Configuration(interfaces = {HintOrigin.class})
})
public class Config {

}

Generated dynamic-proxy-hint-config.json:

[
  { "interfaces": [ "io.goodforgod.graalvm.hint.processor.OptionParser", "io.goodforgod.graalvm.hint.processor.HintOrigin" ] },
  { "interfaces": [ "io.goodforgod.graalvm.hint.processor.HintOrigin" ] }
]

Generated native-image.properties:

Args = -H:DynamicProxyConfigurationResources=META-INF/native-image/io.goodforgod.graalvm.hint.processor/hint/dynamic-proxy-config.json

Interfaces Self Config

In case you need to add only one interface for DynamicProxy Hint configuration, you can annotate that interface directly:

@DynamicProxyHint
public interface Self {

}

Generated dynamic-proxy-hint-config.json:

[
  { "interfaces": [ "io.goodforgod.graalvm.hint.processor.Self" ] }
]

Generated native-image.properties:

Args = -H:DynamicProxyConfigurationResources=META-INF/native-image/io.goodforgod.graalvm.hint.processor/hint/dynamic-proxy-config.json

@JniHint

You can read more about GraalVM JNI configuration in official documentation here.

There are available JNI access hints:

JNI Self Config

Simple case for single Java class:

@JniHint
public class RequestOnly {

    private String name;
}

Generated jni-config.json:

[{
  "name": "io.goodforgod.graalvm.hint.processor.RequestOnly",
  "allDeclaredConstructors": true,
  "allDeclaredFields": true,
  "allDeclaredMethods": true
}]

JNI Multi Config

There may be more different cases, like generating hints for classes that are package private or just private, also there hint can be used for whole package.

Complex example generating reflection access:

@JniHint(types = { Response.class, Request.class }, value = JniHint.AccessType.ALL_DECLARED_FIELDS)
@JniHint(typeNames = { "io.goodforgod.graalvm.hint.processor"})
public final class JniConfig {

}

Generated jni-config.json:

[{
  "name": "io.goodforgod.graalvm.hint.processor",
  "allDeclaredConstructors": true,
  "allDeclaredFields": true,
  "allDeclaredMethods": true
},
{
  "name": "io.goodforgod.example.Response",
  "allDeclaredFields": true
},
{
  "name": "io.goodforgod.example.Request",
  "allDeclaredFields": true
}]

@LinkHint

You can read more about GraalVM Link Build configuration in official documentation here.

Hint is only available in GraalVM 22.1.0+, check this PR for more info.

Specify types to be fully defined at image build-time.

Simple case for single Java class:

@LinkHint
public class RequestOnly {

    private String name;
}

Generated native-image.properties:

Args = --link-at-build-time=io.goodforgod.graalvm.hint.processor.RequestOnly

There may be more different cases, like generating hints for classes that are package private or just private, also there hint can be used for whole package.

Complex example generating link hints:

@LinkHint(types = { Response.class, Request.class })
@LinkHint(typeNames = { "io.goodforgod.graalvm.hint.processor"})
public final class JniConfig {

}

Generated native-image.properties:

Args = --link-at-build-time=io.goodforgod.graalvm.hint.processor,io.goodforgod.graalvm.hint.processor.Response,io.goodforgod.graalvm.hint.processor.Request

Is the same as using GraalVM flag without options.

If used without args, all classes in scope of the option are required to be fully defined.

Example how to force all classes to link in build time, no matter what how other class declare this hint, if any annotation with all = true found, then it will be used this way.

@LinkHint(all = true)
public class RequestOnly {

    private String name;
}

Generated native-image.properties:

Args = --link-at-build-time

Group and Artifact name

You can change the output group and artifact name, by default the group will be the package name where the annotated class was located and the artifact will be named hint.

For class:

package io.goodforgod.graalvm.hint.processor;

import io.goodforgod.graalvm.hint.annotation.ReflectionHint;

@ReflectionHint
public class Request {

    private String name;
}

Hint will be generated into build/classes/java/main/META-INF/native-image/io.goodforgod.graalvm.hint.processor/hint/reflect-config.json directory. Where io.goodforgod.graalvm.hint.processor is group name and hint artifact name.

You can override default behavior and select our own group and artifact name via annotation processor options.

Annotation processor options:

Here are examples of configurations for Gradle and Maven.

Gradle

compileJava {
    options.compilerArgs += [
            "-Agraalvm.hint.group=my.group",
            "-Agraalvm.hint.artifact=myartifact",
    ]
}

Maven

<build>
  <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.10.1</version>
        <configuration>
            <parameters>true</parameters>
            <compilerArgs>
                <compilerArg>-Agraalvm.hint.group=my.group</compilerArg>
                <compilerArg>-Agraalvm.hint.artifact=myartifact</compilerArg>
            </compilerArgs>
        </configuration>
    </plugin>
  </plugins>
</build>

License

This project licensed under the Apache License 2.0 - see the LICENSE file for details