Skip to content

ClassGraph Constructor API

Luke Hutchison edited this page Jul 29, 2024 · 6 revisions

See also the ClassGraph API overview.

Contents

Configuring the ClassGraph instance

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 to stderr. ClassGraph implements its own custom hierarchical logger for debugging purposes. The output can be large, and can cause the scan to take much longer than with debugging disabled. Note that logging will significantly increase the memory and time required to scan the classpath, and may produce a large amount of output on stdout.

      💡 If you don't see any output after calling .verbose(), your logger is probably capturing or redirecting stderr, or you do not have a logger configured. Prior to ClassGraph version 4.6.11, try calling System.setErr(System.out) before scan(). For ClassGraph version 4.6.11 or later, configure a logger to work with java.util.logging.Logger.

    • .enableRealtimeLogging() calls .verbose(), then causes all future log output to be written immediately to stderr 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: in JDK 9+, to be able to scan classes or resources in a package, the package must export itself to the world or to ClassGraph.

    • .enableAllInfo() calls each of the eight .enable...() / .ignore...() methods listed immediately below, for convenience, enabling all relevant ClassInfo, FieldInfo, MethodInfo, MethodParameterInfo and AnnotationInfo objects 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 / JDK 9+) 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 the FieldInfo object for the field, by calling FieldInfo#getConstantInitializerValue(). Note that only primitive-typed or String-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.
      • .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.

    • Packages: You can specify accept criteria using the package separator character, '.' (useful for classfile scanning):

      🛑 Important: It is much better to accept or reject packages or classes using string literals ("com.xyz.pkg") rather than using a Class reference (com.xyz.pkg.Cls.class.getPackage().getName()), since using Class references can cause a class to be loaded into the context classloader for the context that ClassGraph is running in, and that class may be incompatible with the classes returned by ClassInfo#loadClass(), since they may be loaded by a different classloader than classes loaded by ClassGraph. This can lead to a ClassCastException, where a subclass can't be cast to its own superclass. Make sure you do 100% of classloading through ClassGraph methods like ClassInfo#loadClass().

      • .acceptPackages(String... packageNames) specifies packages to scan. May include a glob wildcard (*).

        🛑 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 a glob wildcard (*).

        💡 Automatically calls .enableClassInfo(), so call .rejectPackages() 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.

      • .acceptPaths(String... paths) specifies paths to scan, relative to the package root of the classpath element. May include a glob wildcard (*).

        🛑 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 a glob wildcard (*).

        💡 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 (*).

        💡 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 (*).

        💡 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 (java.*, javax.*, javafx.*, jdk.*, oracle.*) 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().
      • .rejectModules(String... moduleNames) rejects modules that should not be scanned. May include a glob wildcard (*).
    • Classpath elements containing named resources:
      • .acceptClasspathElementsContainingResourcePath(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(paths) rejects classpath elements that contain a resource with a specific path.
    • System jars / modules / packages:
      • .enableSystemJarsAndModules() enables the scanning of system jars and modules. These are not scanned by default:
        • The rt.jar JRE jar (in Java 7/8)
        • Modules with a location URI scheme of jrt:, e.g. jrt:/java.base (in Java 9+)
      • .acceptLibOrExtJars(String... jarLeafNames) accepts specific jars for scanning in JRE/JDK lib/ or ext/ directories. These are not scanned by default. May include a glob wildcard (*), or if you call this method with no parameters, all jarfiles found in JRE lib/ or ext/ directories will be scanned.
      • .rejectLibOrExtJars(String... jarLeafNames) accepts specific jars for scanning in JRE/JDK lib/ or ext/ directories. May include a glob wildcard (*).
    • 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 need to scan any of the nested jars. (This call is also necessary if your runtime environment doesn't have a writable temp dir, since inner jars have to be extracted to temporary files before they can be scanned.)
      • .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 ends up being larger than 64MB. Also supports custom URL schemes.
      • .enableRemoteJarScanning() enables classpath elements to be fetched from remote http: or https: URLs for scanning. Equivalent to calling .enableURLScheme("http").enableURLScheme("https"). Remote URL scanning is disabled by default, as this may present a security vulnerability, since classes from downloaded jars can be subsequently loaded using ClassInfo#loadClass. 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|URL... classpath) allows you to specify which paths to scan, overriding the classpath and the module path.
      • .filterClasspathElements(ClasspathElementFilter filter) allows you to selectively include or exclude classpath elements based on their directory or jarfile path.
        • ClasspathElementFilter is a FunctionalInterface with the single abstract method boolean includeClasspathElement(String classpathElementPathStr).
      • .filterClasspathElementsByURL(ClasspathElementURLFilter filter) allows you to selectively include or exclude classpath elements based on their URL.
        • ClasspathElementURLFilter is a FunctionalInterface with the single abstract method boolean includeClasspathElement(URL classpathElementURL).
    • 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 in the parameter to overrideClassLoaders, and not their parent classloaders.
      • .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).
    • 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).
  • 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).
      • ClassInfoList#generateGraphVizDotFileFromClassDependencies() to create a GraphViz .dot file indicating dependencies between all classes (e.g. call this on the result of ScanResult#getAllClasses()).
  • Advanced:

    • .enableExternalClasses() causes "external classes" to be returned in ClassInfoList lists (i.e. classes that were not in a accepted package, but were referred to in a accepted class' classfile, as a superclass, implemented interface, or annotation).
    • .enableMemoryMapping() enables mmap for file access. By default ClassGraph uses FileChannel for file access, but calling this method before .scan() will cause ClassGraph to use MappedByteBuffer instead. Enabling mmap may increase the scanning speed on classpaths consisting of a large number of large jarfiles, however scan times may also be a little more unpredictable with mmap enabled, and memory pressure will be greatly increased, even if the file content is not actually swapped into RAM. (For some reason, Java chooses to count files that are mapped into virtual memory but not swapped into RAM against the total memory limit for a Java process.) If an OutOfMemoryError occurs while trying to mmap a file, ClassGraph will revert to using FileChannel for file access.
    • .disableRuntimeInvisibleAnnotations() causes only annotations with RetentionPolicy.RUNTIME to be scanned.
    • .initializeLoadedClasses() causes classes to be initialized when they are loaded, using ClassInfo#loadClass() or similar. By default, classes are loaded but not initialized.
    • .removeTemporaryFilesAfterScan() causes temporary files (most often, nested jars that were extracted to temporary files) to be removed before the ScanResult is returned. You can use this if you need to scan many times, but don't want to wait until you call ScanResult#close() or the JVM shuts down before temporary files are cleaned up. May prevent ClassInfo#loadClass() from working.

Starting the scan

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.

  • Synchronous scanning (the standard scanning method):

    💡 This causes ClassGraph to scan in parallel with the default number of worker threads (roughly 1.5-2x the number of available CPU threads). Blocks until scanning is complete.

  • Asynchronous scanning:
    • .scanAsync(executorService, numParallelTasks) returns a Future<ScanResult>.
    • .scanAsync(executorService, numParallelTasks, scanResultProcessor, failureHandler) calls scanResultProcessor with the ScanResult on success, and failureHandler on failure.
  • Reading the classpath / module path:

    💡 Rather than perform a full scan, ClassGraph can return all classpath elements resolved using ClassGraph's support for a wide range of classpath specification mechanisms. (N.B. these same four 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.)

    • .getClasspath(), returns the classpath as a path String separated by File.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 Mac OS X).
    • .getClasspathFiles(), returns classpath entries as a List<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 a List<URI>.
    • .getClasspathURLs(), returns classpath as a List<URL>. Will not include jrt: URIs for system modules or modules obtained from a jlink'd runtime image, since URL does not support the jrt: scheme.
    • .getModules(), returns all visible modules as a List<ModuleRef> of ModuleRef objects (which is a JDK 7/8-compatible wrapper for JPMS' ModuleReference).
    • .getModulePathInfo() returns information about the module path, as specified on the commandline using with --module-path, --add-modules, --patch-module, --add-exports, --add-opens, and --add-reads, as a ModulePathInfo object. If you also require the returned ModulePathInfo to include values from Add-Exports and Add-Opens entries in jarfile manifest files encountered while scanning, then call ScanResult#getModulePathInfo() instead.

Clone this wiki locally