The uber-fast, ultra-lightweight classpath and module scanner for JVM languages.
—
ClassGraph provides comprehensive support for discovering, accessing, and managing resources and classpath elements. This includes files in JARs, directories, modules, and various classpath configurations.
import io.github.classgraph.Resource;
import io.github.classgraph.ResourceList;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.Properties;try (ScanResult scanResult = new ClassGraph().acceptPaths("META-INF", "config").scan()) {
// Get all resources found during scan
ResourceList allResources = scanResult.getAllResources();
// Get resources as a map (path -> ResourceList)
Map<String, ResourceList> resourceMap = scanResult.getAllResourcesAsMap();
// Find resources by exact path
ResourceList springFactories = scanResult.getResourcesWithPath("META-INF/spring.factories");
// Find resources ignoring accept/reject filters
ResourceList allSpringFactories = scanResult.getResourcesWithPathIgnoringAccept("META-INF/spring.factories");
}// Find by filename (leaf name)
ResourceList applicationProperties = scanResult.getResourcesWithLeafName("application.properties");
ResourceList configFiles = scanResult.getResourcesWithLeafName("config.xml");
// Find by file extension
ResourceList jsonFiles = scanResult.getResourcesWithExtension("json");
ResourceList sqlFiles = scanResult.getResourcesWithExtension("sql");
ResourceList propertiesFiles = scanResult.getResourcesWithExtension("properties");
// Find by regex pattern
Pattern logbackPattern = Pattern.compile(".*logback.*\\.xml");
ResourceList logbackConfigs = scanResult.getResourcesMatchingPattern(logbackPattern);
// Find by wildcard pattern
ResourceList allJsonInConfig = scanResult.getResourcesMatchingWildcard("**/config/**/*.json");
ResourceList migrationFiles = scanResult.getResourcesMatchingWildcard("db/migration/**/*.sql");Resource resource = scanResult.getResourcesWithPath("config/database.properties").get(0);
// Basic resource information
String path = resource.getPath(); // "config/database.properties"
String relativePath = resource.getPathRelativeToClasspathElement(); // Relative to JAR/directory root
long lastModified = resource.getLastModified(); // Timestamp
long length = resource.getLength(); // Size in bytes
// Content access methods
try (InputStream inputStream = resource.open()) {
// Read as stream
Properties props = new Properties();
props.load(inputStream);
}
// Read all content at once
byte[] content = resource.load();
String textContent = resource.getContentAsString(); // UTF-8 decoded
// Read as ByteBuffer (memory-efficient for large files)
ByteBuffer buffer = resource.read();
// Remember to call resource.close() when done with ByteBuffer// Process multiple resources with forEach methods
scanResult.getResourcesWithExtension("json")
.forEachByteArray((resource, content) -> {
String json = new String(content, StandardCharsets.UTF_8);
System.out.println("Processing " + resource.getPath() + ": " + json.length() + " bytes");
// Parse and process JSON
try {
JsonNode jsonNode = objectMapper.readTree(json);
processConfiguration(resource.getPath(), jsonNode);
} catch (Exception e) {
System.err.println("Failed to parse JSON in " + resource.getPath() + ": " + e.getMessage());
}
});
// Process with input streams
scanResult.getResourcesWithExtension("properties")
.forEach(resource -> {
try (InputStream input = resource.open()) {
Properties props = new Properties();
props.load(input);
System.out.println("Loaded " + props.size() + " properties from " + resource.getPath());
processProperties(resource.getPath(), props);
} catch (IOException e) {
System.err.println("Failed to load properties from " + resource.getPath());
}
});Resource resource = scanResult.getResourcesWithPath("META-INF/MANIFEST.MF").get(0);
// Resource URIs and URLs
URI resourceURI = resource.getURI(); // Full resource URI
URL resourceURL = resource.getURL(); // Full resource URL
// Containing classpath element information
URI classpathElementURI = resource.getClasspathElementURI(); // JAR or directory URI
URL classpathElementURL = resource.getClasspathElementURL(); // JAR or directory URL
File classpathElementFile = resource.getClasspathElementFile(); // JAR or directory as File
// Module information (if resource is in a module)
ModuleRef moduleRef = resource.getModuleRef(); // null if not in module// Determine resource context
if (resource.getModuleRef() != null) {
// Resource is in a JPMS module
ModuleRef module = resource.getModuleRef();
System.out.println("Resource in module: " + module.getName());
System.out.println("Module location: " + module.getLocationStr());
} else {
// Resource is in traditional classpath
File classpathElement = resource.getClasspathElementFile();
if (classpathElement.isFile()) {
System.out.println("Resource in JAR: " + classpathElement.getName());
} else {
System.out.println("Resource in directory: " + classpathElement.getPath());
}
}// Get classpath information from ClassGraph (before scanning)
ClassGraph classGraph = new ClassGraph();
List<File> classpathFiles = classGraph.getClasspathFiles();
String classpathString = classGraph.getClasspath();
List<URI> classpathURIs = classGraph.getClasspathURIs();
List<URL> classpathURLs = classGraph.getClasspathURLs();
// Get classpath information from ScanResult (after scanning)
try (ScanResult scanResult = new ClassGraph().scan()) {
List<File> scannedFiles = scanResult.getClasspathFiles();
String scannedClasspath = scanResult.getClasspath();
List<URI> scannedURIs = scanResult.getClasspathURIs();
List<URL> scannedURLs = scanResult.getClasspathURLs();
}// Analyze different types of classpath elements
List<File> classpathElements = classGraph.getClasspathFiles();
for (File element : classpathElements) {
if (element.isFile()) {
if (element.getName().endsWith(".jar") || element.getName().endsWith(".zip")) {
System.out.println("JAR/ZIP file: " + element.getPath());
} else {
System.out.println("Other file: " + element.getPath());
}
} else if (element.isDirectory()) {
System.out.println("Directory: " + element.getPath());
}
}// Get module information from ClassGraph
List<ModuleRef> modules = classGraph.getModules();
ModulePathInfo modulePathInfo = classGraph.getModulePathInfo();
// Module path configuration
if (modulePathInfo != null) {
Set<String> modulePath = modulePathInfo.modulePath;
Set<String> addModules = modulePathInfo.addModules;
Set<String> patchModules = modulePathInfo.patchModules;
Set<String> addExports = modulePathInfo.addExports;
Set<String> addOpens = modulePathInfo.addOpens;
Set<String> addReads = modulePathInfo.addReads;
}// Access module information from scan results
try (ScanResult scanResult = new ClassGraph().enableSystemJarsAndModules().scan()) {
ModuleInfoList modules = scanResult.getModuleInfo();
for (ModuleInfo module : modules) {
System.out.println("Module: " + module.getName());
System.out.println("Location: " + module.getLocation());
// Module contents
ClassInfoList classesInModule = module.getClassInfo();
PackageInfoList packagesInModule = module.getPackageInfo();
System.out.println(" Classes: " + classesInModule.size());
System.out.println(" Packages: " + packagesInModule.size());
// Module annotations
AnnotationInfoList moduleAnnotations = module.getAnnotationInfo();
if (!moduleAnnotations.isEmpty()) {
System.out.println(" Annotations: " + moduleAnnotations.size());
}
}
}ModuleRef moduleRef = resource.getModuleRef();
if (moduleRef != null) {
// Basic module information
String name = moduleRef.getName(); // Module name
URI location = moduleRef.getLocation(); // Module location
String locationStr = moduleRef.getLocationStr(); // Location as string
File locationFile = moduleRef.getLocationFile(); // Location as File
// Module version
String version = moduleRef.getRawVersion(); // Version string if available
// Module contents
List<String> packages = moduleRef.getPackages(); // Packages in module
// Module system objects (via reflection)
Object moduleReference = moduleRef.getReference(); // ModuleReference
Object moduleLayer = moduleRef.getLayer(); // ModuleLayer
Object moduleDescriptor = moduleRef.getDescriptor(); // ModuleDescriptor
System.out.println("Module: " + name + " v" + version);
System.out.println("Location: " + locationStr);
System.out.println("Packages: " + packages.size());
}// Custom resource filtering with functional interface
ResourceList filteredResources = scanResult.getAllResources()
.filter(resource -> {
String path = resource.getPath();
return path.startsWith("META-INF/") &&
path.endsWith(".xml") &&
!path.contains("test");
});
// Process configuration files by type
Map<String, List<Resource>> configByType = scanResult.getResourcesMatchingWildcard("**/config/**")
.stream()
.collect(groupingBy(resource -> {
String path = resource.getPath();
int lastDot = path.lastIndexOf('.');
return lastDot > 0 ? path.substring(lastDot + 1).toLowerCase() : "unknown";
}));
System.out.println("Configuration files by type:");
configByType.forEach((type, resources) -> {
System.out.println(" " + type + ": " + resources.size() + " files");
});// Analyze SQL migration files
ResourceList sqlFiles = scanResult.getResourcesMatchingWildcard("db/migration/**/*.sql");
for (Resource sqlFile : sqlFiles) {
String content = sqlFile.getContentAsString();
// Extract version from filename (e.g., V001__create_users.sql)
String filename = sqlFile.getPath();
Pattern versionPattern = Pattern.compile("V(\\d+)__(.+)\\.sql");
Matcher matcher = versionPattern.matcher(filename);
if (matcher.find()) {
String version = matcher.group(1);
String description = matcher.group(2).replace("_", " ");
System.out.println("Migration " + version + ": " + description);
System.out.println(" File: " + filename);
System.out.println(" Size: " + content.length() + " characters");
// Analyze SQL content
boolean hasCreate = content.toUpperCase().contains("CREATE TABLE");
boolean hasInsert = content.toUpperCase().contains("INSERT INTO");
boolean hasUpdate = content.toUpperCase().contains("UPDATE ");
System.out.println(" Operations: " +
(hasCreate ? "CREATE " : "") +
(hasInsert ? "INSERT " : "") +
(hasUpdate ? "UPDATE " : ""));
}
}// Find Spring configuration files and their dependencies
ResourceList springConfigs = scanResult.getResourcesMatchingWildcard("**/spring/**/*.xml");
for (Resource config : springConfigs) {
String content = config.getContentAsString();
// Parse for import statements
Pattern importPattern = Pattern.compile("<import\\s+resource=[\"']([^\"']+)[\"']");
Matcher importMatcher = importPattern.matcher(content);
List<String> imports = new ArrayList<>();
while (importMatcher.find()) {
imports.add(importMatcher.group(1));
}
// Parse for bean definitions
Pattern beanPattern = Pattern.compile("<bean\\s+[^>]*class=[\"']([^\"']+)[\"']");
Matcher beanMatcher = beanPattern.matcher(content);
Set<String> referencedClasses = new HashSet<>();
while (beanMatcher.find()) {
referencedClasses.add(beanMatcher.group(1));
}
System.out.println("Spring config: " + config.getPath());
System.out.println(" Imports: " + imports.size());
System.out.println(" Bean classes: " + referencedClasses.size());
// Check if referenced classes exist in scan results
for (String className : referencedClasses) {
ClassInfo classInfo = scanResult.getClassInfo(className);
if (classInfo == null) {
System.out.println(" WARNING: Missing class " + className);
}
}
}ResourceList resources = scanResult.getResourcesWithExtension("properties");
// Get paths and URLs
List<String> paths = resources.getPaths();
List<String> relativePaths = resources.getPathsRelativeToClasspathElement();
List<URL> urls = resources.getURLs();
List<URI> uris = resources.getURIs();
// Access specific resources
ResourceList configResources = resources.get("application.properties"); // Resources with specific path
Resource firstConfig = configResources.isEmpty() ? null : configResources.get(0);
// Filtering
ResourceList prodConfigs = resources.filter(resource ->
resource.getPath().contains("prod") || resource.getPath().contains("production"));
// Combine with other ResourceLists
ResourceList allConfigs = resources.union(
scanResult.getResourcesWithExtension("yml"),
scanResult.getResourcesWithExtension("yaml")
);// Safe resource processing with proper cleanup
ResourceList jsonConfigs = scanResult.getResourcesWithExtension("json");
for (Resource resource : jsonConfigs) {
try {
// Option 1: Use try-with-resources for streams
try (InputStream input = resource.open()) {
JsonNode config = objectMapper.readTree(input);
processConfiguration(resource.getPath(), config);
}
// Option 2: Use convenience method for small files
String content = resource.getContentAsString();
JsonNode config = objectMapper.readTree(content);
processConfiguration(resource.getPath(), config);
// Option 3: Use ByteBuffer for large files (remember to close resource)
ByteBuffer buffer = resource.read();
try {
// Process buffer...
} finally {
resource.close(); // Important: close resource when using ByteBuffer
}
} catch (IOException e) {
System.err.println("Failed to process " + resource.getPath() + ": " + e.getMessage());
// Continue processing other resources
}
}try (ScanResult scanResult = new ClassGraph().scan()) {
// Check if any resources have changed since scan
boolean modified = scanResult.classpathContentsModifiedSinceScan();
if (modified) {
System.out.println("Classpath contents have been modified since scan");
// Get timestamp of most recent modification
long lastModified = scanResult.classpathContentsLastModifiedTime();
System.out.println("Last modified: " + new Date(lastModified));
// Consider rescanning for updated results
}
}ClassGraph's resource system provides comprehensive access to all classpath and module path resources with efficient processing capabilities and proper resource management, making it ideal for configuration discovery, asset processing, and build-time analysis tasks.
Install with Tessl CLI
npx tessl i tessl/maven-io-github-classgraph--classgraph