-
-
Notifications
You must be signed in to change notification settings - Fork 309
ClassGraph Constructor API
See also the ClassGraph API overview.
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:
-
.verbose()enables verbose logging. ClassGraph builds its own hierarchical log tree while scanning, and writes it out at the end of the scan to ajava.util.logging.Loggernamedio.github.classgraph.ClassGraph, at levelINFO. Configure that logger in your logging framework to see the output. The output can be large, and logging significantly increases the time and memory needed to scan, so this is for debugging only. -
.verbose(boolean verbose)enables verbose logging only if the parameter istrue, so that logging can be switched on by a flag without anifstatement. -
.enableRealtimeLogging()calls.verbose(), then causes all future log output to be written out as it is generated, rather than only in the log tree at the end of the scan. This can be useful for debugging issues where scanning is somehow getting stuck or taking much longer than expected.
-
-
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
exportitself to the world or to ClassGraph.-
.enableAllInfo()calls each of the eight.enable...()/.ignore...()methods listed immediately below, for convenience, enabling all relevantClassInfo,FieldInfo,MethodInfo,MethodParameterInfoandAnnotationInfoobjects to be created from the classfiles for accepted classes, for both public and non-public classes, fields, methods and annotations.💡 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.-
.enableClassInfo()enables the scanning of classes.-
.ignoreClassVisibility()ignores class visibility modifiers (by default only public classes are scanned).💡 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.
-
-
.enableFieldInfo()enables the scanning of fields within classes.-
.ignoreFieldVisibility()ignores field visibility modifiers (by default only public fields are scanned). -
.enableStaticFinalFieldConstantInitializerValues()enables the scanning of constant initializer values assigned to static final fields in classes. These can be obtained from theFieldInfoobject for the field, by callingFieldInfo#getConstantInitializerValue(). Note that only primitive-typed orString-typed initializer values are stored as initializer constants. Some languages (like Kotlin) may store constant initializer values for non-static / non-final fields (and these can be discovered by these methods), but technically this does not conform to the classfile spec, so you should not rely on this behavior not changing in future versions of the compiler.
-
-
.enableMethodInfo()enables the scanning of methods within classes.-
.ignoreMethodVisibility()ignores method visibility modifiers (by default only public methods are scanned).
-
-
.enableAnnotationInfo()enables the scanning of annotations (class annotations, method annotations, method parameter annotations, and field annotations).
-
-
-
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.
-
*matches zero or more characters within a single segment: it never crosses a.(package) or/(path) separator. Socom.*.internalmatchescom.a.internalbut notcom.a.b.internal, and partial-segment globs such ascom.acme.proj*.widgetalso work. Used alone as a segment,*must match one whole segment, sojava.awt.*matches the sub-packages ofjava.awt, but notjava.awtitself -- to scanjava.awtand everything below it, usejava.awt. -
**, used as a complete segment, matches zero or more whole segments:com.**.internalmatchescom.internal,com.a.internalandcom.a.b.internal.**may appear at the start, in the middle, or at the end of a pattern. - A trailing
.**or/**(e.g.com.acme.**) is accepted but redundant, since accepting or rejecting a package or path always recursively covers everything below it. -
**must form a complete segment: a pattern such ascom.a**b.implthrowsIllegalArgumentException. - Any number of wildcards may be used in a single pattern, e.g.
com.*.internal.**.impl. - These rules apply to
.acceptPackages(),.rejectPackages(),.acceptPaths()and.rejectPaths(). Class name globs (.acceptClasses()/.rejectClasses()), jar name globs and module name globs instead use the simpler glob syntax, where*matches zero or more of any character, including separators.
-
-
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 aClassreference (com.xyz.pkg.Cls.class.getPackage().getName()), since aClassreference 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.-
.acceptPackages(String... packageNames)specifies packages to scan. May include glob wildcards (*and**-- see Glob wildcard syntax above).🛑 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. -
.acceptPackagesNonRecursive(String... packageNames)specifies packages to scan, without recursing to sub-packages. May not include a glob wildcard (*).💡 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. -
.rejectPackages(String... packageNames)specifies packages that should not be scanned. May include glob wildcards (*and**-- see Glob wildcard syntax above).💡 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 forResourcescanning):💡 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.-
.acceptPaths(String... paths)specifies paths to scan, relative to the package root of the classpath element. May include glob wildcards (*and**-- see Glob wildcard syntax above).🛑 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. -
.acceptPathsNonRecursive(String... paths)specifies paths to scan, without recursing to sub-packages. May not include a glob wildcard (*).💡 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. -
.rejectPaths(String... paths)specifies paths that should not be scanned. May include glob wildcards (*and**-- see Glob wildcard syntax above).💡 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.
-
.acceptClasses(String... classNames)accepts specific classes for scanning, even if they are not in an accepted package. May include a glob wildcard (*) in the class name, but it is not possible to match any package by glob prefix (e.g.*Suffix) without using a glob in the package name (e.g.new ClassGraph().acceptClasses("*.*Suffix")). -
.rejectClasses(String... classNames)rejects specific classes so that they are not scanned, even if they are in an accepted package. May include a glob wildcard (*) in the class name, but it is not possible to match any package by glob prefix (e.g.*Suffix) without using a glob in the package name (e.g.new ClassGraph().rejectClasses("*.*Suffix")).
-
-
Jars:
-
.acceptJars(String... jarLeafNames)accepts specific jars for scanning (if not specified, all jars in the classpath are scanned to look for accepted packages). May include a glob wildcard (*). The leafname is matched ignoring case, since two filenames that differ only in case name the same file on Windows and macOS.💡 Only the leafname of the jar should be provided, not the full path.
-
.rejectJars(String... jarLeafNames)rejects specific jars that should not be scanned. May include a glob wildcard (*). The leafname is matched ignoring case.💡 Only the leafname of the jar should be provided, not the full path.
-
-
Modules:
-
.acceptModules(String... moduleNames)accepts specific modules for scanning. If no accept is provided, all non-system modules are scanned by default. May include a glob wildcard (*).- System modules (modules whose name starts with
java.,jdk.,javafx.ororacle.) are not scanned by default. You can enable the scanning of specific system modules by calling.acceptModules(String... moduleNames)to accept them by name. - You can enable the scanning of all system modules by calling
.enableSystemJarsAndModules().
- System modules (modules whose name starts with
-
.rejectModules(String... moduleNames)rejects modules that should not be scanned. May include a glob wildcard (*).
-
-
Classpath elements containing named resources:
-
.acceptClasspathElementsContainingResourcePath(String... paths)accepts classpath elements that contain a resource with a specific path. For example, this can be used to scan only classpath elements that contain a specific configuration file. -
.rejectClasspathElementsContainingResourcePath(String... paths)rejects classpath elements that contain a resource with a specific path.
-
-
System jars / modules / packages:
-
.enableSystemJarsAndModules()enables the scanning of the system packages (java.*,javax.*,javafx.*,jdk.*,oracle.*,sun.*) and the system modules that contain them. These are not scanned by default, for speed. Automatically calls.enableClassInfo().💡 This is only needed in order to scan all system modules -- an individual system module can be scanned by accepting it by name with
.acceptModules(String... moduleNames).
-
-
Classpath element types:
-
.disableJarScanning()stops ClassGraph from scanning jars on the traditional classpath.-
.disableNestedJarScanning()causes nested jar classpath entries (jars within jars, specified using paths of the form/path/to/outer.jar!/path/to/inner.jar) to not be scanned. Call this method if you care about scanning speed, and you have nested jars in your project, but you are sure you will never need to scan any of the nested jars.
-
-
.disableDirScanning()stops ClassGraph from scanning directories on the traditional classpath. -
.disableModuleScanning()stops ClassGraph from scanning modules. -
.enableURLScheme(String scheme)enables scanning of classpath elements with the given URL scheme or protocol (note that[jar:]file:URLs are enabled by default). Causes a connection to be opened on the URL, and the content fetched to a RAM buffer, with the content spilled to a temporary file on disk if the content is larger than.setMaxBufferedJarRAMSize(). Also supports custom URL schemes. ThrowsIllegalArgumentExceptionif the scheme is shorter than two characters, since a one-character scheme cannot be told apart from a Windows drive letter. -
.enableRemoteJarScanning()enables classpath elements to be fetched from remotehttp:orhttps:URLs for scanning. Equivalent to calling.enableURLScheme("http").enableURLScheme("https"). Remote URL scanning is disabled by default, as this may present a security vulnerability. Only[jar:]file:URLs are scanned by default.
-
-
-
Manually overriding the classpath / module path: If needed, you can scan something other than the classpath or the visible modules.
💡 If you are overriding the classpath or the scanned classloaders, and you want to scan all packages, remember that simply not calling
.acceptPackages(pkgs)means "scan everything".-
Classpath:
-
.overrideClasspath(String classpath)specifies the paths to scan, as a single string with elements separated byFile.pathSeparatorChar, overriding the classpath and the module path. This causes the classloaders, thejava.class.pathsystem property and the modules all to be ignored. -
.overrideClasspath(Object... classpathElements)does the same, but each argument is one classpath entry, and is not split onFile.pathSeparatorChar. An element may be of any type whosetoString()is a classpath element location, e.g.String,FileorPath. ThrowsIllegalArgumentExceptionif any element is aClassLoader-- pass those to.overrideClassLoaders()instead. -
.overrideClasspath(Iterable<?> classpathElements)does the same, for a collection. A singlePathpassed 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.
-
-
ClassLoaders:
-
.overrideClassLoaders(ClassLoader... classLoaders)allows you to choose which classloader(s) to scan, overriding the context classloaders, the classpath, and the module path. Note that you may want to use this together with.ignoreParentClassLoaders()to extract classpath URLs from only the classloaders you specified, and not 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. If one of them is passed in -- e.g. the value returned by
ClassLoader#getSystemClassLoader()orThread#getContextClassLoader()-- then the scanning mechanism that can reach the classes it loads is used instead: for the application classloader, thejava.class.pathclasspath and the non-system modules are scanned; for the platform classloader, the system modules are scanned, as if.enableSystemJarsAndModules()had been called. -
.addClassLoader(ClassLoader classLoader)adds a single custom classloader to scan, without overriding the context classloaders. This is needed if you dynamically load classes with a custom classloader, when the scanning code is not itself running inside a class loaded by the custom classloader. -
.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, so a registered handler can also override a built-in one: the built-in handlers still run afterwards, but a classloader or classpath entry that has already been placed keeps the position the registered handler gave it.💡 A
ClassLoaderHandlerhas 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, callClasspathOrder#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); } }
A handler can also override
getPackageRootPrefixes()andgetLibDirPrefixes(). 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; it defaults toclasses/,test-classes/,BOOT-INF/classes/andWEB-INF/classes/. 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, and it defaults toBOOT-INF/lib/,WEB-INF/lib/andWEB-INF/lib-provided/. Both defaults are the layouts that any classloader can be handed, whatever container it belongs to. To add a directory that is specific to your own container, keep the defaults withClassLoaderHandler.prefixesPlus(...):@Override public List<String> getLibDirPrefixes() { return ClassLoaderHandler.prefixesPlus(ARCHIVE_LIB_DIR_PREFIXES, "my-container-lib/"); }
Add a prefix only if the classloader really can produce classpath elements in that layout.
BOOT-INFandWEB-INFare 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 likeclasses/orlib/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()) .enableAllInfo().scan()) { // ... }
-
-
Module path / layers:
💡 (See also
.acceptModules(moduleName)/.rejectModules(moduleName))-
.overrideModuleLayers(ModuleLayer... moduleLayers)allows you to choose which module layer(s) to scan, overriding the visible module layers. -
.addModuleLayer(ModuleLayer customModuleLayer)adds a single custom module layer to the list of module layers to scan, without overriding the visible module layers. This is needed if you create or dynamically load your own custom module layer, when the scanning code is not itself running inside the custom module layer. -
.ignoreParentModuleLayers()causes parent module layers to be ignored (i.e. only module layers that are not the parent of another module layer are scanned).
-
-
Classpath:
-
Finding inter-class dependencies:
-
.enableInterClassDependencies()records all dependencies found between classes, by looking for class references in superclasses, interfaces, methods, fields, annotations, local variables, intermediate values within a method's code, concrete type parameters, etc. You can then call one of the following methods to determine inter-class dependencies. (You can also call.enableExternalClasses()if you want non-accepted classes in the results.)-
ClassInfo#getClassDependencies()to find the dependencies for a single class. -
ScanResult#getClassDependencyMap()to find the dependencies for all classes. -
ScanResult#getReverseClassDependencyMap()to find the dependent classes for all classes (the inverse of the map in the previous method). -
GraphVizDotFile#writeFromInterClassDependencies(scanResult, classInfoList, path), in theclassgraph-vizlibrary, to write a GraphViz.dotfile showing the dependencies between classes (e.g. pass the result ofScanResult#getAllClasses()as the class list).
-
-
-
Advanced:
-
.enableExternalClasses()causes "external classes" to be returned inClassInfoListlists (i.e. classes that were not in an accepted package, but were referred to in an accepted class' classfile, as a superclass, implemented interface, or annotation). -
.disableRuntimeInvisibleAnnotations()causes only annotations withRetentionPolicy.RUNTIMEto be scanned. -
.enableMultiReleaseVersions()causes every version of a multi-release resource to be returned, each under its ownMETA-INF/versions/<N>/path prefix, rather than only the one version the running JVM would select. This is for tools that need to inspect all versions in a multi-release jar. Since a multi-release classfile can then appear more than once, this implicitly disables.enableClassInfo()and everything that depends on it, so only resources are scanned. (.enableClassInfo()likewise implicitly disables this option, so call whichever one you want last.) -
.setMaxBufferedJarRAMSize(int maxBufferedJarRAMSize)sets the maximum number of bytes, per jar, that ClassGraph will buffer in RAM before spilling over to a temporary file on disk. This applies to a nested jar that is stored deflated within an outer jar and has to be inflated before it can be read, and to a jar downloaded from anhttp:orhttps:classpath URL. Both are rare. The default is 64MB, i.e. writing to disk is avoided wherever possible; lowering it reduces ClassGraph's memory usage if either situation arises. -
.removeTemporaryFilesAfterScan()causes temporary files (most often, nested jars that were extracted to temporary files) to be removed before theScanResultis returned. You can use this if you need to scan many times, but don't want to wait until you callScanResult#close()or the JVM shuts down before temporary files are cleaned up.
-
-
Version:
-
ClassGraph.getVersion()is a static method returning the version number of the ClassGraph library, or"unknown"if it could not be determined.
-
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 theScanResult, or allocate theScanResultin a try-with-resources block.
💡 If a synchronous scan fails, it throws
ClassGraphException, which extendsRuntimeException, so it does not have to be declared or caught. Whatever went wrong is its cause: anInterruptedExceptionif 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 usualExecutionException, and the failure handler is passed the originalThrowable.
-
Synchronous scanning (the standard scanning method):
💡 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.
-
.scan()returns aScanResult. -
.scan(int numParallelTasks)scans with the given number of worker threads, rather than the default number. -
.scan(ExecutorService executorService, int numParallelTasks)scans using your ownExecutorService.
-
-
Asynchronous scanning:
-
.scanAsync(ExecutorService executorService, int numParallelTasks)returns aFuture<ScanResult>. -
.scanAsync(ExecutorService executorService, int numParallelTasks, Consumer<ScanResult> scanResultProcessor, Consumer<Throwable> failureHandler)callsscanResultProcessorwith theScanResulton success, andfailureHandleron failure.
-
-
Reading the classpath / module path:
💡 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
ClassGraphandScanResult, except that theClassGraphversions do not extract nested jarfiles, but theScanResultversions 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 theclassgraph-classpathlibrary on its own, without pulling in the scanner.-
.getClasspath(), returns the classpath as a pathStringseparated byFile.pathSeparatorChar. Returns only the base file of each classpath entry (i.e. will not include compound URLs with package roots within a jar, or nested jars within jars, since the URL scheme separator char and the path separator char are both:on Linux and macOS). -
.getClasspathFiles(), returns classpath entries as aList<File>. Returns only the base file of each classpath entry (i.e. will not include compound URLs with package roots within a jar, or nested jars within jars). -
.getClasspathURIs(), returns classpath entries and modules as aList<URI>. -
.getClasspathURLs(), returns classpath as aList<URL>. Will not includejrt:URIs for system modules or modules obtained from a jlink'd runtime image, sinceURLdoes not support thejrt:scheme. -
.getModuleReferences(), returns all visible modules as aList<ModuleReference>. -
.getModulePathInfo()returns information about the module path, as specified on the commandline using--module-path,--add-modules,--patch-module,--add-exports,--add-opens, and--add-reads, as aModulePathInfoobject. If you also require the returnedModulePathInfoto include values fromAdd-ExportsandAdd-Opensentries in jarfile manifest files encountered while scanning, then callScanResult#getModulePathInfo()instead.
-