Skip to content

Commit 7cacf08

Browse files
Apply patch from mainline hash: 166210719e
1 parent 86c343e commit 7cacf08

File tree

4 files changed

+162
-22
lines changed

4 files changed

+162
-22
lines changed

src/java.base/share/classes/jdk/internal/jimage/ImageReader.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,45 @@ public Node findNode(String name) throws IOException {
137137
return reader.findNode(name);
138138
}
139139

140+
/**
141+
* Returns a resource node in the given module, or null if no resource of
142+
* that name exists.
143+
*
144+
* <p>This is equivalent to:
145+
* <pre>{@code
146+
* findNode("/modules/" + moduleName + "/" + resourcePath)
147+
* }</pre>
148+
* but more performant, and returns {@code null} for directories.
149+
*
150+
* @param moduleName The module name of the requested resource.
151+
* @param resourcePath Trailing module-relative resource path, not starting
152+
* with {@code '/'}.
153+
*/
154+
public Node findResourceNode(String moduleName, String resourcePath)
155+
throws IOException {
156+
ensureOpen();
157+
return reader.findResourceNode(moduleName, resourcePath);
158+
}
159+
160+
/**
161+
* Returns whether a resource exists in the given module.
162+
*
163+
* <p>This is equivalent to:
164+
* <pre>{@code
165+
* findResourceNode(moduleName, resourcePath) != null
166+
* }</pre>
167+
* but more performant, and will not create or cache new nodes.
168+
*
169+
* @param moduleName The module name of the resource being tested for.
170+
* @param resourcePath Trailing module-relative resource path, not starting
171+
* with {@code '/'}.
172+
*/
173+
public boolean containsResource(String moduleName, String resourcePath)
174+
throws IOException {
175+
ensureOpen();
176+
return reader.containsResource(moduleName, resourcePath);
177+
}
178+
140179
/**
141180
* Returns a copy of the content of a resource node. The buffer returned by
142181
* this method is not cached by the node, and each call returns a new array
@@ -276,10 +315,7 @@ public void close(ImageReader image) throws IOException {
276315
* Returns a node with the given name, or null if no resource or directory of
277316
* that name exists.
278317
*
279-
* <p>This is the only public API by which anything outside this class can access
280-
* {@code Node} instances either directly, or by resolving symbolic links.
281-
*
282-
* <p>Note also that there is no reentrant calling back to this method from within
318+
* <p>Note that there is no reentrant calling back to this method from within
283319
* the node handling code.
284320
*
285321
* @param name an absolute, {@code /}-separated path string, prefixed with either
@@ -291,6 +327,9 @@ synchronized Node findNode(String name) {
291327
// We cannot get the root paths ("/modules" or "/packages") here
292328
// because those nodes are already in the nodes cache.
293329
if (name.startsWith(MODULES_ROOT + "/")) {
330+
// This may perform two lookups, one for a directory (in
331+
// "/modules/...") and one for a non-prefixed resource
332+
// (with "/modules" removed).
294333
node = buildModulesNode(name);
295334
} else if (name.startsWith(PACKAGES_ROOT + "/")) {
296335
node = buildPackagesNode(name);
@@ -307,6 +346,55 @@ synchronized Node findNode(String name) {
307346
return node;
308347
}
309348

349+
/**
350+
* Returns a resource node in the given module, or null if no resource of
351+
* that name exists.
352+
*
353+
* <p>Note that there is no reentrant calling back to this method from within
354+
* the node handling code.
355+
*/
356+
Node findResourceNode(String moduleName, String resourcePath) {
357+
// Unlike findNode(), this method makes only one lookup in the
358+
// underlying jimage, but can only reliably return resource nodes.
359+
if (moduleName.indexOf('/') >= 0) {
360+
throw new IllegalArgumentException("invalid module name: " + moduleName);
361+
}
362+
String nodeName = MODULES_ROOT + "/" + moduleName + "/" + resourcePath;
363+
// Synchronize as tightly as possible to reduce locking contention.
364+
synchronized (this) {
365+
Node node = nodes.get(nodeName);
366+
if (node == null) {
367+
ImageLocation loc = findLocation(moduleName, resourcePath);
368+
if (loc != null && isResource(loc)) {
369+
node = newResource(nodeName, loc);
370+
nodes.put(node.getName(), node);
371+
}
372+
return node;
373+
} else {
374+
return node.isResource() ? node : null;
375+
}
376+
}
377+
}
378+
379+
/**
380+
* Returns whether a resource exists in the given module.
381+
*
382+
* <p>This method is expected to be called frequently for resources
383+
* which do not exist in the given module (e.g. as part of classpath
384+
* search). As such, it skips checking the nodes cache and only checks
385+
* for an entry in the jimage file, as this is faster if the resource
386+
* is not present. This also means it doesn't need synchronization.
387+
*/
388+
boolean containsResource(String moduleName, String resourcePath) {
389+
if (moduleName.indexOf('/') >= 0) {
390+
throw new IllegalArgumentException("invalid module name: " + moduleName);
391+
}
392+
// If the given module name is 'modules', then 'isResource()'
393+
// returns false to prevent false positives.
394+
ImageLocation loc = findLocation(moduleName, resourcePath);
395+
return loc != null && isResource(loc);
396+
}
397+
310398
/**
311399
* Builds a node in the "/modules/..." namespace.
312400
*

src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -414,26 +414,18 @@ private static class SystemModuleReader implements ModuleReader {
414414
* Returns {@code true} if the given resource exists, {@code false}
415415
* if not found.
416416
*/
417-
private boolean containsResource(String resourcePath) throws IOException {
418-
Objects.requireNonNull(resourcePath);
417+
private boolean containsResource(String module, String name) throws IOException {
418+
Objects.requireNonNull(name);
419419
if (closed)
420420
throw new IOException("ModuleReader is closed");
421421
ImageReader imageReader = SystemImage.reader();
422-
if (imageReader != null) {
423-
ImageReader.Node node = imageReader.findNode("/modules" + resourcePath);
424-
return node != null && node.isResource();
425-
} else {
426-
// not an images build
427-
return false;
428-
}
422+
return imageReader != null && imageReader.containsResource(module, name);
429423
}
430424

431425
@Override
432426
public Optional<URI> find(String name) throws IOException {
433-
Objects.requireNonNull(name);
434-
String resourcePath = "/" + module + "/" + name;
435-
if (containsResource(resourcePath)) {
436-
URI u = JNUA.create("jrt", resourcePath);
427+
if (containsResource(module, name)) {
428+
URI u = JNUA.create("jrt", "/" + module + "/" + name);
437429
return Optional.of(u);
438430
} else {
439431
return Optional.empty();
@@ -465,9 +457,7 @@ private ImageReader.Node findResource(ImageReader reader, String name) throws IO
465457
if (closed) {
466458
throw new IOException("ModuleReader is closed");
467459
}
468-
String nodeName = "/modules/" + module + "/" + name;
469-
ImageReader.Node node = reader.findNode(nodeName);
470-
return (node != null && node.isResource()) ? node : null;
460+
return reader.findResourceNode(module, name);
471461
}
472462

473463
@Override

src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ private synchronized Node connectResourceNode() throws IOException {
8787
if (module.isEmpty() || path == null) {
8888
throw new IOException("cannot connect to jrt:/" + module);
8989
}
90-
Node node = READER.findNode("/modules/" + module + "/" + path);
91-
if (node == null || !node.isResource()) {
90+
Node node = READER.findResourceNode(module, path);
91+
if (node == null) {
9292
throw new IOException(module + "/" + path + " not found");
9393
}
9494
this.resourceNode = node;

test/jdk/jdk/internal/jimage/ImageReaderTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.junit.jupiter.api.Test;
3131
import org.junit.jupiter.api.TestInstance;
3232
import org.junit.jupiter.params.ParameterizedTest;
33+
import org.junit.jupiter.params.provider.CsvSource;
3334
import org.junit.jupiter.params.provider.ValueSource;
3435
import tests.Helper;
3536
import tests.JImageGenerator;
@@ -43,9 +44,11 @@
4344
import java.util.stream.Collectors;
4445

4546
import static org.junit.jupiter.api.Assertions.assertEquals;
47+
import static org.junit.jupiter.api.Assertions.assertFalse;
4648
import static org.junit.jupiter.api.Assertions.assertNotNull;
4749
import static org.junit.jupiter.api.Assertions.assertNull;
4850
import static org.junit.jupiter.api.Assertions.assertSame;
51+
import static org.junit.jupiter.api.Assertions.assertThrows;
4952
import static org.junit.jupiter.api.Assertions.assertTrue;
5053
import static org.junit.jupiter.api.Assertions.fail;
5154
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@@ -122,6 +125,65 @@ public void testModuleResources() throws IOException {
122125
}
123126
}
124127

128+
@ParameterizedTest
129+
@CsvSource(delimiter = ':', value = {
130+
"modfoo:com/foo/Alpha.class",
131+
"modbar:com/bar/One.class",
132+
})
133+
public void testResource_present(String modName, String resPath) throws IOException {
134+
try (ImageReader reader = ImageReader.open(image)) {
135+
assertNotNull(reader.findResourceNode(modName, resPath));
136+
assertTrue(reader.containsResource(modName, resPath));
137+
138+
String canonicalNodeName = "/modules/" + modName + "/" + resPath;
139+
Node node = reader.findNode(canonicalNodeName);
140+
assertTrue(node != null && node.isResource());
141+
}
142+
}
143+
144+
@ParameterizedTest
145+
@CsvSource(delimiter = ':', value = {
146+
// Absolute resource names are not allowed.
147+
"modfoo:/com/bar/One.class",
148+
// Resource in wrong module.
149+
"modfoo:com/bar/One.class",
150+
"modbar:com/foo/Alpha.class",
151+
// Directories are not returned.
152+
"modfoo:com/foo",
153+
"modbar:com/bar",
154+
// JImage entries exist for these, but they are not resources.
155+
"modules:modfoo/com/foo/Alpha.class",
156+
"packages:com.foo/modfoo",
157+
// Empty module names/paths do not find resources.
158+
"'':modfoo/com/foo/Alpha.class",
159+
"modfoo:''"})
160+
public void testResource_absent(String modName, String resPath) throws IOException {
161+
try (ImageReader reader = ImageReader.open(image)) {
162+
assertNull(reader.findResourceNode(modName, resPath));
163+
assertFalse(reader.containsResource(modName, resPath));
164+
165+
// Non-existent resources names should either not be found,
166+
// or (in the case of directory nodes) not be resources.
167+
String canonicalNodeName = "/modules/" + modName + "/" + resPath;
168+
Node node = reader.findNode(canonicalNodeName);
169+
assertTrue(node == null || !node.isResource());
170+
}
171+
}
172+
173+
@ParameterizedTest
174+
@CsvSource(delimiter = ':', value = {
175+
// Don't permit module names to contain paths.
176+
"modfoo/com/bar:One.class",
177+
"modfoo/com:bar/One.class",
178+
"modules/modfoo/com:foo/Alpha.class",
179+
})
180+
public void testResource_invalid(String modName, String resPath) throws IOException {
181+
try (ImageReader reader = ImageReader.open(image)) {
182+
assertThrows(IllegalArgumentException.class, () -> reader.containsResource(modName, resPath));
183+
assertThrows(IllegalArgumentException.class, () -> reader.findResourceNode(modName, resPath));
184+
}
185+
}
186+
125187
@Test
126188
public void testPackageDirectories() throws IOException {
127189
try (ImageReader reader = ImageReader.open(image)) {

0 commit comments

Comments
 (0)