Skip to content

Classpath Specification Mechanisms

Luke Hutchison edited this page Aug 5, 2021 · 58 revisions

ClassGraph handles the following classpath and module path specification mechanisms:

  • The JDK 9+ module path (Project Jigsaw). ClassGraph uses this mechanism to scan all visible modules, however it is entirely implemented with reflection, so that ClassGraph can be compiled with JDK 7 for backwards compatibility.
    • Can find all modules visible from the calling code's module layer: modules in the module path, system modules, and modules in jlink'd images.
    • Includes support for automatic modules, non-modular jars on the module path whose module name is derived from the Automatic-Module-Name entry in the manifest file, or from the filename of the jar. (The module name is found by ClassGraph whether these jars are added to the module path or the traditional classpath.)
  • Multi-release jars on the traditional classpath in JDK 9+, emulating the JPMS semantics for overriding the "base section" using "versioned sections" contained in paths of the form META-INF/versions/X, where X is an integer greater than or equal to 9, and less than or equal to the running JVM version. (N.B. This requires the manifest file to contain the attribute Multi-Release: true.)
  • The standard (now legacy) Java URLClassLoader and subclasses.
  • The java.class.path system property, supporting specification of the classpath using the -cp JRE commandline switch.
  • ClassGraph includes reflection code to extract path entries from specific classloaders from a wide range of common runtime environments:
    • The Spring, Spring-Boot, and Spring-Boot executable jar classloaders, including properly handling nested classpaths (jars within jars) in an optimal way, by "slicing" the jarfile to extract entries directly from nested jars (which is what Spring Boot also does for speed).
      • Also works with Spring Boot Dev Tools' RestartClassLoader for hot reload (though you will have to run a new scan on each restart).
    • The JBoss WildFly classloader, including JARs and WARs nested inside EARs.
    • The WebLogic classloader.
    • The OSGi Eclipse DefaultClassLoader.
    • The OSGi Equinox classloader (e.g. for Eclipse PDE).
    • The Apache Felix (OSGi reimplementation) classloader.
    • The traditional Websphere classloader.
    • The Websphere Liberty / Open Liberty classloader, in both war and overlay mode.
    • The Tomcat 8+ / Catalina WebappClassLoaderBaseHandler classloader.
    • The TomEE classloaders for servlets and JAX-RS endpoints (TomEEWebappClassLoader and CxfContainerClassLoader).
    • The Ant classloader.
    • The Plexus ClassWorlds ClassRealm classloader.
    • The Quarkus RuntimeClassLoader.
    • The Uno-Jar JarClassLoader, and the (older, unmaintained) One-Jar JarClassLoader.
    • Any unknown classloader with a predictable method such as getClasspath(), getClassPath(), getURLs() etc. (a number of method and field names are tried).
  • Classes added to lib/ or ext/ directories in the JDK or JRE (this is a rare but valid way to add classes to the classpath), or any other extension directories found in the java.ext.dirs system property.
    • Note however that if you use this method to add jars to the classpath, and you want ClassGraph to scan your jars, you'll have to un-reject the scanning of system jars, or specifically accept the lib/ext jars you want to scan (see the documentation for info).
  • OS-specific site-wide lib/ or ext/ directories (i.e. directories where jarfiles may be installed such that they are accessible to all installed JREs and JDKs):
    • /usr/java/packages on Linux
    • %SystemRoot%\Sun\Java\lib, %SystemRoot%\Sun\Java\lib\ext, %SystemRoot%\OracleJava\lib, or %SystemRoot%\Oracle\Java\lib\ext on Windows
    • /System/Library/Java/Libraries and /System/Library/Java/Extensions on Mac OS X
    • /usr/jdk/packages on Solaris
  • Remotely-hosted jarfiles, specified using http:// or https:// URLs. (Some ClassLoaders allow this.) This is disabled by default for security reasons. If .enableRemoteJarScanning() is called before .scan(), any remote jars are downloaded to a ByteBuffer in RAM, or if the jar is too large, the jar is spilled over to a temporary file on disk. (Note that if your classpath contains remote jars, they will be downloaded every time the classpath is scanned. Both ClassGraph and the context ClassLoader may separately download the same jarfiles.)
  • Jarfiles specified using custom URL schemes. This requires the custom URL scheme for the filesystem to be enabled in ClassGraph via .enableURLScheme("jimfs") or similar. If a custom URI scheme is not backed by an underlying FileSystem implementation, then URL.openConnection() is used to open the URL. If the data fetched over the correction is large, it will automatically spill to a temporary file on disk.
  • Classpath elements hosted on custom filesystems such as Jimfshttps://github.com/google/jimfs). Supports scanning a classpath URL of custom scheme (e.g. jimfs://) via the NIO Files and Path APIs. This works for URLs that point to either a jarfile or to a directory (ClassGraph figures out which of these the URL points to). If you specify the URL path to a jarfile on a custom filesystem, it will be opened using FileChannel for speed, rather than calling URL.openConnection().
  • Wildcarded classpath entries, e.g. lib/* (which includes all jars and directories in lib/ as separate classpath entries), which is allowed as of JDK 6. (Only whole-directory globs are currently supported, so lib/proj* doesn't work, but this should match the JRE's behavior.)
  • Class-Path entries in a jarfile's manifest file, whereby jarfiles may add other external jarfiles to their own classpaths, with paths resolved relative to the parent directory or parent jarfile of the manifest's jarfile. ClassGraph is able to determine the transitive closure of these references, breaking cycles if necessary.
  • Bundle-ClassPath entries in an OSGi bundle jarfile's manifest file, which allows a bundle to add nested jars to the bundle classpath, with paths resolved relative to the root of the bundle jarfile.
  • Jarfiles within jarfiles (to unlimited nesting depth), e.g.
    project.jar!/BOOT-INF/lib/dependency.jar, as required by Spring-Boot, JBoss, and Felix classloaders, and probably others.
    • To accomplish this, ClassGraph implements a custom highly-optimized zipfile central directory parser, which is capable of handling zipfile "slices" (zipfiles nested within zipfiles, when the inner zipfile is stored rather than deflated), to arbitrary nesting depth. This allows ClassGraph to scan nested jarfiles (jarfiles within jarfiles, e.g. Spring Boot lib jars in BOOT-INF/lib/) without first extracting the inner jars to temporary files (the Java ZipFile API is not capable of this).
    • If nested jarfiles are deflated rather than stored (which is rare), they cannot be read as a slice since they are compressed, so these jarfiles are automatically inflated to RAM (with failover to a temporary file on disk if the nested jar is over 32MB in uncompressed size).
  • Zip64-format jarfiles
    • ClassGraph's custom jarfile reader has full support for Zip64. This extension of the zip file format is triggered if a jarfile contains more than 65536 entries, or any entry is over 4GB in uncompressed size, or the entire jar is over 4GB in size.
    • ClassGraph's jarfile reader contains workarounds for the Java ByteBuffer and array size limit of 2GB, by reading or mmap'ing large jarfiles in 2GB chunks, and seamlessly swapping in new chunks as needed.
  • The package hierarchy being rooted at a non-root path within a jarfile, e.g. BOOT-INF/classes/ (Spring-Boot), WEB-INF/classes/ (Tomcat), and classes/ (Ant). (Note that the standard Java classloaders do not support this.)
  • Nested lib and ext jars at the following relative paths, which are implicitly part of the classpath in some runtime environments:
    • lib/
    • lib/ext/
    • BOOT-INF/lib/
    • BOOT-INF/lib-provided/
    • WEB-INF/lib/
    • WEB-INF/lib-provided/
    • META-INF/lib/
  • Auto-detects nested classpath roots at the following relative paths, which are implicitly part of the classpath in some runtime environments:
    • classes/
    • test-classes/
    • BOOT-INF/classes/
    • WEB-INF/classes/
  • Handles both PARENT_FIRST and PARENT_LAST classloader delegation modes (used by Websphere and Spring Boot), in order to resolve classpath elements in the correct order, mirroring the delegation order of the classloaders that the classpath elements were obtained from. (Standard Java classloaders use PARENT_FIRST delegation.)
  • ClassGraph supports classloading through either the classloader that a classfile was found in, or if that fails, by directly loading the class through a custom classloader that directly loads the bytecode as a resource file from the classpath or module path, and then defines the class.