-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Automatically register classes in META-INF/services for reflection. #563
Comments
The classes should probably only be registered if the service type is actually used, to avoid extra classes being pulled in. |
This is somewhat complicated because it does not appear that the Feature API allows dependencies to be added to classes. Might it be better to rewrite calls to |
I agree that registering only the service providers that are a subtype of the |
@cstancu I am trying to create a feature for that and here is my attempt (that's a rapid POC and absolutely not the final code as I do not know too much the stack yet). It allows to compile & execute your example, output is:
I would love to have your opinion on that. Every feedback is welcome. I can improve it to register only the service type if it is actually used. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java
index e54994cc113..6d53d91eb0b 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java
@@ -52,8 +52,9 @@ import com.oracle.svm.core.util.VMError;
*/
public final class Resources {
- static class ResourcesSupport {
- final Map<String, List<byte[]>> resources = new HashMap<>();
+ // TODO: find a better way to do that
+ public static class ResourcesSupport {
+ public final Map<String, List<byte[]>> resources = new HashMap<>();
}
private Resources() {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceFeature.java
new file mode 100644
index 00000000000..f902c1991c3
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceFeature.java
@@ -0,0 +1,67 @@
+package com.oracle.svm.hosted;
+
+import com.oracle.svm.core.annotate.AutomaticFeature;
+import com.oracle.svm.core.jdk.Resources;
+import com.oracle.svm.core.jdk.ResourcesFeature;
+import org.graalvm.nativeimage.Feature;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.RuntimeReflection;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+@AutomaticFeature
+public final class ServiceFeature implements Feature {
+ @Override
+ public List<Class<? extends Feature>> getRequiredFeatures() {
+ return Collections.singletonList(ResourcesFeature.class);
+ }
+
+ @Override
+ public void beforeAnalysis(BeforeAnalysisAccess access) {
+ FeatureImpl.BeforeAnalysisAccessImpl config = (FeatureImpl.BeforeAnalysisAccessImpl) access;
+
+ ImageClassLoader classLoader = config.getImageClassLoader();
+
+ for (Map.Entry<String, List<byte[]>> entry : ImageSingletons.lookup(Resources.ResourcesSupport.class).resources.entrySet()) {
+ String fileName = entry.getKey();
+
+ if (fileName.startsWith("META-INF/services/")) {
+ // TODO: `.get(0)`
+ parseProviderConfigurationFile(classLoader, entry.getValue().get(0));
+ }
+ }
+ }
+
+ private void parseProviderConfigurationFile(ImageClassLoader classLoader, byte[] content) {
+ // TODO: remove doc
+ // From https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#The_META-INF_directory
+ // A service provider identifies itself by placing a provider-configuration file in the resource directory META-INF/services.
+ // The file's name should consist of the fully-qualified name of the abstract service class.
+ // The file should contain a newline-separated list of unique concrete provider-class names.
+ // Space and tab characters, as well as blank lines, are ignored.
+ // The comment character is '#' (0x23); on each line all characters following the first comment character
+ // are ignored. The file must be encoded in UTF-8.
+
+ // TODO: better regex
+ String[] lines = new String(content, StandardCharsets.UTF_8).split("\n");
+
+ for (String line : lines) {
+ // TODO: combine regexes
+ String className = line
+ .replaceAll("[ \t]", "")
+ .replaceAll("#.*", "");
+
+
+ Class<?> clazz = classLoader.findClassByName(className, false);
+
+ // TODO: remove me
+ System.out.println("FOUND: " + className);
+
+ RuntimeReflection.register(clazz);
+ RuntimeReflection.register(clazz.getDeclaredConstructors());
+ }
+ }
+} |
With automatic detection: #566 |
This feature, i.e., automatically registering services for run-time lookup using ServiceLoader, is now enabled by default and can be disabled using |
Currently all service providers declared in
META-INF/services
resources need to be manually registered for reflection. To improve usability this should be automated.Registration for reflection is needed because the service providers are loaded with
Class.forName()
and are instantiated withClass.newInstance()
. A typical reflection configuration for service providers looks like this:,i.e., it specifies the class and the no-parameter constructor. A complete example of registering service providers can be found in this demo project.
The automatic registration can be achieved by parsing
META-INF/services
at native image build time and using the SubstrateVM API to register the classes.Automatic registration should be limited to the
META-INF/services
resources explicitely registered using the-H:IncludeResources=<resource-path-regexp>
option to avoid polution of the image with all resources on the classpath.SubstrateVM provides the
Feature
mechanism to extend its internals with plugable components. For reflection registration theRuntimeReflection
API can be invoked from aFeature
. The reflection documentation provides details about the reflection API. An example of registering elements for reflection from aFeature
can be found inGsonFeature
.(Make sure to read the contributing guidelines if you would like to take on this issue.)
The text was updated successfully, but these errors were encountered: