Skip to content

Commit 6aa6078

Browse files
committed
Throw exception for missing resource metadata
1 parent 86f363c commit 6aa6078

12 files changed

+330
-76
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package com.oracle.svm.core;
2626

27+
import java.io.IOException;
2728
import java.io.InputStream;
2829
import java.net.URI;
2930
import java.util.List;
@@ -56,6 +57,10 @@ public interface ResourceCollector {
5657
void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar);
5758

5859
void addDirectoryResource(Module module, String dir, String content, boolean fromJar);
60+
61+
void registerNegativeQuery(Module module, String resourceName);
62+
63+
void registerIOException(Module module, String resourceName, IOException e);
5964
}
6065

6166
public abstract void collectResources(ResourceCollector resourceCollector);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ public Enum<?>[] getEnumConstantsShared() {
805805
@Substitute
806806
public InputStream getResourceAsStream(String resourceName) {
807807
String resolvedName = resolveName(resourceName);
808-
return Resources.createInputStream(module, resolvedName);
808+
return Resources.singleton().createInputStream(module, resolvedName);
809809
}
810810

811811
@KeepOriginal

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java

+189-49
Large diffs are not rendered by default.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ResourcesHelper.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public static Resource nameToResource(String resourceName) {
9090
}
9191

9292
public static Enumeration<Resource> nameToResources(String resourceName) {
93-
Enumeration<URL> urls = Resources.createURLs(resourceName);
93+
Enumeration<URL> urls = Resources.singleton().createURLs(resourceName);
9494
List<Resource> resourceURLs = new ArrayList<>();
9595
while (urls.hasMoreElements()) {
9696
resourceURLs.add(urlToResource(resourceName, urls.nextElement()));
@@ -99,11 +99,11 @@ public static Enumeration<Resource> nameToResources(String resourceName) {
9999
}
100100

101101
public static URL nameToResourceURL(String resourceName) {
102-
return Resources.createURL(resourceName);
102+
return Resources.singleton().createURL(resourceName);
103103
}
104104

105105
public static URL nameToResourceURL(Module module, String resourceName) {
106-
return Resources.createURL(module, resourceName);
106+
return Resources.singleton().createURL(module, resourceName);
107107
}
108108

109109
public static InputStream nameToResourceInputStream(String resourceName) throws IOException {
@@ -112,7 +112,7 @@ public static InputStream nameToResourceInputStream(String resourceName) throws
112112
}
113113

114114
public static List<URL> nameToResourceListURLs(String resourcesName) {
115-
Enumeration<URL> urls = Resources.createURLs(resourcesName);
115+
Enumeration<URL> urls = Resources.singleton().createURLs(resourcesName);
116116
List<URL> resourceURLs = new ArrayList<>();
117117
while (urls.hasMoreElements()) {
118118
resourceURLs.add(urls.nextElement());

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_Module.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ private InputStream getResourceAsStream(String resourceName) {
4747
if (resName.startsWith("/")) {
4848
resName = resName.substring(1);
4949
}
50-
ResourceStorageEntry res = Resources.get(SubstrateUtil.cast(this, Module.class), resName);
51-
return res == null ? null : new ByteArrayInputStream(res.getData().get(0));
50+
Object res = Resources.singleton().get(SubstrateUtil.cast(this, Module.class), resName, true);
51+
return res == null ? null : new ByteArrayInputStream(((ResourceStorageEntry) res).getData().get(0));
5252
}
5353

5454
@Substitute
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk.resources;
26+
27+
import org.graalvm.compiler.options.Option;
28+
import org.graalvm.compiler.options.OptionType;
29+
30+
import com.oracle.svm.core.option.HostedOptionKey;
31+
import com.oracle.svm.core.util.ExitStatus;
32+
33+
public final class MissingResourceMetadataException extends RuntimeException {
34+
private static final long serialVersionUID = 1L;
35+
36+
public static class Options {
37+
@Option(help = "Enable termination caused by missing metadata.")//
38+
public static final HostedOptionKey<Boolean> ExitOnMissingMetadata = new HostedOptionKey<>(false);
39+
40+
@Option(help = "Simulate exiting the program with an exception instead of calling System.exit() (for testing)")//
41+
public static final HostedOptionKey<Boolean> ExitWithException = new HostedOptionKey<>(false);
42+
43+
@Option(help = "Throw Native Image-specific exceptions when encountering an unregistered reflection call.", type = OptionType.User)//
44+
public static final HostedOptionKey<Boolean> ThrowMissingMetadataExceptions = new HostedOptionKey<>(false);
45+
}
46+
47+
private MissingResourceMetadataException(String message) {
48+
super(message);
49+
}
50+
51+
public static MissingResourceMetadataException missingResource(String resourcePath) {
52+
MissingResourceMetadataException exception = new MissingResourceMetadataException(
53+
"Resource at path " + resourcePath + " has not been registered as reachable. To ensure this resource is available at run time, you need to add it to the resource metadata.");
54+
if (MissingResourceMetadataException.Options.ExitOnMissingMetadata.getValue()) {
55+
exitOnMissingMetadata(exception);
56+
}
57+
return exception;
58+
}
59+
60+
private static void exitOnMissingMetadata(MissingResourceMetadataException exception) {
61+
if (Options.ExitWithException.getValue()) {
62+
throw new ExitException(exception);
63+
} else {
64+
exception.printStackTrace(System.out);
65+
System.exit(ExitStatus.MISSING_METADATA.getValue());
66+
}
67+
}
68+
69+
public static final class ExitException extends Error {
70+
private static final long serialVersionUID = 1L;
71+
72+
private ExitException(Throwable cause) {
73+
super(cause);
74+
}
75+
}
76+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystemUtil.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ private NativeImageResourceFileSystemUtil() {
3636
}
3737

3838
public static byte[] getBytes(String resourceName, boolean readOnly) {
39-
ResourceStorageEntry entry = Resources.get(resourceName);
39+
Object entry = Resources.singleton().get(resourceName, true);
4040
if (entry == null) {
4141
return new byte[0];
4242
}
43-
byte[] bytes = entry.getData().get(0);
43+
byte[] bytes = ((ResourceStorageEntry) entry).getData().get(0);
4444
if (readOnly) {
4545
return bytes;
4646
} else {
@@ -49,11 +49,11 @@ public static byte[] getBytes(String resourceName, boolean readOnly) {
4949
}
5050

5151
public static int getSize(String resourceName) {
52-
ResourceStorageEntry entry = Resources.get(resourceName);
52+
Object entry = Resources.singleton().get(resourceName, true);
5353
if (entry == null) {
5454
return 0;
5555
} else {
56-
return entry.getData().get(0).length;
56+
return ((ResourceStorageEntry) entry).getData().get(0).length;
5757
}
5858
}
5959

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ public void connect() {
7474
String resourceName = urlPath.substring(1);
7575

7676
Module module = hostNameOrNull != null ? ModuleLayer.boot().findModule(hostNameOrNull).orElse(null) : null;
77-
ResourceStorageEntry entry = Resources.get(module, resourceName);
77+
Object entry = Resources.singleton().get(module, resourceName, true);
7878
if (entry != null) {
79-
List<byte[]> bytes = entry.getData();
80-
isDirectory = entry.isDirectory();
79+
ResourceStorageEntry resourceStorageEntry = (ResourceStorageEntry) entry;
80+
List<byte[]> bytes = resourceStorageEntry.getData();
81+
isDirectory = resourceStorageEntry.isDirectory();
8182
String urlRef = url.getRef();
8283
int index = 0;
8384
if (urlRef != null) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,21 @@ private static void collectResourceFromModule(ResourceCollector resourceCollecto
144144
for (String resName : foundResources) {
145145
Optional<InputStream> content = moduleReader.open(resName);
146146
if (content.isEmpty()) {
147+
resourceCollector.registerNegativeQuery(info.module, resName);
147148
continue;
148149
}
149150
try (InputStream is = content.get()) {
150151
resourceCollector.addResource(info.module, resName, is, false);
152+
} catch (IOException resourceException) {
153+
resourceCollector.registerIOException(info.module, resName, resourceException);
151154
}
152155
}
153156
} catch (IOException e) {
154157
throw VMError.shouldNotReachHere(e);
155158
}
156159
}
157160

158-
private static void scanDirectory(Path root, ResourceCollector collector) throws IOException {
161+
private static void scanDirectory(Path root, ResourceCollector collector) {
159162
Map<String, List<String>> matchedDirectoryResources = new HashMap<>();
160163
Set<String> allEntries = new HashSet<>();
161164

@@ -183,11 +186,15 @@ private static void scanDirectory(Path root, ResourceCollector collector) throws
183186
filtered = filtered.filter(Predicate.not(ClassUtil.CLASS_MODULE_PATH_EXCLUDE_DIRECTORIES::contains));
184187
}
185188
filtered.forEach(queue::push);
189+
} catch (IOException resourceException) {
190+
collector.registerIOException(null, relativeFilePath, resourceException);
186191
}
187192
} else {
188193
if (collector.isIncluded(null, relativeFilePath, Path.of(relativeFilePath).toUri())) {
189194
try (InputStream is = Files.newInputStream(entry)) {
190195
collector.addResource(null, relativeFilePath, is, false);
196+
} catch (IOException resourceException) {
197+
collector.registerIOException(null, relativeFilePath, resourceException);
191198
}
192199
}
193200
}
@@ -199,6 +206,8 @@ private static void scanDirectory(Path root, ResourceCollector collector) throws
199206
List<String> dirContent = matchedDirectoryResources.get(key);
200207
if (dirContent != null && !dirContent.contains(entry)) {
201208
dirContent.add(entry.substring(last + 1));
209+
} else if (dirContent == null) {
210+
collector.registerNegativeQuery(null, key);
202211
}
203212
}
204213

@@ -223,6 +232,8 @@ private static void scanJar(Path jarPath, ResourceCollector collector) throws IO
223232
if (collector.isIncluded(null, entry.getName(), jarPath.toUri())) {
224233
try (InputStream is = jf.getInputStream(entry)) {
225234
collector.addResource(null, entry.getName(), is, true);
235+
} catch (IOException resourceException) {
236+
collector.registerIOException(null, entry.getName(), resourceException);
226237
}
227238
}
228239
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HeapBreakdownProvider.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,12 @@ protected void calculate(BeforeImageWriteAccessImpl access) {
146146
/* Extract byte[] for resources. */
147147
long resourcesByteArraySize = 0;
148148
int resourcesByteArrayCount = 0;
149-
for (ResourceStorageEntry resourceList : Resources.singleton().resources()) {
150-
for (byte[] resource : resourceList.getData()) {
151-
resourcesByteArraySize += objectLayout.getArraySize(JavaKind.Byte, resource.length, true);
152-
resourcesByteArrayCount++;
149+
for (Object resourceList : Resources.singleton().resources()) {
150+
if (resourceList instanceof ResourceStorageEntry resourceStorageEntry) {
151+
for (byte[] resource : resourceStorageEntry.getData()) {
152+
resourcesByteArraySize += objectLayout.getArraySize(JavaKind.Byte, resource.length, true);
153+
resourcesByteArrayCount++;
154+
}
153155
}
154156
}
155157
ProgressReporter reporter = ProgressReporter.singleton();

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR;
2929

30+
import java.io.IOException;
3031
import java.io.InputStream;
3132
import java.lang.reflect.InvocationTargetException;
3233
import java.lang.reflect.Method;
@@ -156,7 +157,7 @@ public void addResources(ConfigurationCondition condition, String pattern) {
156157

157158
@Override
158159
public void injectResource(Module module, String resourcePath, byte[] resourceContent) {
159-
Resources.registerResource(module, resourcePath, resourceContent);
160+
Resources.singleton().registerResource(module, resourcePath, resourceContent);
160161
}
161162

162163
@Override
@@ -308,6 +309,16 @@ public void addResource(Module module, String resourceName, InputStream resource
308309
public void addDirectoryResource(Module module, String dir, String content, boolean fromJar) {
309310
registerDirectoryResource(debugContext, module, dir, content, fromJar);
310311
}
312+
313+
@Override
314+
public void registerIOException(Module module, String resourceName, IOException e) {
315+
Resources.singleton().registerIOException(module, resourceName, e);
316+
}
317+
318+
@Override
319+
public void registerNegativeQuery(Module module, String resourceName) {
320+
Resources.singleton().registerNegativeQuery(module, resourceName);
321+
}
311322
}
312323

313324
@Override
@@ -321,7 +332,13 @@ public void duringAnalysis(DuringAnalysisAccess access) {
321332

322333
DuringAnalysisAccessImpl duringAnalysisAccess = ((DuringAnalysisAccessImpl) access);
323334
ResourcePattern[] includePatterns = compilePatterns(resourcePatternWorkSet);
335+
for (ResourcePattern resourcePattern : includePatterns) {
336+
Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern);
337+
}
324338
ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns);
339+
for (ResourcePattern resourcePattern : excludePatterns) {
340+
Resources.singleton().registerExcludePattern(resourcePattern.moduleName, resourcePattern.pattern);
341+
}
325342
DebugContext debugContext = duringAnalysisAccess.getDebugContext();
326343
ResourceCollectorImpl collector = new ResourceCollectorImpl(debugContext, includePatterns, excludePatterns, duringAnalysisAccess.bb.getHeartbeatCallback());
327344
try {
@@ -399,7 +416,7 @@ private static void registerResource(DebugContext debugContext, Module module, S
399416
try (DebugContext.Scope s = debugContext.scope("registerResource")) {
400417
String moduleNamePrefix = module == null ? "" : module.getName() + ":";
401418
debugContext.log(DebugContext.VERBOSE_LEVEL, "ResourcesFeature: registerResource: %s%s", moduleNamePrefix, resourceName);
402-
Resources.registerResource(module, resourceName, resourceStream, fromJar);
419+
Resources.singleton().registerResource(module, resourceName, resourceStream, fromJar);
403420
}
404421
}
405422

@@ -408,7 +425,7 @@ private static void registerDirectoryResource(DebugContext debugContext, Module
408425
try (DebugContext.Scope s = debugContext.scope("registerResource")) {
409426
String moduleNamePrefix = module == null ? "" : module.getName() + ":";
410427
debugContext.log(DebugContext.VERBOSE_LEVEL, "ResourcesFeature: registerResource: %s%s", moduleNamePrefix, dir);
411-
Resources.registerDirectoryResource(module, dir, content, fromJar);
428+
Resources.singleton().registerDirectoryResource(module, dir, content, fromJar);
412429
}
413430
}
414431

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/ImageHeapConnectedComponentsPrinter.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,13 @@ private static EnumMap<ObjectReachabilityGroup, GroupEntry> groupObjectsByReacha
145145
}
146146

147147
private static void markResources(NativeImageHeap heap) {
148-
for (ResourceStorageEntry value : Resources.singleton().resources()) {
149-
for (byte[] arr : value.getData()) {
150-
ObjectInfo info = heap.getObjectInfo(arr);
151-
if (info != null) {
152-
heap.objectReachabilityInfo.get(info).addReason(HeapInclusionReason.Resource);
148+
for (Object value : Resources.singleton().resources()) {
149+
if (value instanceof ResourceStorageEntry resourceStorageEntry) {
150+
for (byte[] arr : resourceStorageEntry.getData()) {
151+
ObjectInfo info = heap.getObjectInfo(arr);
152+
if (info != null) {
153+
heap.objectReachabilityInfo.get(info).addReason(HeapInclusionReason.Resource);
154+
}
153155
}
154156
}
155157
}

0 commit comments

Comments
 (0)