Luke Hutchison edited this page Jan 31, 2019 · 266 revisions

ClassGraph Logo      Duke Award Logo

ClassGraph (formerly known as FastClasspathScanner) is a fast classpath and module path scanner for JVM languages, enabling metaprogramming in JVM languages -- the ability to write code that analyzes or responds to the properties of other code.

Contents

ClassGraph API

Downloading

Maven dependency

<dependency>
    <groupId>io.github.classgraph</groupId>
    <artifactId>classgraph</artifactId>
    <version>LATEST</version>
</dependency>

Pre-built JARs

You can get pre-built JARs (usable on JRE 7 or newer) from Sonatype.

Building from source

ClassGraph must be built on JDK 8 or newer (due to the presence of @FunctionalInterface annotations on some interfaces), but is built using -target 1.7 for backwards compatibility with JRE 7.

The following commands will build the most recent version of ClassGraph from git master. The compiled package will then be in the "classgraph/target" directory.

git clone https://github.com/classgraph/classgraph.git
cd classgraph
export JAVA_HOME=/usr/java/default   # Or similar -- Maven needs JAVA_HOME
mvn -Dmaven.test.skip=true package

This will allow you to build a local SNAPSHOT jar in target/. Alternatively, use mvn -Dmaven.test.skip=true install to build a SNAPSHOT jar and then copy it into your local repository, so that you can use it in your Maven projects. Note that may need to do mvn dependency:resolve in your project if you overwrite an older snapshot with a newer one.

mvn -U updates from remote repositories an may overwrite your local artifact. But you can always change the artifactId or the groupId of your local ClassGraph build to place your local build artifact in another location within your local repository.

Use as a module

The ClassGraph jarfile includes a module-info.class file, which means that it can be added to your project as a Java module. In fact, in JDK 9+, if you are trying to call ClassGraph from code in a named module, ClassGraph must be added to the module path as a module, rather than included in the traditional classpath (you can't call non-modular code from modular code).

Requiring the ClassGraph module

To use ClassGraph as a Java module, add the jar dependency to your project's module path, then add the following to your module-info.java:

requires io.github.classgraph;

If when trying to run your code, you get the following exception, the problem is that the code you are trying to run is modular, but the ClassGraph jar was added to the regular classpath, not the module path:

java.lang.NoClassDefFoundError: io/github/classgraph/ClassGraph

In this case, if you manually added the ClassGraph jar to an Eclipse project, go to Project Properties > Java Build Path > Libraries > Classpath > classgraph-X.Y.Z.jar, open the dropdown, double click on Is not modular, and check the box at the top, Defines one or more modules, then OK, then Apply and Save.

If you are launching from the commandline, make sure both your project and ClassGraph are on the module path, not the classpath.

Opening your module to ClassGraph (scanning / loading classes in modules)

The Java Platform Module System (in JDK 9+) enforces strict encapsulation on both classes and resources. ClassGraph reads classfiles directly, so to some degree it can get around visibility restrictions, as long as the module exports the package that the classfile is contained within, since classfiles are just like any other files from the point of view of listing resources. If the classfile can be read, even if the class itself is non-public or its fields or methods are non-public, ClassGraph can still build ClassInfo, FieldInfo, MethodInfo objects etc., as long as you call ClassGraph#ignoreClassVisibility(), ClassGraph#ignoreFieldVisibility() and/or ClassGraph#ignoreMethodVisibility() before starting the scan.

However, if you try to actually load a non-public class found by ClassGraph in package that is exported but not opened to ClassGraph or to the world, by calling ClassInfo#loadClass() or ClassInfoList#loadClasses(), then the reflective call to instantiate the class will fail if the class and its superclasses are not public. (In other words, in general, listing and reading resources (including classfile scanning) only requires exports, but reflection (in particular, classloading) requires open/opens, for non-public classes and their superclasses.)

Note also that ClassGraph uses reflective access (often to private fields and/or methods) to get the classpath URLs and paths from ClassLoaders. This can give an "Illegal reflective access" warning on stderr, and if you are getting that warning, ClassGraph will not be able to read the necessary fields or call the necessary methods in some future JRE, once strong encapsulation is enforced. You will need to open your ClassLoader to ClassGraph to prevent this.

So the "tl;dr" version is:

  1. If you want ClassGraph to be able to scan classfiles or resources in a package, the package or the module must be exported, either to ClassGraph or to the world.
  2. If you are using ClassInfo#loadClass() or ClassInfoList#loadClasses() to get Class<?> references from ClassInfo objects, and the class and its superclasses are not all public, the package or the module must be opened, either to ClassGraph or to the world -- otherwise you will get an IllegalAccessError or similar.
  3. If you get "Illegal reflective access" warnings, you need to open the module containing your ClassLoader to ClassGraph.

Your options for opening the module or package to ClassGraph or to the world, given a module name of your.module, are:

  1. Open the entire module for reflection:
  open module your.module {
      requires io.github.classgraph;
  }
  1. Open only selected package(s) for reflection:
  module your.module {
      requires io.github.classgraph;
      opens some.package;
  }
  1. Open selected package(s) to ClassGraph specifically:
  module your.module {
      requires io.github.classgraph;
      opens some.package to io.github.classgraph;
  }

Use with a SecurityManager (advanced)

ClassGraph needs to obtain a stacktrace to find all ClassLoaders and/or modules. Most of the time, this will work fine out of the box. The exception is if you are using a SecurityManager.

If you are using a SecurityManager, then to enable ClassGraph to get a stacktrace:

  • In JDK 9+: you will need to grant to the classgraph module:
    • RuntimePermission("getStackWalkerWithClassReference")
    • RuntimePermission("accessClassInPackage.sun.misc")
    • RuntimePermission("accessClassInPackage.jdk.internal.misc")
    • ReflectPermission("suppressAccessChecks")
  • In JDK 7 or 8: you will need to grant to the classgraph jar:
    • RuntimePermission("createSecurityManager")
    • RuntimePermission("accessClassInPackage.sun.misc")
    • ReflectPermission("suppressAccessChecks")

There is a fallback methanism that will attempt to use Exception#getStackTrace() if the above permissions fail, but ClassGraph may fail to find the classpath or module path if the above permissions are not granted to runtime environments that do have a SecurityManager installed.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.