Skip to content

Commit e71f13e

Browse files
committed
Fix resource URL handling
1 parent b53f6fc commit e71f13e

File tree

3 files changed

+87
-32
lines changed

3 files changed

+87
-32
lines changed

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

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
import java.io.InputStream;
3030
import java.net.MalformedURLException;
3131
import java.net.URL;
32-
import java.net.URLConnection;
33-
import java.net.URLStreamHandler;
3432
import java.nio.charset.StandardCharsets;
3533
import java.util.ArrayList;
3634
import java.util.Collections;
@@ -47,7 +45,6 @@
4745
import com.oracle.svm.core.annotate.AutomaticFeature;
4846
import com.oracle.svm.core.jdk.resources.NativeImageResourcePath;
4947
import com.oracle.svm.core.jdk.resources.ResourceStorageEntry;
50-
import com.oracle.svm.core.jdk.resources.ResourceURLConnection;
5148
import com.oracle.svm.core.util.ImageHeapMap;
5249
import com.oracle.svm.core.util.VMError;
5350

@@ -143,17 +140,11 @@ public static ResourceStorageEntry get(String moduleName, String resourceName) {
143140

144141
private static URL createURL(String moduleName, String resourceName, int index) {
145142
try {
146-
return new URL(JavaNetSubstitutions.RESOURCE_PROTOCOL, moduleName, -1, resourceName,
147-
new URLStreamHandler() {
148-
@Override
149-
protected URLConnection openConnection(URL url) {
150-
return new ResourceURLConnection(url, index);
151-
}
152-
});
143+
String refPart = index != 0 ? '#' + Integer.toString(index) : "";
144+
return new URL(JavaNetSubstitutions.RESOURCE_PROTOCOL, moduleName, -1, '/' + resourceName + refPart);
153145
} catch (MalformedURLException ex) {
154146
throw new IllegalStateException(ex);
155147
}
156-
157148
}
158149

159150
public static URL createURL(String resourceName) {
@@ -195,18 +186,34 @@ public static Enumeration<URL> createURLs(String moduleName, String resourceName
195186
if (resourceName == null) {
196187
return null;
197188
}
198-
199189
String canonicalResourceName = toCanonicalForm(resourceName);
200-
ResourceStorageEntry entry = Resources.get(moduleName, canonicalResourceName);
201-
if (entry == null) {
190+
191+
List<URL> resourcesURLs = new ArrayList<>();
192+
193+
/* If moduleName was unspecified we have to consider all modules in the image */
194+
if (moduleName == null) {
195+
for (Module module : BootModuleLayerSupport.instance().getBootLayer().modules()) {
196+
ResourceStorageEntry entry = Resources.get(module.getName(), canonicalResourceName);
197+
addURLEntries(resourcesURLs, entry, module.getName(), canonicalResourceName);
198+
}
199+
}
200+
ResourceStorageEntry explicitEntry = Resources.get(moduleName, canonicalResourceName);
201+
addURLEntries(resourcesURLs, explicitEntry, moduleName, canonicalResourceName);
202+
203+
if (resourcesURLs.isEmpty()) {
202204
return Collections.emptyEnumeration();
203205
}
206+
return Collections.enumeration(resourcesURLs);
207+
}
208+
209+
private static void addURLEntries(List<URL> resourcesURLs, ResourceStorageEntry entry, String moduleName, String canonicalResourceName) {
210+
if (entry == null) {
211+
return;
212+
}
204213
int numberOfResources = entry.getData().size();
205-
List<URL> resourcesURLs = new ArrayList<>(numberOfResources);
206214
for (int index = 0; index < numberOfResources; index++) {
207215
resourcesURLs.add(createURL(moduleName, canonicalResourceName, index));
208216
}
209-
return Collections.enumeration(resourcesURLs);
210217
}
211218
}
212219

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

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,15 @@
3333
import java.net.URLConnection;
3434
import java.util.List;
3535

36+
import com.oracle.svm.core.jdk.JavaNetSubstitutions;
3637
import com.oracle.svm.core.jdk.Resources;
3738

3839
public class ResourceURLConnection extends URLConnection {
3940

40-
private final URL url;
41-
private final int index;
4241
private byte[] data;
4342

4443
public ResourceURLConnection(URL url) {
45-
this(url, 0);
46-
}
47-
48-
public ResourceURLConnection(URL url, int index) {
4944
super(url);
50-
this.url = url;
51-
this.index = index;
52-
}
53-
54-
private static String resolveName(String resourceName) {
55-
return resourceName.startsWith("/") ? resourceName.substring(1) : resourceName;
5645
}
5746

5847
@Override
@@ -62,12 +51,25 @@ public void connect() {
6251
}
6352
connected = true;
6453

65-
String resourceName = resolveName(url.getPath());
66-
String hostName = url.getHost();
67-
String hostNameOrNull = hostName != null && !hostName.isEmpty() ? hostName : null;
54+
String urlHost = url.getHost();
55+
String hostNameOrNull = urlHost != null && !urlHost.isEmpty() ? urlHost : null;
56+
String urlPath = url.getPath();
57+
if (urlPath.isEmpty()) {
58+
throw new IllegalArgumentException("Empty URL path not allowed in " + JavaNetSubstitutions.RESOURCE_PROTOCOL + " URL");
59+
}
60+
String resourceName = urlPath.substring(1);
6861
ResourceStorageEntry entry = Resources.get(hostNameOrNull, Resources.toCanonicalForm(resourceName));
6962
if (entry != null) {
7063
List<byte[]> bytes = entry.getData();
64+
String urlRef = url.getRef();
65+
int index = 0;
66+
if (urlRef != null) {
67+
try {
68+
index = Integer.valueOf(urlRef);
69+
} catch (NumberFormatException e) {
70+
throw new IllegalArgumentException("URL anchor '#" + urlRef + "' not allowed in " + JavaNetSubstitutions.RESOURCE_PROTOCOL + " URL");
71+
}
72+
}
7173
if (index < bytes.size()) {
7274
this.data = bytes.get(index);
7375
} else {
@@ -98,5 +100,4 @@ public long getContentLengthLong() {
98100
connect();
99101
return data != null ? data.length : -1L;
100102
}
101-
102103
}

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceFileSystemProviderTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import java.io.IOException;
2929
import java.io.InputStream;
30+
import java.net.MalformedURLException;
3031
import java.net.URI;
3132
import java.net.URISyntaxException;
3233
import java.net.URL;
@@ -48,11 +49,15 @@
4849
import java.nio.file.attribute.FileTime;
4950
import java.nio.file.spi.FileSystemProvider;
5051
import java.util.Collections;
52+
import java.util.Enumeration;
5153
import java.util.HashMap;
5254
import java.util.HashSet;
5355
import java.util.Iterator;
56+
import java.util.List;
5457
import java.util.Map;
5558
import java.util.Set;
59+
import java.util.stream.Collectors;
60+
import java.util.stream.StreamSupport;
5661

5762
import org.graalvm.nativeimage.ImageSingletons;
5863
import org.graalvm.nativeimage.hosted.Feature;
@@ -80,6 +85,11 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
8085
// Remove leading / for the resource patterns
8186
registry.addResources(ConfigurationCondition.alwaysTrue(), RESOURCE_FILE_1.substring(1));
8287
registry.addResources(ConfigurationCondition.alwaysTrue(), RESOURCE_FILE_2.substring(1));
88+
89+
/** Needed for {@link #testURLExternalFormEquivalence()} */
90+
for (Module module : ModuleLayer.boot().modules()) {
91+
registry.addResources(ConfigurationCondition.alwaysTrue(), module.getName() + ":" + "module-info.class");
92+
}
8393
}
8494
}
8595

@@ -588,4 +598,41 @@ public void moduleResourceURLAccess() {
588598
Assert.fail("IOException in url.openStream(): " + e.getMessage());
589599
}
590600
}
601+
602+
@Test
603+
public void testURLExternalFormEquivalence() {
604+
Enumeration<URL> urlEnumeration = null;
605+
try {
606+
urlEnumeration = ClassLoader.getSystemResources("module-info.class");
607+
} catch (IOException e) {
608+
Assert.fail("IOException in ClassLoader.getSystemResources(\"module-info.class\"): " + e.getMessage());
609+
}
610+
611+
Assert.assertNotNull(urlEnumeration);
612+
Enumeration<URL> finalVar = urlEnumeration;
613+
Iterable<URL> urlIterable = () -> finalVar.asIterator();
614+
List<URL> urlList = StreamSupport.stream(urlIterable.spliterator(), false).collect(Collectors.toList());
615+
Assert.assertTrue("ClassLoader.getSystemResources(\"module-info.class\") must return many module-info.class URLs",
616+
urlList.size() > 3);
617+
618+
for (URL url : urlList) {
619+
System.out.println(url);
620+
}
621+
622+
URL thirdEntry = urlList.get(2);
623+
String thirdEntryExternalForm = thirdEntry.toExternalForm();
624+
URL thirdEntryFromExternalForm = null;
625+
try {
626+
thirdEntryFromExternalForm = new URL(thirdEntryExternalForm);
627+
} catch (MalformedURLException e) {
628+
Assert.fail("Creating a new URL from the ExternalForm of another has to work: " + e.getMessage());
629+
}
630+
631+
try {
632+
boolean compareResult = compareTwoURLs(thirdEntry, thirdEntryFromExternalForm);
633+
Assert.assertTrue("Contents of original URL and one created from originals ExternalForm must be the same", compareResult);
634+
} catch (IOException e) {
635+
Assert.fail("Contents of original URL and one created from originals ExternalForm must be the same: " + e);
636+
}
637+
}
591638
}

0 commit comments

Comments
 (0)