Skip to content
Luke Hutchison edited this page Aug 14, 2026 · 9 revisions

See also the ClassGraph API overview.

Contents

Resource usage pattern

💡 If you just need the content of the resource as a String, call Resource#getContentAsString(), which loads the content and closes the resource for you.

A Resource can be opened as an InputStream or a ByteBuffer, or loaded as a byte[] array. After calling open() or read(), you must close either the Resource or the InputStream / CloseableByteBuffer you were handed, so that the stream is closed or the buffer is released. All three implement AutoCloseable, so try-with-resources does this for you, and closing both the Resource and what it handed you is safe:

ResourceList matchingResources = scanResult.getResourcesWithPath("config/cfg.xml");
for (Resource resource : matchingResources) {
    try (resource; InputStream inputStream = resource.open()) {
        // ... Use inputStream ...
    }
}

ResourceList also implements AutoCloseable, and closing the list closes every Resource in it, so a single try-with-resources block can cover the whole list:

try (ResourceList matchingResources = scanResult.getResourcesWithPath("config/cfg.xml")) {
    for (Resource resource : matchingResources) {
        InputStream inputStream = resource.open();
        // ... Use inputStream ...
    }
}

Alternatively you can use the ResourceList#forEach*() methods, which open the resource, hand its content to your consumer, and then close the resource for you:

scanResult.getResourcesWithPath("config/cfg.xml").forEachInputStream((resource, inputStream) -> {
    // ... Use inputStream ...
});

Resource

A reference to a file found within a classpath directory or jarfile, or within a module. Obtained by calling ScanResult#getAllResources() and related methods.

In addition to optionally scanning classes on the classpath, ClassGraph always records the names of all resources (files) encountered in accepted packages during the scan (including classfiles and non-class files). The content of any resource file, or of resource files with paths matching a certain pattern, can be read after the scan.

