diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index fd45783d107b..db5dc36fdd3d 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -1017,6 +1017,7 @@ supr java.lang.Object CLSS public final org.graalvm.nativeimage.hosted.RuntimeResourceAccess meth public static void addResource(java.lang.Module,java.lang.String) +meth public static void addResource(java.lang.Module,java.lang.String,byte[]) meth public static void addResourceBundle(java.lang.Module,java.lang.String,java.util.Locale[]) meth public static void addResourceBundle(java.lang.Module,java.lang.String) supr java.lang.Object diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java index 6f62b7a194b2..355a4f06e4f7 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java @@ -67,10 +67,28 @@ public final class RuntimeResourceAccess { * @since 22.3 */ public static void addResource(Module module, String resourcePath) { + Objects.requireNonNull(module); + Objects.requireNonNull(resourcePath); ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), withModuleName(module, Pattern.quote(resourcePath))); } + /** + * Inject a Java resource at {@code resourcePath} in {@code module} with the specified + * {@code resourceContent}. At runtime the resource can be accessed as if it was part of the + * original application. If the given {@code module} is unnamed, the resource is placed on the + * classpath instead. + * + * @since 22.3 + */ + public static void addResource(Module module, String resourcePath, byte[] resourceContent) { + Objects.requireNonNull(module); + Objects.requireNonNull(resourcePath); + Objects.requireNonNull(resourceContent); + ImageSingletons.lookup(RuntimeResourceSupport.class).injectResource( + module, resourcePath, resourceContent); + } + /** * Make Java ResourceBundle that is specified by a {@code baseBundleName} and {@code locales} * from module {@code module} available at run time. If the given {@code module} is unnamed, the diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java index b98cb47b6d1c..68242b311d69 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java @@ -46,6 +46,8 @@ public interface RuntimeResourceSupport { void addResources(ConfigurationCondition condition, String pattern); + void injectResource(Module module, String resourcePath, byte[] resourceContent); + void ignoreResources(ConfigurationCondition condition, String pattern); void addResourceBundles(ConfigurationCondition condition, String name); diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index ecf08e03e429..b5ac2b3cc134 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -13,6 +13,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-15630) Allow multiple classes with the same name from different class loaders. * (GR-40198) Introduce public API for programmatic JNI / Resource / Proxy / Serialization registration from Feature classes during the image build. * (GR-38909) Moved strictly-internal annotation classes (e.g. @AlwaysInline, @NeverInline, @Uninterruptible, ...) out of com.oracle.svm.core.annotate. Moved remaining annotation classes to org.graalvm.sdk module. +* (GR-40906) Add RuntimeResourceAccess#addResource(Module module, String resourcePath, byte[] resource) API method that allows injecting resources into images ## Version 22.2.0 * (GR-20653) Re-enable the usage of all CPU features for JIT compilation on AMD64. diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 1d5fe077170b..d949092c7ccb 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -93,6 +93,10 @@ public void addResources(ConfigurationCondition condition, String pattern) { addedResources.add(pattern); } + @Override + public void injectResource(Module module, String resourcePath, byte[] resourceContent) { + } + @Override public void ignoreResources(ConfigurationCondition condition, String pattern) { ignoredResources.add(pattern); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index b0845a4db275..0fd6d5e53301 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -35,15 +35,16 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; -import com.oracle.svm.core.configure.ConfigurationParser; -import com.oracle.svm.core.configure.ResourceConfigurationParser; import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.json.JsonPrinter; import com.oracle.svm.configure.json.JsonWriter; import com.oracle.svm.core.configure.ConditionalElement; +import com.oracle.svm.core.configure.ConfigurationParser; +import com.oracle.svm.core.configure.ResourceConfigurationParser; import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.util.VMError; public final class ResourceConfiguration extends ConfigurationBase { @@ -62,6 +63,11 @@ public void addResources(ConfigurationCondition condition, String pattern) { configuration.addResourcePattern(condition, pattern); } + @Override + public void injectResource(Module module, String resourcePath, byte[] resourceContent) { + VMError.shouldNotReachHere("Resource injection is only supported via Feature implementation"); + } + @Override public void ignoreResources(ConfigurationCondition condition, String pattern) { configuration.ignoreResourcePattern(condition, pattern); 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 3596120035e5..d83c44134f93 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 @@ -41,10 +41,10 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jdk.resources.NativeImageResourcePath; import com.oracle.svm.core.jdk.resources.ResourceStorageEntry; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; @@ -86,14 +86,16 @@ public static byte[] inputStreamToByteArray(InputStream is) { } private static void addEntry(String moduleName, String resourceName, boolean isDirectory, byte[] data, boolean fromJar) { - Resources support = singleton(); - Pair key = Pair.create(moduleName, resourceName); - ResourceStorageEntry entry = support.resources.get(key); - if (entry == null) { - entry = new ResourceStorageEntry(isDirectory, fromJar); - support.resources.put(key, entry); + var resources = singleton().resources; + synchronized (resources) { + Pair key = Pair.create(moduleName, resourceName); + ResourceStorageEntry entry = resources.get(key); + if (entry == null) { + entry = new ResourceStorageEntry(isDirectory, fromJar); + resources.put(key, entry); + } + entry.getData().add(data); } - entry.getData().add(data); } @Platforms(Platform.HOSTED_ONLY.class) @@ -111,6 +113,11 @@ public static void registerResource(String moduleName, String resourceName, Inpu registerResource(moduleName, resourceName, is, true); } + @Platforms(Platform.HOSTED_ONLY.class) + public static void registerResource(String moduleName, String resourceName, byte[] resourceContent) { + addEntry(moduleName, resourceName, false, resourceContent, true); + } + @Platforms(Platform.HOSTED_ONLY.class) public static void registerResource(String moduleName, String resourceName, InputStream is, boolean fromJar) { addEntry(moduleName, resourceName, false, inputStreamToByteArray(is), fromJar); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index f75e559b5709..5966939e9ada 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -403,6 +403,7 @@ void processClassLoaderOptions() { private static void processListModulesOption(ModuleLayer layer) { Class launcherHelperClass = ReflectionUtil.lookupClass(false, "sun.launcher.LauncherHelper"); + Method initOutputMethod = ReflectionUtil.lookupMethod(launcherHelperClass, "initOutput", boolean.class); Method showModuleMethod = ReflectionUtil.lookupMethod(launcherHelperClass, "showModule", ModuleReference.class); boolean first = true; @@ -411,6 +412,11 @@ private static void processListModulesOption(ModuleLayer layer) { .sorted(Comparator.comparing(ResolvedModule::name)) .collect(Collectors.toList()); if (first) { + try { + initOutputMethod.invoke(null, false); + } catch (ReflectiveOperationException e) { + throw VMError.shouldNotReachHere("Unable to use " + initOutputMethod + " to set printing with " + showModuleMethod + " to System.out.", e); + } first = false; } else if (!resolvedModules.isEmpty()) { System.out.println(); @@ -419,7 +425,7 @@ private static void processListModulesOption(ModuleLayer layer) { try { showModuleMethod.invoke(null, resolvedModule.reference()); } catch (ReflectiveOperationException e) { - throw VMError.shouldNotReachHere("Unable to " + showModuleMethod + " for printing list of modules.", e); + throw VMError.shouldNotReachHere("Unable to use " + showModuleMethod + " for printing list of modules.", e); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index f9fb8dda78e8..ec633a06163e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -52,6 +52,7 @@ import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.configure.ResourceConfigurationParser; import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributes; @@ -60,7 +61,6 @@ import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.config.ConfigurationParserUtils; @@ -133,6 +133,12 @@ public void addResources(ConfigurationCondition condition, String pattern) { }); } + @Override + public void injectResource(Module module, String resourcePath, byte[] resourceContent) { + var moduleName = module.isNamed() ? module.getName() : null; + Resources.registerResource(moduleName, resourcePath, resourceContent); + } + @Override public void ignoreResources(ConfigurationCondition condition, String pattern) { if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { @@ -261,13 +267,11 @@ public void duringAnalysis(DuringAnalysisAccess access) { access.requireAnalysisIteration(); - DebugContext debugContext = ((DuringAnalysisAccessImpl) access).getDebugContext(); ResourcePattern[] includePatterns = compilePatterns(resourcePatternWorkSet); ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); + DebugContext debugContext = ((DuringAnalysisAccessImpl) access).getDebugContext(); ResourceCollectorImpl collector = new ResourceCollectorImpl(debugContext, includePatterns, excludePatterns, includedResourcesModules); - ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); - resourcePatternWorkSet.clear(); }