Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving documentation for @AutomaticFeature #1234

Closed
vojkny opened this issue May 3, 2019 · 10 comments
Closed

Improving documentation for @AutomaticFeature #1234

vojkny opened this issue May 3, 2019 · 10 comments

Comments

@vojkny
Copy link

vojkny commented May 3, 2019

I am trying to implement @AutomaticFeature described here:

https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md#manual-configuration

I implemented the class, but it is not clear where it should be put. Firstly, I had it normally within my code, but it did not work. Then I found some information about META-INF directory, but it is still not being registered. I turned on the trace info for reflection with -H:+TraceServiceLoaderFeature, but my class doesn't appear there.

I am just trying simply the original example:

@AutomaticFeature
class RuntimeReflectionRegistrationFeature implements Feature {
  public void beforeAnalysis(BeforeAnalysisAccess access) {
    try {
      RuntimeReflection.register(MyClass.class);
    } catch (NoSuchMethodException | NoSuchFieldException e) { ... }
  }
}

Also would it be possible to write wildcards within reflect.json - something like:

[{
  "name": "com.maxmind.geoip2.model.*",
  "allPublicMethods": true,
  "allDeclaredConstructors": true
}]

There are tens of classes and writing each of them makes the file super long.

@cstancu
Copy link
Member

cstancu commented May 3, 2019

Try adding the feature class to the native-image using --features= which takes A comma-separated list of fully qualified Feature implementation classes. This is preferred over the @AutomaticFeature approach, we will update the docs. The class implementing Feature just needs to be on the native-image classpath in both cases.

Regarding wildcards for reflection, we currently don't support that.

@vojkny
Copy link
Author

vojkny commented May 3, 2019

I added the --feature param and now I can see the:

adding implementation class: ...RuntimeReflectionRegistrationFeature

But the class it is supposed to add is still not added.

To support the wildcard - do you want to create separate issue for feature?

@cstancu
Copy link
Member

cstancu commented May 3, 2019

Yes, please create a separate issue for the wildcard feature.

@cstancu
Copy link
Member

cstancu commented May 3, 2019

What do you mean by:

But the class it is supposed to add is still not added.

@vojkny
Copy link
Author

vojkny commented May 3, 2019

Okay, I probably see the problem now:

    override fun beforeAnalysis(access: Feature.BeforeAnalysisAccess) {
        RuntimeReflection.register(com.maxmind.geoip2.model.CityResponse::class.java)
        RuntimeReflection.register(com.maxmind.geoip2.model.CountryResponse::class.java)

        registerPackage("com.maxmind.geoip2.model")
        registerPackage("com.maxmind.geoip2.record")
    }



    private fun registerPackage(packageName: String) {
        System.err.println("Registering package $packageName.")
        val reflections = Reflections(packageName)

        for (clazz in reflections.getSubTypesOf(Any::class.java)) {
            RuntimeReflection.register(clazz)
        }
    }

When I call directly the .register method, it works correctly, but the reflection used to find all classes within package is probably ignored. Is there some hidden logic, which would prevent adding the whole package?

@cstancu
Copy link
Member

cstancu commented May 3, 2019

Is there some hidden logic, which would prevent adding the whole package?

No. Are you sure that for (clazz in reflections.getSubTypesOf(Any::class.java)) enumerates all the classes that you want to register? This is outside the scope of our code.

@vojkny
Copy link
Author

vojkny commented May 3, 2019

Okay, that was my error. However, I fixed that and the classes are still not registered. Current code is:

   override fun beforeAnalysis(access: Feature.BeforeAnalysisAccess) {
        RuntimeReflection.register(com.maxmind.geoip2.model.CountryResponse::class.java)
        registerPackage("com.maxmind.geoip2.model")
        registerPackage("com.maxmind.geoip2.record")
    }



    private fun registerPackage(packageName: String) {
        val reflections = Reflections(packageName, SubTypesScanner(false))
        val classes = reflections.getSubTypesOf(Any::class.java)
        log.info("Registering package, found classes: ${classes.size}.")

        for (clazz in classes) {
            log.info("Registering $clazz.")
            RuntimeReflection.register(clazz)
        }
    }

which logs this during native-image:

22:11:24.440 INFO  org.reflections.Reflections - Reflections took 41 ms to scan 1 urls, producing 6 keys and 13 values
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering package, found classes: 12.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.EnterpriseResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.IspResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.CityResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.AbstractCityResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.AbstractResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.AbstractCountryResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.DomainResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.InsightsResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.CountryResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.AnonymousIpResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.ConnectionTypeResponse.
22:11:24.443 INFO  n.g.g.RuntimeReflectionRegistrationFeature - Registering class com.maxmind.geoip2.model.AsnResponse.
ServiceLoaderFeature: processing service class io.micronaut.inject.BeanDefinitionReference
  adding implementation class: io.micronaut.buffer.netty.$NettyByteBufferFactoryDefinitionClass
  adding implementation class: io.micronaut.cache.$AsyncCacheErrorHandlerDefinitionClass
  adding implementation class: io.micronaut.cache.$DefaultCacheConfigurationDefinitionClass

Full log is here: https://gist.github.com/knyttl/645f9e557ce02a514e109f84673762d9

Basically not even the first line RuntimeReflection.register(com.maxmind.geoip2.model.CountryResponse::class.java) seems not to do anything.

Is it okay, the adding implementation class does not happen for the classes added via .register method?

When the compilation finishes and these classes should be used, I get:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.maxmind.geoip2.model.CityResponse` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: UNKNOWN; line: -1, column: -1]

@cstancu
Copy link
Member

cstancu commented May 3, 2019

(no Creators, like default construct, exist) probably means that you need to register the default constructor too. RuntimeReflection.register(clazz) only registers the java.lang.Class itself, e.g., for access via Class.forName(), it doesn't register any methods or fields. The reflection documentation describes how to register/methods fieds, e.g.: RuntimeReflection.register(String.class.getDeclaredConstructor(char[].class));

@vojkny
Copy link
Author

vojkny commented May 3, 2019

Ah okay. I guess there is no wildcard like there is within the reflect.json file.

@vojkny
Copy link
Author

vojkny commented May 3, 2019

I created two separate tasks:

@vojkny vojkny closed this as completed May 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants