| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
See also the ClassGraph API overview.
Nothing is scanned unless it is enabled, so a ClassGraph with no enable method called finds nothing at all:
try (ScanResult scanResult = new ClassGraph()
.enableNonSystemModules() // scan the modules of the visible module layers
.enableClasspath() // scan the classpath of every classloader that can be found
.enableClassInfo().acceptPackages("com.xyz").scan()) {
// ...
}The methods that say where to scan come in pairs. The method with no arguments enables the sources that are found in the current runtime environment; the method that takes varargs enables exactly the sources it is given:
| No arguments: scan what is in the environment | Varargs: scan exactly what is named |
|---|---|
| .enableClasspath() -- every classpath element of every classloader that can be found -- the thread context classloader, the system classloader, the classloader of the class in any frame of the call stack, and the ancestors of all of those, including java.class.path | .enableClassLoaders(ClassLoader...), .enableClasspathEntries(Object...) |
| .enableModules(), .enableSystemModules(), .enableNonSystemModules() -- the modules of the module layers that are visible to the caller | .enableModuleLayers(ModuleLayer...) |
So calling only the varargs method scans only what it names, and nothing from the environment -- this is how 4.x's overrideClasspath() and overrideClassLoaders() are now written. Calling both scans the environment as well as what you named, which is how 4.x's addClassLoader() and addModuleLayer() are now written. There is never a second overload of the same method name that changes the meaning of the call.
Each call adds to the end of the list of things to scan, so classpath sources are scanned in the order the calls were made. Modules are always scanned before all of them, whatever order they were enabled in, since that is the order in which the JVM resolves a class: a module is searched before the classpath, and even before a classloader delegates to its parent.
.ignoreParentClassLoaders() and .ignoreParentModuleLayers() narrow what is reached from an enabled source, and .disableJarScanning() and .disableDirScanning() narrow what kind of classpath element is read. After a scan, ScanResult#getClasspathURIs() and ScanResult#getModuleReferences() report exactly what was scanned.
๐ก A class in a module that was not scanned can still have its classfile read, if it is needed to complete the class graph above an accepted class -- this is what puts java.lang.Object at the top of every superclass chain, even when no module was enabled. Such a class is never added to the scan results unless .enableExternalClasses() is called. Rejecting a module with .rejectModules() prevents even this.
After instantiating a new ClassGraph(), you can call the following methods. Methods return this for method chaining, so you can call several of these methods in succession.
Logging:
Enabling class scanning:
๐ก By default, classfiles are not scanned. You must call one or more of the methods below to enable classfile scanning. (Accepting packages or classes also implicitly calls .enableClassInfo(), see below.)
๐ก If you only need to scan resources files, and not classes, then for speed, you should not call any of the following methods. You should also use .acceptPaths() rather than .acceptPackages(), so that classfile scanning is not enabled.
๐ก Important: to be able to scan classes or resources in a package, the package must export itself to the world or to ClassGraph.
๐ก If you need to scan classfiles, but don't necessarily know yet what information about classes, methods, fields or annotations you might need, or you don't care too much about speed, always call .enableAllInfo(), to save yourself any surprises where classes, fields, methods or annotations end up missing from results. Once you decide to optimize for performance, you can instead call just the individual methods you need from the following list, to increase scanning speed.
๐ก As noted above, in modular (JPMS / Project Jigsaw) projects, the classes in a package are not visible to ClassGraph unless the package or module exports itself to the world or to ClassGraph.
Accepting / rejecting: If no accept criteria are provided, all packages/paths are scanned.
๐ก If no packages, classes or paths are accepted, then everything is accepted, i.e. all packages or paths are scanned. ๐ก As a corollary, if you accept a specific package or class, then other packages or classes will not be scanned unless they too are accepted. If you accept a specific class, other classes in the same package will not be scanned unless the package itself is also accepted (and if the package is accepted, you don't need to accept the specific class, because it resides in the package). ๐ก Note that package and path accepting/rejecting work the same internally, they just accept different separator characters ('.' for packages, '/' for paths), meaning that if you accept/reject a path, the corresponding package is accepted/rejected, and vice versa.
Glob wildcard syntax: Package and path accept/reject criteria may contain glob wildcards.
Packages: You can specify accept criteria using the package separator character, '.' (useful for classfile scanning):
๐ก Accept and reject packages using string literals ("com.xyz.pkg") rather than a Class reference (com.xyz.pkg.Cls.class.getPackage().getName()), since a Class reference causes the class to be loaded and initialized by the JVM just to read its package name -- and the point of ClassGraph is to find out about classes without loading them.
๐ Omit this call to scan all packages. In other words, you never need .acceptPackages("*") or .acceptPackages("") -- by default, if no packages are accepted, all paths are scanned. ๐ก Automatically calls .enableClassInfo(), so call .acceptPaths() instead if you don't need to scan classes.
๐ก e.g. you can specify .acceptPackagesNonRecursive("com.xyz.widgets") to scan only resources in that packge, or .acceptPackagesNonRecursive("") to scan only the root package of each classpath element, but not any sub-packages. ๐ก Automatically calls .enableClassInfo(), so call .acceptPathsNonRecursive() instead if you don't need to scan classes.
๐ก Automatically calls .enableClassInfo(), so call .rejectPaths() instead if you don't need to scan classes. ๐ก Rejecting packages always works recursively (i.e. rejecting a package causes the package and its sub-packages to not be scanned).
Paths: ...Or you can specify accept criteria using the path separator characer, '/' (useful for Resource scanning):
๐ก Note that if you just need to read a small number of specific resource files, you don't necessarily need to accept the paths that contain those files, you can also call ScanResult#getResourcesWithPathIgnoringAccept(String resourcePath) after the scan has completed, and all classpath elements will be searched for resources with the specific path, whether or not that path was accepted.
๐ Omit this call to scan all paths. In other words, you never need to call .acceptPaths("*") or .acceptPaths("") -- by default, if no paths are accepted, all paths are scanned.
๐ก e.g. you can specify .acceptPathsNonRecursive("META-INF/config") to scan only resources in that directory, or .acceptPathsNonRecursive("") to scan only the root directory of each classpath element, but not any sub-directories.
๐ก Rejecting paths always works recursively (i.e. rejecting a path causes the path and its sub-paths (sub-directories) to not be scanned).
Classes: You can accept/reject specific classes, not just whole packages.
๐ก If nothing is accepted, everything is. But if you accept a specific package or class, then other packages or classes will not be scanned unless they too are accepted. Therefore, if you accept a specific class, other classes in the same package will not be scanned unless the package itself is also accepted -- and if the package is accepted, you don't need to accept the specific class, because it resides within the accepted package.
Jars:
๐ก Only the leafname of the jar should be provided, not the full path.
๐ก Only the leafname of the jar should be provided, not the full path.
Modules:
๐ Accepting a module by name does not enable a module source: nothing is scanned from any module unless .enableModules(), .enableSystemModules(), .enableNonSystemModules() or .enableModuleLayers() is also called. Accepting a system module by name therefore needs .enableSystemModules(), which is what makes system modules (those whose name starts with java., jdk., javafx. or oracle.) eligible at all.
Classpath elements containing named resources:
System jars:
๐ก This is about jars, not modules: call .enableSystemModules() to scan the system modules. (In 4.x, one method enableSystemJarsAndModules() did both.)
Classpath element types:
๐ก There is no disableModuleScanning(): simply do not enable a module source, and no module is scanned, or even looked for.
Choosing the classpath / module path to scan: See Saying what to scan above for the rule that governs this whole group.
๐ก If you have named the classpath or the classloaders to scan, and you want to scan all packages within them, remember that simply not calling .acceptPackages(pkgs) means "scan everything".
.enableClasspath() scans every classpath element of every classloader that can be found in the current runtime environment. A classloader is found if it is any of the following:
Every classpath element that every one of those classloaders loads classes from is scanned, whether or not the classloader exposes it publicly -- see Classpath specification mechanisms for how each supported classloader is read. Classpath elements are scanned in the order in which the classloaders that declared them would be asked to load a class, so a class that appears on the classpath more than once is reported from the copy the JVM would actually load, and each classpath element is scanned only once, however many of the classloaders declare it. The application classloader is normally one of the classloaders found, so the entries that the java.class.path system property lists are scanned too, at the position the application classloader takes in that order.
This method takes no arguments, because it scans what is in the environment. To scan specific classloaders or specific classpath elements instead, call .enableClassLoaders(...) or .enableClasspathEntries(...) and leave this call out; calling both scans the environment as well as what you named. It does not enable the scanning of modules -- that is what the module methods below are for, and modules are always scanned first.
.enableClasspathEntries(String classpath) scans the given classpath, with elements separated by File.pathSeparatorChar. No classloader is asked for it, so nothing else is scanned unless it is enabled as well.
.enableClasspathEntries(Object... classpathElements) does the same, but each argument is one classpath entry, and is not split on File.pathSeparatorChar. An element may be of any type whose toString() is a classpath element location, e.g. String, File or Path. Throws IllegalArgumentException if any element is a ClassLoader -- pass those to .enableClassLoaders() instead.
.enableClasspathEntries(Iterable<?> classpathElements) does the same, for a collection. A single Path passed here is treated as one classpath entry, not as a sequence of its name elements.
.filterClasspathElements(Predicate<String> filter) selectively includes or excludes classpath elements based on their directory or jarfile path. The predicate returns true for elements that should be scanned.
.filterClasspathElementsByURL(Predicate<URL> filter) does the same, based on the classpath element's URL.
.enableClassLoaders(ClassLoader... classLoaders) scans the classpath elements declared by the given classloaders, and by their parents, rather than by the classloaders found in the current runtime environment. Call .enableClasspath() as well to scan both. Note that you may want to use this together with .ignoreParentClassLoaders(), so that classpath entries are obtained only from the classloaders you passed in, and not from their parent classloaders.
๐ก The JDK's own application and platform classloaders do not expose the locations they load classes from, so they cannot be scanned as classloaders. Passing one in -- e.g. the value returned by ClassLoader#getSystemClassLoader() or Thread#getContextClassLoader() -- is therefore not enough on its own. The application classloader's own classpath entries are still found, since its handler falls back to the java.class.path system property; but the platform classloader loads only from the system modules, so .enableSystemModules() is what reaches its classes. (In 4.x, passing one of these classloaders silently switched on a different scanning mechanism instead. It no longer does: say what you want scanned.)
.ignoreParentClassLoaders() causes parent classloaders to be ignored (i.e. classpath element paths are only obtained from classloaders that are not the parent of another classloader).
.registerClassLoaderHandler(ClassLoaderHandler classLoaderHandler) teaches ClassGraph how to read the classpath out of a classloader that it does not already know about. ClassGraph ships with handlers for the classloaders of the common application servers, build tools and frameworks (see Classpath Specification Mechanisms), so this is only needed for a classloader that none of those handle. Registered handlers are offered each classloader before the built-in handlers are, in the order they were registered, and are never dropped, so a registered handler can also override a built-in one. Of the built-in handlers, only those that name the most specific classloader class are used, so a handler that names a subclass of URLClassLoader takes the place of the built-in URLClassLoader handler rather than running alongside it, and has to add the classloader's own URLs itself. The handlers that are kept run in turn, and a classloader or classpath entry that has already been placed keeps the position the first handler to place it gave it.
๐ก A ClassLoaderHandler has to be stateless, since one instance is shared by every scan. For work that should only happen once per scan, such as reading a container-wide set of classpath entries that every one of its classloaders would yield, call ClasspathOrder#claimOncePerScan(String), which returns true only for the first caller in each scan.
public class MyClassLoaderHandler implements ClassLoaderHandler {
@Override
public boolean canHandle(Class<?> classLoaderClass, ClassGraphLog log) {
return classIsOrExtendsOrImplements(classLoaderClass, "com.example.MyClassLoader");
}
@Override
public void findClassLoaderOrder(ClassLoader classLoader, ClassLoaderOrder classLoaderOrder,
ClassGraphLog log) {
// This classloader resolves classes parent-last, so add it before delegating to its parent
classLoaderOrder.add(classLoader, log);
classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log);
}
@Override
public void findClasspathOrder(ClassLoader classLoader, ClasspathOrder classpathOrder,
ClassGraphLog log) {
classpathOrder.addClasspathEntry(((MyClassLoader) classLoader).getClasspath(), classLoader, log);
}
}The log handed to each method is null unless verbose logging is switched on, which it is not by default. Pass it straight through to the methods that take one, as above -- every one of them accepts null -- and null-check it before calling it yourself.
A handler can also override getPackageRootPrefixes() and getLibDirPrefixes(). The first names the directories the classloader may root the package hierarchy at, which are looked for within each classpath entry and stripped if present. The second names the directories the classloader loads jarfiles from without listing them as classpath entries; the jarfiles found in one, at any depth, are added to the classpath after the entry that contains them. Both default to empty, since a classloader normally loads classes only from the classpath elements it was given -- URLClassLoader has no automatic package roots or lib dirs at all. Override one only if your classloader's own code goes looking in a directory of a fixed name:
@Override
public List<String> getLibDirPrefixes() {
return List.of("my-container-lib/");
}Declare a prefix only if the classloader really does look there. BOOT-INF and WEB-INF are unambiguous, because a hyphen is not legal in a Java identifier, so a directory with one of those names cannot be a package; an ordinary name like classes/ or lib/ can be, and declaring one wrongly either hides a real package or puts jarfiles that are only resources on the classpath.
try (ScanResult scanResult = new ClassGraph()
.registerClassLoaderHandler(new MyClassLoaderHandler())
.enableClasspath().enableAllInfo().scan()) {
// ...
}๐ก (See also .acceptModules(moduleName) / .rejectModules(moduleName))
Finding inter-class dependencies:
Advanced:
Version:
With a configured ClassGraph instance, you can call one of the following methods to start the scan, producing a ScanResult, which holds all the ClassInfo objects and Resource objects found during a scan.
๐ Make sure you call ScanResult#close() when you have finished with the ScanResult, or allocate the ScanResult in a try-with-resources block.
๐ก If a synchronous scan fails, it throws ClassGraphException, which extends RuntimeException, so it does not have to be declared or caught. Whatever went wrong is its cause: an InterruptedException if the calling thread was interrupted while waiting for the scan (the thread's interrupt status is restored before the exception is thrown), otherwise the exception thrown by the scan. The asynchronous methods do not wrap anything: Future#get() throws the usual ExecutionException, and the failure handler is passed the original Throwable.
๐ก This causes ClassGraph to scan in parallel with the default number of worker threads: 1.25x the number of available processors for scanning, plus up to 4 more for I/O, and never fewer than 2 in total. Blocks until scanning is complete.
๐ก Rather than perform a full scan, ClassGraph can return all classpath elements resolved using its support for a wide range of classpath specification mechanisms. (N.B. these same methods are defined in both ClassGraph and ScanResult, except that the ClassGraph versions do not extract nested jarfiles, but the ScanResult versions do return URLs/files for nested jars, if any nested jars were extracted during classpath scanning.) ๐ก If reading the classpath is all you need, you can depend on the classgraph-classpath library on its own, without pulling in the scanner.
| Back | FazBrowse Home | New Git URL |