NetBeans Platform Module System: A Minimal, Trust‑Bounded Design for Safe Plugin Development
Discover how NetBeans Platform’s module system isolates plugins, enforces dependencies, and keeps configuration separate. Learn the minimal manifest design, operational checks, failure modes, and when to rethink the architecture for JPMS or native runtimes.
14 Jun 2026, 14:33 UTC

Why a Dedicated Module Layer Matters
When building a NetBeans Platform application, you often need to ship third‑party plugins or evolve features without touching the core. The module system isolates each plugin in its own class loader, enforces explicit dependencies, and keeps configuration data separate. This guarantees that a buggy module can’t corrupt the whole JVM, and it lets you install or remove modules at runtime without a restart.
Key Requirements
- Independent development and deployment of modules.
- Versioned, declarative dependencies to avoid “it worked on my machine” bugs.
- Class‑loader isolation to prevent static state leakage.
- Dynamic install/uninstall without JVM restart.
- Stable API/SPI contract for extension points.
The Smallest Viable Design
A NetBeans module is simply a JAR with a manifest that describes its identity and relationships. The manifest must contain at least the following attributes:
OpenIDE-Module: com.example.foo
OpenIDE-Module-Specification-Version: 1.0
OpenIDE-Module-Implementation-Version: 1.0.3
OpenIDE-Module-Provides: com.example.foo.api
OpenIDE-Module-Requires: com.example.bar 1.0
OpenIDE-Module-OpenIDE-API: 1.0
OpenIDE-Module-Resources: path to resources
OpenIDE-Module-OpenIDE-API: 1.0
Only the packages listed in OpenIDE-Module-Provides are visible to other modules. All other packages are private to the module. The module system reads this manifest, builds a directed‑acyclic graph, and creates a dedicated ClassLoader per module.
Trust and Data Boundaries
Modules can only see classes from packages they declare as Provides or from packages of modules they list in Requires. The platform enforces package visibility: public classes are exported, friend classes are visible only to explicitly listed friend modules, and private classes are hidden entirely.
Persistent settings are stored under ${userdir}/config/Preferences/module‑code‑name/. This keeps user preferences isolated per module, preventing accidental overrides.
Operational Checks
- Graph consistency at startup: NetBeans logs any cycles or unsatisfied dependencies in
messages.log. A module with a missing dependency is disabled automatically. - Export validation: The platform verifies that every package listed in
Providesactually exists in the JAR. Mismatches generate a warning. - ClassLoader memory monitoring: After uninstalling a module, run
jcmd VM.classloader_statsto ensure the module’s ClassLoader is eligible for GC. Persistent references indicate leaks. - Lookup registration checks: If a provider is unloaded but its service remains registered, a
NoClassDefFoundErrorwill surface. The platform logs such mismatches. - Layered filesystem integrity: XML layer files are merged at runtime. Use
Tools > Options > Module Developmentto view the overlay order and avoid resource collisions.
Common Failure Modes
- Missing or incompatible dependencies: Modules are disabled, and the user sees a warning in the module manager.
- ClassLoader leaks: Repeated enable/disable cycles can grow Metaspace, leading to
java.lang.OutOfMemoryError: Metaspace. Profile withjcmd VM.classloader_stats. - Stale Lookup registrations: Providers removed from the classpath leave dangling service references, causing runtime errors.
- Layer ordering conflicts: Two modules providing the same resource path in their XML layer can result in unpredictable overrides.
When to Re‑think the Design
Consider a redesign if:
- You adopt the Java Platform Module System (JPMS) as the primary modularity mechanism. JPMS requires
module-info.javafiles and changes how exports and requires are expressed. - You target a runtime that restricts dynamic classloading, such as GraalVM native images. In that case you must pre‑compile all modules into the image.
- Fine‑grained sandboxing is needed beyond classloader isolation, e.g., a per‑module SecurityManager replacement.
Concrete Example: Two Inter‑Dependent Modules
Assume a minimal Maven project generated with the NetBeans Platform archetype. Add two modules: com.example.api and com.example.impl.
# pom.xml snippet for com.example.api
<project>
<groupId>com.example</groupId>
<artifactId>api</artifactId>
<version>1.0.0</version>
<packaging>nbm</packaging>
<properties>
<nbm.module.name>com.example.api</nbm.module.name>
<nbm.module.version>1.0.0</nbm.module.version>
</properties>
</project>
# pom.xml snippet for com.example.impl
<project>
<groupId>com.example</groupId>
<artifactId>impl</artifactId>
<version>1.0.0</version>
<packaging>nbm</packaging>
<properties>
<nbm.module.name>com.example.impl</nbm.module.name>
<nbm.module.version>1.0.0</nbm.module.version>
</properties>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
After building, run the application and open Tools > Options > Module Development. Verify that com.example.impl lists com.example.api as a dependency, and that the class loader hierarchy shows separate loaders.
Practical Verification Steps
- Enable verbose class loading: Launch NetBeans with
-J-verbose:classto see which module’s ClassLoader loads each class. - Check module metadata: Inspect
${userdir}/config/Modules/for thecom.example.api.nbmfile; it should contain the correct manifest entries. - Profile memory after uninstall: Disable
com.example.impl, then runjcmd VM.classloader_statsto confirm its ClassLoader is no longer present.
Limitations and Caveats
- Manifest attributes are fragile; a typo in a package name silently breaks wiring. Use the NetBeans Module Project wizard to generate correct manifests.
- Friend packages create implicit coupling. Treat them as semi‑public API and document version changes carefully.
- Layered filesystem ordering is non‑deterministic unless explicitly positioned. Use
positionattributes in XML layers to guarantee order. - Dynamic install/uninstall relies on GC. On JDK 8+ Metaspace, leaks are harder to detect; monitor with
jcmdtools. - Mixing JPMS and NetBeans modules requires
--add-exportsflags on the command line to expose internal APIs.
Conclusion
By sticking to the minimal manifest‑based design, you get a clean, isolated module ecosystem that satisfies the core requirements of plug‑in development. Operational checks and awareness of common failure modes let you maintain a healthy platform even as you add or remove modules at runtime. When the ecosystem evolves—JPMS adoption, native images, or stricter sandboxing—you’ll know exactly which parts of the design to revisit.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.