-
-
Notifications
You must be signed in to change notification settings - Fork 309
Resource API
See also the ClassGraph API overview.
💡 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 ...
});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
ResourceListdown to the resources with paths of interest, then calling one of the.forEach*()methods on the list, since those all close eachResourceafter the consumer has read from it.💡 If you read the content of a
Resourceyourself, using one of the following methods, don't forget to callResource#close()when you have finished reading from it (or open theResourcein a try-with-resources block).🛑 Important:
Resourceis not threadsafe -- any givenResourceshould only be open in one thread at a time. Opening aResourcethat is already open, or opening anyResourceafter theScanResulthas been closed, throwsIllegalStateException.-
.open()opens the resource as anInputStream. -
.read()reads the resource as aByteBuffer, wrapped in aCloseableByteBuffer. 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 theByteBuffer. Close theCloseableByteBufferwhen you are finished with it, to release or unmap the buffer; closing theResourcedoes this for you. -
.load()loads the entire content of the resource as abyte[]array. (This calls.close()on theResourceonce 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 aString, assuming UTF-8 encoding. (This also calls.close()for you.) -
.close()closes the underlyingInputStream, or releases/unmaps the underlyingByteBuffer.
-
-
Reading resource metadata:
-
.getPath()returns the path of the resource relative to the package root, as aString. For example, for a resource path ofBOOT-INF/classes/com/xyz/resource.xmland a package root ofBOOT-INF/classes/, returnscom/xyz/resource.xml. Version prefixes in multi-version jars are also dropped, so for a resource path ofMETA-INF/versions/11/com/xyz/resource.xml, this returnscom/xyz/resource.xml. -
.getPathRelativeToClasspathElement()returns the full path of the resource within the classpath element, as aString, i.e.BOOT-INF/classes/com/xyz/resource.xmlorMETA-INF/versions/11/com/xyz/resource.xmlfor the two examples above. -
.getLength()returns the length of the resource in bytes, or-1if 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. Returns0Lif 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 aSet<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 aURIfor the resource. -
.getURL()returns aURLfor the resource. Note that this throwsIllegalStateExceptionif the resource was obtained from a system module or a jlink'd runtime image, since thejrt:URI scheme is only supported byURI, not byURL. -
.getClasspathElementURI()returns aURIfor the classpath element or module containing the resource. -
.getClasspathElementURL()returns aURLfor the classpath element or module containing the resource. This throwsIllegalStateExceptionforjrt:URIs, for the same reason as.getURL(). -
.getClasspathElementFile()returns aFilefor 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 ajrt:URI), for modules with an unknown location, and for jars downloaded from anhttp://orhttps://classpath entry straight into RAM rather than to a temporary file. -
.getModuleReference()returns thejava.lang.module.ModuleReferencefor 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 theVfsEntrythat the resource is read from, giving you the rest of theVfsAPI for it: reading it as aReadableByteChannel, addressing it as ajava.nio.file.Pathof a read-only virtual filesystem, or asking for its compressed size.🛑 The returned
VfsEntrystops working once theScanResultthat theResourcecame from is closed, since closing theScanResultcloses theVfsthat the entry is read through.
-
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 wrappedByteBuffer, or null once theCloseableByteBufferhas been closed. -
.close()releases the wrappedByteBuffer. For a buffer returned byResource#read(), this closes theResourceit 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 ...
}Extends ArrayList<Resource>, and implements AutoCloseable, with the following convenience methods.
💡
ResourceListinstances returned by the ClassGraph API are unmodifiable: any attempt to add, remove, replace or sort their elements throwsUnsupportedOperationException. Copy the list if you need a modifiable version of it, e.g.new ArrayList<>(resourceList).
-
Closing:
-
.close()closes everyResourcein the list, releasing any open file handles or memory mappings. This makes aResourceListusable as the resource of a try-with-resources block.
-
-
Converting to
Map:-
.asMap()returns theResourceListas aMap<String, ResourceList>that maps each path to aResourceListof the resources with that path.💡 If a resource path is duplicated across different classpath elements, the
ResourceListin the map value will contain more than one element. See also.findDuplicatePaths(). -
.findDuplicatePaths()returns theResourceelements in this list that have a non-unique path, as aList<Entry<String, ResourceList>>, with the path as the key and aResourceListof two or moreResourceobjects with that path as the value. The list is sorted in lexicographic order of path.
-
-
Reading resource metadata for each
Resourcein the list:-
.getPaths()returns the paths of the resources in this list relative to the package root, by calling.getPath()on eachResourcein the list. Returns aList<String>. -
.getPathsRelativeToClasspathElement()returns the paths of the resources in this list relative to the classpath element, by calling.getPathRelativeToClasspathElement()on eachResourcein the list. Returns aList<String>. -
.getURIs()returns the URIs of the resources in this list, as aList<URI>. -
.getURLs()returns the URLs of the resources in this list, as aList<URL>. You should probably use.getURIs()instead, since a resource in a jlink'd image or a system module has ajrt:URI, andURLdoes not support that scheme.
-
-
Selecting
Resourceelements:-
.get(String resourcePath)returns aResourceListof the resources in this list with the given path, or the empty list if there are none. (This returns a list rather than a singleResource, because the same path may occur in more than one classpath element or module.) -
.filter(Predicate<Resource> filter)returns aResourceListthat is the subset of the original list for which the given predicate returns true. -
.classFilesOnly()returns aResourceListthat is the subset of the original list whose paths end with.class. -
.nonClassFilesOnly()returns aResourceListthat is the subset of the original list whose paths do not end with.class.
-
-
Reading the content of each
Resourcein 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
IOExceptionthrown while opening or reading a resource (or thrown by the consumer itself) to the caller, and theIgnoringIOExceptionform silently skips that resource and continues with the rest of the list.💡 All six methods call
Resource#close()after the consumer returns, and returnthis, 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.-
ByteArrayConsumeris aFunctionalInterfacewith the single abstract methodvoid accept(Resource resource, byte[] byteArray) throws IOException.
-
-
.forEachInputStream(InputStreamConsumer consumer)/.forEachInputStreamIgnoringIOException(InputStreamConsumer consumer)opens each resource in the list as anInputStream, and calls the consumer with the stream.-
InputStreamConsumeris aFunctionalInterfacewith the single abstract methodvoid accept(Resource resource, InputStream inputStream) throws IOException.
-
-
.forEachByteBuffer(ByteBufferConsumer consumer)/.forEachByteBufferIgnoringIOException(ByteBufferConsumer consumer)reads each resource in the list as aByteBuffer, and calls the consumer with the buffer.-
ByteBufferConsumeris aFunctionalInterfacewith the single abstract methodvoid accept(Resource resource, ByteBuffer byteBuffer) throws IOException.
-
-