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

Allow all classes from a JAR, Module, or Package to be Included for Reflection #558

Open
vjovanov opened this issue Jan 12, 2024 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@vjovanov
Copy link
Member

vjovanov commented Jan 12, 2024

End users with third-party dependencies without provided reflection metadata must currently devise the required metadata themselves. Producing metadata is time-consuming and it can discourage users from compiling natively.

We need a mechanism in both Maven and Gradle plugins to include all elements for reflection:

  1. From any maven dependency.
  2. From any package prefix.
  3. From any module, including the JDK modules.

The implementation should be native-image agnostic and should produce reflect-config.json based on the classpath. The build tool must crawl the classpath and based on the detected classes produce corresponding reflect-config.json files.

This feature has also been requested for Native Image directly in the reflect-config.json files. This is not feasible as it would make it too easy for the libraries to include all elements and bloat images across the ecosystem.

The Policy for Selecting Classes

We would include all classes in the jar (module, or package) except:

  1. The Native Image Feature subclasses that are needed only at build time.
  2. The lambdas due to the need to parse all bytecodes to fetch them.
  3. The proxy classes, due to an exponential number of interface configurations that could create them.

The reflect-config.json would be computed by crawling the whole classpath (when the classpath changes) and by traversing all class files in the library. The functionality needs to be implemented as a common functionality.

Notifying the Users About the Added Elements

Each build should contain the output containing the list of packages that are bulk-added for reflection and notifying that these might bloat the native image size. This is necessary so users know what was included and that they expect the image-size increase.

Allowing Access to Generated Reflection Metadata

The generated metadata should be output to a predefined location (mentioned in the build output) and editable by the end users so they can choose to use it as a starting point for their hand-crafted metadata.

Implementation in Gradle

The current DSL proposal for Gradle follows. Under the individual binary section we would add a block:

includeForReflection {
  packages.add("my.app.tests.**") // testing should just work, don't care
  packages.add("my.app.messages.json.*") // all classes in a package are JSON messages
  modules.add("java.desktop") // my swing app should just work
  dependencies.add("com.graphql-java:graphql-java:19.2") // this library does not provide reflection
}
@vjovanov vjovanov added the enhancement New feature or request label Jan 12, 2024
@vjovanov vjovanov changed the title Allow Whole JARs or Packages to be Included for Reflection Allow all classes from a JAR or a Package to be Included for Reflection Jan 12, 2024
@vjovanov vjovanov changed the title Allow all classes from a JAR or a Package to be Included for Reflection Allow all classes from a JAR, Module, or Package to be Included for Reflection Feb 6, 2024
@pompiuses
Copy link

This feature is very welcomed! We're currently using the tracing agent together with our test suite to automatically pick up all use of reflection (which is somewhat error prone if low test coverage). This new feature in combination with the tracing agent would eliminate all reflection related runtime errors we get. So it'd be nice if it's made to work seamlessly together.

@kristofdho
Copy link

Looks promising. A couple questions/suggestions.

  1. Is it possible to combine filters? E.g. Only package org.example.foo from dependency org.example:bar:*?
  2. If you know you only need constructors, is it possible to only add them for an entire package?
  3. Is it possible to provide a custom filter to the plugin? A Predicate<Class> and Predicate<Executable> for example?
  4. Is it possible to still specify a "typeReachable" condition on the generated entries?

A specific use-case I have now, for which I have a Feature with a subtype reachability handler:
For all subclasses of Foo Register all methods prefixed with create* with a matching return type and parameter list.
Which is extremely specific I guess. I'd imagine specifying a custom filter class in the lines of this, which is still missing the "typeReachable" part:

class CustomFilter implements PluginFilter {
    @Override
    boolean includeClass(Class<?> clazz) {
        return Foo.class.isAssignableFrom(clazz);
    }

    @Override
    boolean includeMethod(Method method) {
        return method.getName().startsWith("create"); // + check returntype & parameter types
    }

    @Override
    boolean includeConstructor(Constructor<?> cconstructor) {
        return false;
    }

    @Override
    boolean includeField(Field field) {
        return false;
    }
}

@vjovanov
Copy link
Member Author

vjovanov commented Mar 4, 2024

@kristofdho this is possible, we need to be careful not to duplicate too much of functionality. Such fine-grained adjustments can currently be done with Native Image features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants