120 }
121
122 private void requireOpen() {
123 if (closed) {
124 throw new IllegalStateException("image file closed");
125 }
126 }
127
128 /**
129 * Finds the node with the given name.
130 *
131 * @param name a node name of the form {@code "/modules/<module>/...} or
132 * {@code "/packages/<package>/...}.
133 * @return a node representing a resource, directory or symbolic link.
134 */
135 public Node findNode(String name) throws IOException {
136 ensureOpen();
137 return reader.findNode(name);
138 }
139
140 /**
141 * Returns a copy of the content of a resource node. The buffer returned by
142 * this method is not cached by the node, and each call returns a new array
143 * instance.
144 *
145 * @throws IOException if the content cannot be returned (including if the
146 * given node is not a resource node).
147 */
148 public byte[] getResource(Node node) throws IOException {
149 ensureOpen();
150 return reader.getResource(node);
151 }
152
153 /**
154 * Releases a (possibly cached) {@link ByteBuffer} obtained via
155 * {@link #getResourceBuffer(Node)}.
156 *
157 * <p>Note that no testing is performed to check whether the buffer about
158 * to be released actually came from a call to {@code getResourceBuffer()}.
159 */
259 synchronized (OPEN_FILES) {
260 if (!openers.remove(image)) {
261 throw new IOException("image file already closed");
262 }
263
264 if (openers.isEmpty()) {
265 close();
266 nodes.clear();
267
268 if (!OPEN_FILES.remove(this.getImagePath(), this)) {
269 throw new IOException("image file not found in open list");
270 }
271 }
272 }
273 }
274
275 /**
276 * Returns a node with the given name, or null if no resource or directory of
277 * that name exists.
278 *
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
283 * the node handling code.
284 *
285 * @param name an absolute, {@code /}-separated path string, prefixed with either
286 * "/modules" or "/packages".
287 */
288 synchronized Node findNode(String name) {
289 Node node = nodes.get(name);
290 if (node == null) {
291 // We cannot get the root paths ("/modules" or "/packages") here
292 // because those nodes are already in the nodes cache.
293 if (name.startsWith(MODULES_ROOT + "/")) {
294 node = buildModulesNode(name);
295 } else if (name.startsWith(PACKAGES_ROOT + "/")) {
296 node = buildPackagesNode(name);
297 }
298 if (node != null) {
299 nodes.put(node.getName(), node);
300 }
301 } else if (!node.isCompleted()) {
302 // Only directories can be incomplete.
303 assert node instanceof Directory : "Invalid incomplete node: " + node;
304 completeDirectory((Directory) node);
305 }
306 assert node == null || node.isCompleted() : "Incomplete node: " + node;
307 return node;
308 }
309
310 /**
311 * Builds a node in the "/modules/..." namespace.
312 *
313 * <p>Called by {@link #findNode(String)} if a {@code /modules/...} node
314 * is not present in the cache.
315 */
316 private Node buildModulesNode(String name) {
317 assert name.startsWith(MODULES_ROOT + "/") : "Invalid module node name: " + name;
318 // Returns null for non-directory resources, since the jimage name does not
319 // start with "/modules" (e.g. "/java.base/java/lang/Object.class").
320 ImageLocation loc = findLocation(name);
321 if (loc != null) {
322 assert name.equals(loc.getFullName()) : "Mismatched location for directory: " + name;
323 assert isModulesSubdirectory(loc) : "Invalid modules directory: " + name;
324 return completeModuleDirectory(newDirectory(name), loc);
325 }
326 // Now try the non-prefixed resource name, but be careful to avoid false
327 // positives for names like "/modules/modules/xxx" which could return a
328 // location of a directory entry.
329 loc = findLocation(name.substring(MODULES_ROOT.length()));
|
120 }
121
122 private void requireOpen() {
123 if (closed) {
124 throw new IllegalStateException("image file closed");
125 }
126 }
127
128 /**
129 * Finds the node with the given name.
130 *
131 * @param name a node name of the form {@code "/modules/<module>/...} or
132 * {@code "/packages/<package>/...}.
133 * @return a node representing a resource, directory or symbolic link.
134 */
135 public Node findNode(String name) throws IOException {
136 ensureOpen();
137 return reader.findNode(name);
138 }
139
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
179 /**
180 * Returns a copy of the content of a resource node. The buffer returned by
181 * this method is not cached by the node, and each call returns a new array
182 * instance.
183 *
184 * @throws IOException if the content cannot be returned (including if the
185 * given node is not a resource node).
186 */
187 public byte[] getResource(Node node) throws IOException {
188 ensureOpen();
189 return reader.getResource(node);
190 }
191
192 /**
193 * Releases a (possibly cached) {@link ByteBuffer} obtained via
194 * {@link #getResourceBuffer(Node)}.
195 *
196 * <p>Note that no testing is performed to check whether the buffer about
197 * to be released actually came from a call to {@code getResourceBuffer()}.
198 */
298 synchronized (OPEN_FILES) {
299 if (!openers.remove(image)) {
300 throw new IOException("image file already closed");
301 }
302
303 if (openers.isEmpty()) {
304 close();
305 nodes.clear();
306
307 if (!OPEN_FILES.remove(this.getImagePath(), this)) {
308 throw new IOException("image file not found in open list");
309 }
310 }
311 }
312 }
313
314 /**
315 * Returns a node with the given name, or null if no resource or directory of
316 * that name exists.
317 *
318 * <p>Note that there is no reentrant calling back to this method from within
319 * the node handling code.
320 *
321 * @param name an absolute, {@code /}-separated path string, prefixed with either
322 * "/modules" or "/packages".
323 */
324 synchronized Node findNode(String name) {
325 Node node = nodes.get(name);
326 if (node == null) {
327 // We cannot get the root paths ("/modules" or "/packages") here
328 // because those nodes are already in the nodes cache.
329 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).
333 node = buildModulesNode(name);
334 } else if (name.startsWith(PACKAGES_ROOT + "/")) {
335 node = buildPackagesNode(name);
336 }
337 if (node != null) {
338 nodes.put(node.getName(), node);
339 }
340 } else if (!node.isCompleted()) {
341 // Only directories can be incomplete.
342 assert node instanceof Directory : "Invalid incomplete node: " + node;
343 completeDirectory((Directory) node);
344 }
345 assert node == null || node.isCompleted() : "Incomplete node: " + node;
346 return node;
347 }
348
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
398 /**
399 * Builds a node in the "/modules/..." namespace.
400 *
401 * <p>Called by {@link #findNode(String)} if a {@code /modules/...} node
402 * is not present in the cache.
403 */
404 private Node buildModulesNode(String name) {
405 assert name.startsWith(MODULES_ROOT + "/") : "Invalid module node name: " + name;
406 // Returns null for non-directory resources, since the jimage name does not
407 // start with "/modules" (e.g. "/java.base/java/lang/Object.class").
408 ImageLocation loc = findLocation(name);
409 if (loc != null) {
410 assert name.equals(loc.getFullName()) : "Mismatched location for directory: " + name;
411 assert isModulesSubdirectory(loc) : "Invalid modules directory: " + name;
412 return completeModuleDirectory(newDirectory(name), loc);
413 }
414 // Now try the non-prefixed resource name, but be careful to avoid false
415 // positives for names like "/modules/modules/xxx" which could return a
416 // location of a directory entry.
417 loc = findLocation(name.substring(MODULES_ROOT.length()));
|