forked from jnode/jnode
-
Notifications
You must be signed in to change notification settings - Fork 0
Plugin Dependency Checker
opencode-agent[bot] edited this page May 10, 2026
·
1 revision
An Ant build task that validates plugin dependency declarations against actual bytecode usage, ensuring all required plugins are declared and unnecessary imports are avoided.
The PluginDependencyChecker is an Ant task (org.jnode.build.dependencies.PluginDependencyChecker) that performs build-time validation of JNode's plugin dependency graph. It parses plugin XML descriptors and analyzes compiled JAR bytecode to detect:
- Unresolved class dependencies (classes used but not in any plugin)
- Unnecessary plugin imports (plugins imported but not actually used)
- Missing import declarations (classes used from imported plugins but not exported)
- Unresolved plugin references
- Useless plugins (no runtime, extensions, or plugin class)
| Class / File | Role |
|---|---|
PluginDependencyChecker.java |
Main Ant task, orchestrates the validation pipeline |
Inner Plugin class |
Represents a plugin, tracks contained and used classes |
Inner Fragment class |
Represents a fragment attached to a host plugin |
Inner JarFiles class |
Maps plugin IDs to JAR file paths |
1. Read descriptor XMLs → PluginDescriptor objects
2. Match descriptors to JAR files (by plugin ID + version)
3. For each JAR:
a. Extract all .class entries → containedClasses
b. Analyze bytecode (BCEL) → usedClasses
4. For each plugin:
a. collectUnmatchedDependencies() — classes used but not in any plugin
b. isNotUseful() — plugin with no runtime, extensions, or plugin class
c. assortClassesContainedInImportedPlugins() — imports vs actual usage
The task uses Apache BCEL to analyze class files. For each contained class, it traces references from:
analyzeClass(className, javaClass)
├── analyzeSuperClass() // getSuperclassName()
├── analyzeInterfaces() // getInterfaceNames()
├── analyzeFields() // field types from Field.getType()
├── analyzeMethods() // argument & return types
└── analyzeConstantPool() // CONSTANT_Class, CONSTANT_NameAndTypeThis builds a usedClasses map: usingClass → Set<usedClassName>.
private boolean collectUnmatchedDependencies(...) {
// For every class used by the plugin...
for (String className : usedClasses.get(usingClass)) {
// Check if it's contained in ANY known plugin
if (!containedClasses.containsKey(className)) {
// UNRESOLVED — error
}
}
}private boolean assortClassesContainedInImportedPlugins(...) {
// Check each declared prerequisite plugin
for (PluginPrerequisite prerequisite : plugin.descr.getPrerequisites()) {
Plugin usedPlugin = findPlugin(idOfUsedPlugin);
// Does the plugin actually use any classes from the import?
if (!allClasses.removeAll(usedPlugin.containedClasses)) {
// UNNECESSARY — plugin imports but doesn't use it
}
}
}Used in all/build.xml:
<pluginDependencyChecker>
<descriptors .../>
<plugins .../>
</pluginDependencyChecker>The task is configured via nested <descriptors> and <plugins> FileSets that match descriptor XMLs and JAR files respectively.
-
Fragment handling — Fragments are treated as plugins but track their host plugin separately via
getFullPluginIdOfOwningPlugin(). - System plugins excluded — Classes from system plugins are removed from the undeclared set before reporting.
-
Recursive analysis —
assortClassesContainedInImportedPlugins()recursively validates transitive dependencies. -
BCEL SyntheticRepository — Class loading uses
SyntheticRepositorywith the JAR itself as the classpath, enabling offline analysis. - Missing JAR warning — If no JAR exists for a descriptor, only a warning is issued (not an error) to allow partial builds.
-
Type signature parsing — Uses regex
\u004C[a-zA-Z_0-9/\u002E\u0024]*;to extract L-prefixed type descriptors from bytecode signatures.
- Build-System — Where this task is invoked during build
- Plugin-System — Plugin architecture and classloader hierarchy
- Plugin-Descriptor-Schema — XML descriptor format validated by this task