Behind the scenes, ClassGraph reads resources through the virtual filesystem, which reads a file through an NIO FileChannel, memory-mapping it on Windows and using positioned channel reads on Linux and macOS, because that is the faster of the two on each of those platforms. It translates transparently from the fastest available API for accessing any given resource to whichever API you want to use to read it, so that you can access the resource as an InputStream, a ByteBuffer or a byte[] array, in the fastest way possible.

  • Reading resource content:

    💡 The simplest way to read from resources is by filtering a ResourceList down to the resources with paths of interest, then calling one of the .forEach*() methods on the list, since those all close each Resource after the consumer has read from it.

    💡 If you read the content of a Resource yourself, using one of the following methods, don't forget to call Resource#close() when you have finished reading from it (or open the Resource in a try-with-resources block).

    🛑 Important: Resource is not threadsafe -- any given Resource should only be open in one thread at a time. Opening a Resource that is already open, or opening any Resource after the ScanResult has been closed, throws IllegalStateException.

    • .open() opens the resource as an InputStream.
    • .read() reads the resource as a ByteBuffer, wrapped in a CloseableByteBuffer. Where the resource is stored uncompressed in a file that is memory-mapped, the buffer is a slice of the mapping, with no copy; otherwise the content is read into a buffer of its own. Call .getByteBuffer() on the returned instance to reach the ByteBuffer. Close the CloseableByteBuffer when you are finished with it, to release or unmap the buffer; closing the Resource does this for you.
    • .load() loads the entire content of the resource as a byte[] array. (This calls .close() on the Resource once all the content has been loaded.)
    • .getContentAsString() is a convenience method that calls .load() to get the resource content, then decodes and returns it as a String, assuming UTF-8 encoding. (This also calls .close() for you.)
    • .close() closes the underlying InputStream, or releases/unmaps the underlying ByteBuffer.
  • Reading resource metadata:

    • .getPath() returns the path of the resource relative to the package root, as a String. For example, for a resource path of BOOT-INF/classes/com/xyz/resource.xml and a package root of BOOT-INF/classes/, returns com/xyz/resource.xml. Version prefixes in multi-version jars are also dropped, so for a resource path of META-INF/versions/11/com/xyz/resource.xml, this returns com/xyz/resource.xml.
    • .getPathRelativeToClasspathElement() returns the full path of the resource within the classpath element, as a String, i.e. BOOT-INF/classes/com/xyz/resource.xml or META-INF/versions/11/com/xyz/resource.xml for the two examples above.
    • .getLength() returns the length of the resource in bytes, or -1 if unknown. This only reliably returns a valid value after calling .read() or .load() -- and after .open(), only if the underlying jarfile records the length in the zip entry, which not all jarfiles do.
    • .getLastModifiedMillis() returns the last modified time of the resource, in milliseconds since the epoch, taken from the directory entry for a file on disk, or from the zipfile central directory for a jarfile entry. Returns 0L if unknown (e.g. for system module resources or jlink'd resources).

      💡 The ZIP format has no notion of timezone, so ClassGraph assumes that zipfile timestamps are UTC. If you know the timezone the zipfile was created in, and it was not UTC, you will need to apply the corresponding correction yourself.

    • .getPosixFilePermissions() returns the POSIX file permissions of the resource as a Set<PosixFilePermission>, taken from the directory entry for a file on disk, or from the zipfile central directory for a jarfile entry. Returns null if unknown (e.g. for system module resources or jlink'd resources, or on filesystems or operating systems that are not POSIX-compliant).
    • .getURI() returns a URI for the resource.
    • .getURL() returns a URL for the resource. Note that this throws IllegalStateException if the resource was obtained from a system module or a jlink'd runtime image, since the jrt: URI scheme is only supported by URI, not by URL.
    • .getClasspathElementURI() returns a URI for the classpath element or module containing the resource.
    • .getClasspathElementURL() returns a URL for the classpath element or module containing the resource. This throws IllegalStateException for jrt: URIs, for the same reason as .getURL().
    • .getClasspathElementFile() returns a File for the package root directory or jarfile containing the resource, or null if there is no such file -- which is the case for resources in system modules or jlink'd runtime images (whose location is a jrt: URI), for modules with an unknown location, and for jars downloaded from an http:// or https:// classpath entry straight into RAM rather than to a temporary file.
    • .getModuleReference() returns the java.lang.module.ModuleReference for the module containing the resource, or null if the resource was found in a directory or jarfile on the traditional classpath.
  • Reaching the virtual filesystem:

    • .getVfsEntry() returns the VfsEntry that the resource is read from, giving you the rest of the Vfs API for it: reading it as a ReadableByteChannel, addressing it as a java.nio.file.Path of a read-only virtual filesystem, or asking for its compressed size.

      🛑 The returned VfsEntry stops working once the ScanResult that the Resource came from is closed, since closing the ScanResult closes the Vfs that the entry is read through.

CloseableByteBuffer

Returned by Resource#read() and by VfsEntry#read(). Wraps a ByteBuffer in an AutoCloseable, so that the buffer is released or unmapped when the CloseableByteBuffer is closed.

  • .getByteBuffer() returns the wrapped ByteBuffer, or null once the CloseableByteBuffer has been closed.
  • .close() releases the wrapped ByteBuffer. For a buffer returned by Resource#read(), this closes the Resource it came from. Closing twice has no effect, so the buffer is only released once, however many times it is closed.
try (CloseableByteBuffer buf = resource.read()) {
    ByteBuffer byteBuffer = buf.getByteBuffer();
    // ... Use byteBuffer ...
}

ResourceList

Extends ArrayList<Resource>, and implements AutoCloseable, with the following convenience methods.

💡 ResourceList instances returned by the ClassGraph API are unmodifiable: any attempt to add, remove, replace or sort their elements throws UnsupportedOperationException. Copy the list if you need a modifiable version of it, e.g. new ArrayList<>(resourceList).

  • Closing:

    • .close() closes every Resource in the list, releasing any open file handles or memory mappings. This makes a ResourceList usable as the resource of a try-with-resources block.
  • Converting to Map:

    • .asMap() returns the ResourceList as a Map<String, ResourceList> that maps each path to a ResourceList of the resources with that path.

      💡 If a resource path is duplicated across different classpath elements, the ResourceList in the map value will contain more than one element. See also .findDuplicatePaths().

    • .findDuplicatePaths() returns the Resource elements in this list that have a non-unique path, as a List<Entry<String, ResourceList>>, with the path as the key and a ResourceList of two or more Resource objects with that path as the value. The list is sorted in lexicographic order of path.
  • Reading resource metadata for each Resource in the list:

    • .getPaths() returns the paths of the resources in this list relative to the package root, by calling .getPath() on each Resource in the list. Returns a List<String>.
    • .getPathsRelativeToClasspathElement() returns the paths of the resources in this list relative to the classpath element, by calling .getPathRelativeToClasspathElement() on each Resource in the list. Returns a List<String>.
    • .getURIs() returns the URIs of the resources in this list, as a List<URI>.
    • .getURLs() returns the URLs of the resources in this list, as a List<URL>. You should probably use .getURIs() instead, since a resource in a jlink'd image or a system module has a jrt: URI, and URL does not support that scheme.
  • Selecting Resource elements:

    • .get(String resourcePath) returns a ResourceList of the resources in this list with the given path, or the empty list if there are none. (This returns a list rather than a single Resource, because the same path may occur in more than one classpath element or module.)
    • .filter(Predicate<Resource> filter) returns a ResourceList that is the subset of the original list for which the given predicate returns true.
    • .classFilesOnly() returns a ResourceList that is the subset of the original list whose paths end with .class.
    • .nonClassFilesOnly() returns a ResourceList that is the subset of the original list whose paths do not end with .class.
  • Reading the content of each Resource in the list:

    💡 These methods all have distinct names, rather than being overloads of a single name, so that a lambda passed as the consumer is never ambiguous.

    💡 Each of the three content types comes in two forms: the plain form propagates any IOException thrown while opening or reading a resource (or thrown by the consumer itself) to the caller, and the IgnoringIOException form silently skips that resource and continues with the rest of the list.

    💡 All six methods call Resource#close() after the consumer returns, and return this, for method chaining.

    • .forEachByteArray(ByteArrayConsumer consumer) / .forEachByteArrayIgnoringIOException(ByteArrayConsumer consumer) loads the complete content of each resource in the list, and calls the consumer with the content.
      • ByteArrayConsumer is a FunctionalInterface with the single abstract method void accept(Resource resource, byte[] byteArray) throws IOException.
    • .forEachInputStream(InputStreamConsumer consumer) / .forEachInputStreamIgnoringIOException(InputStreamConsumer consumer) opens each resource in the list as an InputStream, and calls the consumer with the stream.
      • InputStreamConsumer is a FunctionalInterface with the single abstract method void accept(Resource resource, InputStream inputStream) throws IOException.
    • .forEachByteBuffer(ByteBufferConsumer consumer) / .forEachByteBufferIgnoringIOException(ByteBufferConsumer consumer) reads each resource in the list as a ByteBuffer, and calls the consumer with the buffer.
      • ByteBufferConsumer is a FunctionalInterface with the single abstract method void accept(Resource resource, ByteBuffer byteBuffer) throws IOException.

Clone this wiki locally