Playground repository for experimenting with multi-module Maven setup and the ServiceLoader class in Java.
A proof-of-concept project that uses Java's ServiceLoader to dynamically discover and register extension implementations at runtime — no hardcoded references required. Application entry point is ApplicationLauncher.java.
Extensions are grouped into families, each representing a type of capability (e.g. a weather extension family). A family can have multiple variants, each configured independently to target different endpoints, retry limits, etc. At startup, variant configuration files are read from disk, merged with per-family defaults, and used to initialise each extension instance.
The extensions are implementable either by the example functional extension interfaces or by the functional extension abstract classes, the later of which validates correct typing at runtime and prevents ClassCastException errors.
- JDK 25 — install from the official site or via Homebrew:
brew install openjdk@25 - Maven 3 — install from the official site or via Homebrew:
brew install maven
This project includes version files for jenv (.java-version) and mvnvm (mvnvm.properties). Both can be installed via Homebrew:
brew install jenv mvnvmjenv requires additional shell configuration, after which the installed JDK can be registered with:
jenv add /opt/homebrew/opt/openjdk@25The application reads extension configuration files from a root directory. The path is resolved in order of precedence:
- JVM system property —
-DCONFIGURATION_ROOT_PATH=<path> - Environment variable —
CONFIGURATION_ROOT_PATH=<path> - Default —
~/config
The path must point to an existing directory. Each extension implementation (family) has its own subdirectory containing a defaults.json (shared base values) file and one or more variant files that are merged on top of the default extension configuration values when read in. An example of such can be found in the Extension A defaults.
From the project root:
mvn clean installThis produces a thin jar for the launcher along with all dependency jars in launcher/target/lib/.
(optionally) to build the extension outside the classpath Extension C from the project root and copy the jar to the extensions directory:
mkdir -p ./runtime-extensions && mvn clean install -f ./extensions/extension-c/pom.xml && cp ./extensions/extension-c/target/extension-c-1.0-SNAPSHOT.jar ./runtime-extensionsThis produces a thin jar for the external extension simulating runtime loaded JARs compiled externally to the project.
java -DCONFIGURATION_ROOT_PATH="$(pwd)/config" -DEXTENSION_ROOT_PATH="$(pwd)/runtime-extensions" -jar launcher/target/launcher-1.0-SNAPSHOT.jarThe launch configuration sets CONFIGURATION_ROOT_PATH to the workspace config/ and EXTENSION_ROOT_PATH to the workspace extensions/ (if relevant) directories respectively. Open the Run and Debug panel (⇧⌘D), select Java Application, and press F5.