native-image fails to build with google-auth-library-oauth2-http in classpath

216 views Asked by At

I am trying to build some software using GraalVM's native-image. My dependencies include google-auth-library-oauth2-http. My build fails even when I don't actually use anything from the library:

➜  gv cat HelloWorld.java
class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

➜  javac HelloWorld.java && native-image -cp google-auth-library-oauth2-http-1.20.0.jar -cp . HelloWorld
...
The build process encountered an unexpected error:

> com.oracle.svm.core.util.VMError$HostedError: com.oracle.svm.core.util.UserError$UserException: Image heap writing found a class not seen during static analysis. Did a static field or an object referenced from a static field change during native image generation? For example, a lazily initialized cache could have been initialized during image generation, in which case you need to force eager initialization of the cache before static analysis or reset the cache using a field value recomputation.
class: java.util.concurrent.ConcurrentSkipListMap$Values
  reachable through:
    object: [Ljava.lang.Class;@1668bbd3  of class: java.lang.Class[]
    object: com.oracle.svm.core.code.ImageCodeInfo@1703e50d  of class: com.oracle.svm.core.code.ImageCodeInfo
    root: com.oracle.svm.core.code.ImageCodeInfo.prepareCodeInfo()

The library itself seems to come with some graalvm resources, so the authors clearly keep this use-case in mind:

➜  gv unzip -l google-auth-library-oauth2-http-1.20.0.jar | grep .json
    14581  10-04-2023 00:49   META-INF/native-image/com.google.auth/google-auth-library-oauth2-http/reflect-config.json

But I can't quite figure out what I am doing wrong to make it work.

1

There are 1 answers

0
VonC On BEST ANSWER

Check if this is related to "Reflection in Native Image

Native Image has partial support for reflection and needs to know ahead-of-time the reflectively accessed program elements. Examining and accessing program elements through java.lang.reflect.* or loading classes with Class.forName(String) at run time requires preparing additional metadata for those program elements. (Note: loading classes with Class.forName(String) are included here since it is closely related to reflection.)

Native Image tries to resolve the target elements through a static analysis that detects calls to the Reflection API. Where the analysis fails, the program elements reflectively accessed at run time must be specified using a manual configuration.

The error message shows that a class not encountered during the static analysis phase is being used dynamically at runtime, which native-image cannot handle by default.

You should try and provide additional configuration to help native-image understand and include these dynamically loaded classes. The library does come with a reflect-config.json, but you might need to extend this configuration to cover all cases.

Try and modify or extend the reflect-config.json file in the library to include any missing classes or members that are dynamically accessed.
When building your native image, specify the path to the reflection configuration file using the -H:ReflectionConfigurationFiles option.

For extending the reflect-config.json for google-auth-library-oauth2-http, you will need to identify the classes and methods that are accessed reflectively but are not covered in the existing configuration. The error message you encountered gives a hint: java.util.concurrent.ConcurrentSkipListMap$Values.

Create a new JSON file (named; for instance, extended-reflect-config.json) and include entries for the missing classes and their methods.
For the class mentioned in the error, the JSON entry would be:

[
    {
    "name": "java.util.concurrent.ConcurrentSkipListMap$Values",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
    }
    // include other missing classes similarly
]

That configuration tells native-image to include all constructors and methods (both declared and public) of the specified class.

If there are other configurations in the existing reflect-config.json, you should merge them with your extended-reflect-config.json to make sure all necessary configurations are included.

When building with native-image, reference your extended-reflect-config.json:

native-image -cp google-auth-library-oauth2-http-1.20.0.jar -cp . -H:ReflectionConfigurationFiles=extended-reflect-config.json HelloWorld

That process may require some trial and error to identify all the necessary classes and methods.
You can see a similar case study in "Spring Boot with GraalVM Native Image" from Dmitry Chuyko.