Dynamic JVM Plugin Engine
I built a hot-swappable backend that loads, compiles, and runs external Java code at runtime without restarting the server. Here’s what it does and how it works.
Repo: MohammedHamzaMalik/plugin-server
What it is
Standard Java apps need every .class file on the classpath at startup. This project goes further: a custom ClassLoader engine that:
- Watches a directory for new plugin
.classfiles - Loads them into the JVM on the fly
- Exposes them via REST so you can trigger plugin execution without restarting
So you can drop a new plugin in, and the server picks it up and runs it—zero-downtime extensibility.
Tech stack and mechanisms
| Area | Choice |
|---|---|
| Framework | Spring Boot 3.x / Java 21 |
| Loading | Custom java.lang.ClassLoader |
| Watching | Java NIO WatchService |
| Execution | Java Reflection API |
| Concurrency | CommandLineRunner, background thread, ConcurrentHashMap |
Architecture highlights
-
ClassLoader isolation — The custom ClassLoader loads raw bytecode from a plugin directory and uses the Spring Boot application ClassLoader as parent. That keeps external code compatible with your interfaces and avoids
ClassCastExceptionwhen passing plugin instances around. -
Non-blocking watch — A dedicated background thread runs the file watcher so it never blocks the main Tomcat thread.
-
Thread-safe registry — Plugins are stored in a
ConcurrentHashMap, so registration and lookup are safe under concurrent HTTP requests.
How to run and test hot-swap
-
Start the server
./mvnw spring-boot:run— it creates a./pluginsdirectory and watches it. -
Check loaded plugins
curl http://localhost:8080/api/plugins→ initially[]. -
Implement a plugin (outside the project), e.g.
DataCleanupPlugin.javaimplementingTaskPlugin, withgetName()andexecute(). -
Compile against the server’s classes:
javac -cp path/to/plugin-server/target/classes DataCleanupPlugin.java -
Drop
DataCleanupPlugin.classinto./plugins. The console should log registration. -
Run via API
curl -X POST http://localhost:8080/api/plugins/DataCleanup/run
Why it matters for learning
This project ties together:
- ClassLoaders — how the JVM loads classes and how to load bytecode yourself
- NIO WatchService — reactive file/directory monitoring
- Spring Boot — embedding custom lifecycle (e.g.
CommandLineRunner) and REST APIs - Concurrency — safe shared state and background tasks
It’s a concrete step on my java-backend-roadmap and a good base for experimenting with plugin APIs, security boundaries, or versioning later.