Giter VIP home page Giter VIP logo

graalvm-hint's Introduction

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 ๐Ÿš€

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:

  • allPublicFields
  • allPublicMethods
  • allPublicConstructors
  • allDeclaredFields
  • allDeclaredMethods
  • allDeclaredConstructors

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:

  • allPublicFields
  • allPublicMethods
  • allPublicConstructors
  • allDeclaredFields
  • allDeclaredMethods
  • allDeclaredConstructors

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.

Link Self Config

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

Link 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 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

Link All Classes Config

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:

  • graalvm.hint.group - group name.
  • graalvm.hint.artifact - artifact name.

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

graalvm-hint's People

Contributors

entlicher avatar goodforgod avatar linux-china avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

graalvm-hint's Issues

@ResourceHint is not up-to-date

Describe the bug
Currently generating like:

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

But in documentation is like:

{
  "resources": {
    "includes": [
      {"pattern": "<Java regexp that matches resource(s) to be included in the image>"},
      {"pattern": "<another regexp>"},
      ...
    ],
    "excludes": [
      {"pattern": "<Java regexp that matches resource(s) to be excluded from the image>"},
      {"pattern": "<another regexp>"},
      ...
    ]
  }
}

Expected behavior
A clear and concise description of what you expected to happen.

{
  "resources": {
    "includes": [
      {"pattern": "<Java regexp that matches resource(s) to be included in the image>"},
      {"pattern": "<another regexp>"},
      ...
    ],
    "excludes": [
      {"pattern": "<Java regexp that matches resource(s) to be excluded from the image>"},
      {"pattern": "<another regexp>"},
      ...
    ]
  }
}

Also add bundle options support.
This will be breaking change.

Java Module System Support

Describe the solution you'd like
Yes, my feature request is related to a problem. Currently, I am incorporating your library into a Java project that utilizes the Java Module System. However, the lack of support for this system in your library presents a challenge.

Describe alternatives you've considered
To resolve this issue, I kindly request you to consider adding support for the Java Module System to your library. This could be achieved by incorporating a module-info.java file or by defining an Automatic-Module-Name in the MANIFEST.MF file. Either of these additions would allow your library to be used as a module in a modular Java application.

Reflection registration for getter / setter

Is your feature request related to a problem? Please describe.
Lets say I have the following Java class:

@ReflectionHint
public class RootFs {
   private final String type;
   private final List diffIds;

   public RootFs(String type, List diffIds) {
      this.type = type;
      this.diffIds = diffIds;
   }

   public String getType() {
      return this.type;
   }

   public List getDiffIds() {
      return this.diffIds;
   }
}

What actually gets generated is:

[{
  "name": "de.cmdjulian.kirc.spec.image.RootFs",
  "allDeclaredConstructors": true,
  "allDeclaredFields": true,
  "allDeclaredMethods": true
}]

On the runtime, this registers querying of all the different methods, but it does not register the getters nor the constructor. When I know try to serialize / deserialize such a class with Jackson, I get an error as nor the constructor, nor the getters are registered.

Describe the solution you'd like
I'm not 100% sure how to resolve this, but I think maybe having a boolean flag in the annotation includeAccessors and includeConstructors with a default true setting would be the way to go. Additionally the annotation could be allowed to be placed on methods and constructors to register the annotated element as well.

Describe alternatives you've considered

Additional context
Atm is quite impossible to use the lib with classes which do depend on getter / setter / constructor serialization like jackson does

@ReflectionHint types has problem with inner static class

Describe the bug

The @ReflectionHint(types = { Result.class }) does not seem to work for inner static classes.

To Reproduce
Steps to reproduce the behavior:

  1. Create a file with the following content
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 21+
//DEPS io.goodforgod:graalvm-hint-processor:1.1.0
//DEPS io.goodforgod:graalvm-hint-annotations:1.1.0
//DEPS com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.0


import static java.lang.System.*;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

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

public class TestYaml {

    public static void main(String... args) throws JsonMappingException, JsonProcessingException {
        out.println("Hello World");

        String document = "hello: 25";

        ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
        Result r = objectMapper.readValue(document, Result.class);

        out.println(r.hello);
    }

    public static class Result {
        public Integer hello;
    }
    
    //@ReflectionHint(typeNames = {"TestYaml$Result"})
    @ReflectionHint(types = { Result.class })
    public class ReflectionConfig {}

}
  1. Execute it with jbang it should display
Hello World
25
  1. when exporting the script jbang export native --force --verbose TestSnakeYaml.java the execution of the binary yields the following error
Hello World
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `TestYaml$Result`: cannot deserialize from Object value (no delegate- or property-based Creator): this appears to be a native image, in which case you may need to configure reflection for the class that is to be deserialized
 at [Source: (StringReader); line: 1, column: 1]
        at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1887)
  1. If the comment is switched to the typeNames it works.

Expected behavior
Should work in both cases.

How about @DynamicProxyHint and @SubstitutionHint

@GoodforGod, it's nice project ๐Ÿ‘

Now I use optionNames to add DynamicProxy support with following code:

@NativeImageHint(optionNames = {"-H:DynamicProxyConfigurationResources=com.example.UserService"})

Another problem is -H:SubstitutionResources=${.}/substitutions.json , and substitutions should be supplied by json file.

How about new @DynamicProxyHint and @SubstitutionHint annotations? I think they are more friendly to developers.

Add groupId and artifactId attributes for @NativeImageHint

Now hint processor uses package name from classes as groupId, and hint as artifactId for META-INF/native-image/. It's ok for native image app, such as command line app. I have an sdk/library and I want use hint processor to generate native-image configuration json files, and other normal java apps or native-image apps can use this library both.

Now groupId and artifactId generated by processor are not fixed, and could be changed by new classes with hint annotations:

image

Is it possible to add groupId and artifactId for @NativeImageHint? and processor can use them for native-image configuration files generation.

@NativeImageHint(groupId="com.example", artifactId="my-sdk-hint") 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.