<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luis Iñesta Gelabert</title>
    <description>The latest articles on DEV Community by Luis Iñesta Gelabert (@luiinge).</description>
    <link>https://dev.to/luiinge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F573604%2Fbadaa157-6103-4547-9cee-3d1df5893c83.png</url>
      <title>DEV Community: Luis Iñesta Gelabert</title>
      <link>https://dev.to/luiinge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luiinge"/>
    <language>en</language>
    <item>
      <title>JExten: Building a Robust Plugin Architecture with Java Modules (JPMS)</title>
      <dc:creator>Luis Iñesta Gelabert</dc:creator>
      <pubDate>Thu, 15 Jan 2026 16:45:48 +0000</pubDate>
      <link>https://dev.to/luiinge/jexten-building-a-robust-plugin-architecture-with-java-modules-jpms-47l2</link>
      <guid>https://dev.to/luiinge/jexten-building-a-robust-plugin-architecture-with-java-modules-jpms-47l2</guid>
      <description>&lt;h2&gt;
  
  
  1. Motivation: The Road to Modular Isolation
&lt;/h2&gt;

&lt;p&gt;When building extensible applications in Java, developers often start with a simple question: "How can I let users add functionality without recompiling the core application?" The journey usually begins with the standard &lt;code&gt;java.util.ServiceLoader&lt;/code&gt;, which provides a simple mechanism for discovering implementations of an interface.&lt;/p&gt;

&lt;p&gt;However, as the application grows, a critical problem emerges: &lt;strong&gt;"Classpath Hell."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you have a host application that uses &lt;code&gt;library-v1&lt;/code&gt;. You create a plugin system, and someone writes a "Twitter Plugin" that requires &lt;code&gt;library-v2&lt;/code&gt;. If you run everything on the same flat classpath, you get a conflict. Either the host crashes because it gets the wrong version of the library, or the plugin fails. You cannot have two versions of the same library on the classpath without facing the risk of runtime exceptions such as &lt;code&gt;ClassDefNotFoundError&lt;/code&gt; or &lt;code&gt;NoSuchMethodError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This was the driving motivation behind &lt;strong&gt;JExten&lt;/strong&gt;. I needed a way to strictly encapsulate plugins so that each one could define its own dependencies without affecting the host or other plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter JPMS (Java Platform Module System)
&lt;/h3&gt;

&lt;p&gt;Java 9 introduced the Module System (JPMS), which provides strong encapsulation and explicit dependency graphs. It allows us to create isolated "layers" of modules.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Boot Layer&lt;/strong&gt;: The JVM and platform modules.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Host Layer&lt;/strong&gt;: The core application and its dependencies.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Plugin Layers&lt;/strong&gt;: Dynamically created layers on top of the host layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging JPMS ModuleLayers, JExten allows Plugin A to rely on &lt;code&gt;Jackson 2.14&lt;/code&gt; while Plugin B relies on &lt;code&gt;Jackson 2.10&lt;/code&gt;, and both can coexist peacefully within the same running application.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Architecture and Design
&lt;/h2&gt;

&lt;p&gt;JExten is designed to be lightweight and annotation-driven, abstracting away the complexity of raw ModuleLayers while providing powerful features like Dependency Injection (DI) and lifecycle management.&lt;/p&gt;

&lt;p&gt;The architecture consists of three main pillars:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Extension Model
&lt;/h3&gt;

&lt;p&gt;At the core, JExten uses a clean separation between the "contract" (API) and the "implementation".&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extension Point (&lt;code&gt;@ExtensionPoint&lt;/code&gt;)&lt;/strong&gt;: An interface defined in the host application (or a shared API module) that defines what functionality can be extended.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ExtensionPoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extension (&lt;code&gt;@Extension&lt;/code&gt;)&lt;/strong&gt;: The concrete implementation provided by a plugin.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Extension&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Priority&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;HIGH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeGateway&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that you can make use of the ExtensionManager without the PluginManager. This is useful for testing or when you want to use JExten in a non-plugin environment and all the extensions are already available in the modulepath.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Manager Split
&lt;/h3&gt;

&lt;p&gt;To separate concerns, the library splits responsibilities into two distinct managers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PluginManager ("The Physical Layer")&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  This component handles the raw artifacts (JARs/ZIPs).&lt;/li&gt;
&lt;li&gt;  It verifies integrity using SHA-256 checksums ensuring that plugins haven't been tampered with.&lt;/li&gt;
&lt;li&gt;  It builds the JPMS &lt;code&gt;ModuleLayer&lt;/code&gt; graph. It reads the &lt;code&gt;plugin.yaml&lt;/code&gt; manifest, resolves dependencies (from a local cache or Maven repo), and constructs the classloading environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ExtensionManager ("The Logical Layer")&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Once layers are built, this component takes over.&lt;/li&gt;
&lt;li&gt;  It scans the layers for classes annotated with &lt;code&gt;@Extension&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  It manages the lifecycle of these extensions (Singleton, Session, or Prototype scopes).&lt;/li&gt;
&lt;li&gt;  It handles &lt;strong&gt;Dependency Injection&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Dependency Injection (DI)
&lt;/h4&gt;

&lt;p&gt;Since plugins run in isolated layers, standard DI frameworks (like Spring or Guice) can sometimes be "too heavy" or tricky to configure across dynamic module boundaries. JExten includes a built-in, lightweight DI system.&lt;/p&gt;

&lt;p&gt;You can simply use &lt;code&gt;@Inject&lt;/code&gt; to wire extensions together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Extension&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyPluginService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PaymentGateway&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Automatically injects the highest priority implementation&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works seamlessly across module boundaries. A plugin can inject a service provided by the host, or even a service provided by another plugin (if the module graph allows it).&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Usage Example
&lt;/h2&gt;

&lt;p&gt;Here is a quick look at how to define an extension point, implement it in a plugin, and use it in your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  I. Define an Extension Point
&lt;/h3&gt;

&lt;p&gt;Create an interface and annotate it with &lt;code&gt;@ExtensionPoint&lt;/code&gt;. This is the contract that plugins will implement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ExtensionPoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  II. Implement an Extension
&lt;/h3&gt;

&lt;p&gt;In your plugin module, implement the interface and annotate it with &lt;code&gt;@Extension&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Extension&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FriendlyGreeter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  III. Discover and Use
&lt;/h3&gt;

&lt;p&gt;In your host application, use the &lt;code&gt;ExtensionManager&lt;/code&gt; to discover and invoke extensions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Initialize the manager&lt;/span&gt;
        &lt;span class="nc"&gt;ExtensionManager&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExtensionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pluginManager&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get all extensions for the Greeter point&lt;/span&gt;
        &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getExtensions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Greeter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IV. Package your Extension(s) as a Plugin
&lt;/h3&gt;

&lt;p&gt;Finally, use the &lt;code&gt;jexten-maven-plugin&lt;/code&gt; Maven plugin to check your &lt;code&gt;module-info.java&lt;/code&gt; at compile time and package your extension into a ZIP bundle that includes all dependencies and the generated &lt;code&gt;plugin.yaml&lt;/code&gt; manifest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.myjtools.jexten&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jexten-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;generate-manifest&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;assemble-bundle&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;hostModule&amp;gt;&lt;/span&gt;com.example.app&lt;span class="nt"&gt;&amp;lt;/hostModule&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then install the generated ZIP bundle to your host application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Path&lt;/span&gt; &lt;span class="n"&gt;pluginDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"plugins"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Create plugin manager&lt;/span&gt;
        &lt;span class="nc"&gt;PluginManager&lt;/span&gt; &lt;span class="n"&gt;pluginManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PluginManager&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"org.myjtools.jexten.example.app"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Application ID&lt;/span&gt;
            &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClassLoader&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;pluginDir&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Install plugin from bundle&lt;/span&gt;
        &lt;span class="n"&gt;pluginManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;installPluginFromBundle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;pluginDir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-plugin-1.0.0.zip"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Create extension manager with plugin support&lt;/span&gt;
        &lt;span class="nc"&gt;ExtensionManager&lt;/span&gt; &lt;span class="n"&gt;extensionManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExtensionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pluginManager&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

         &lt;span class="c1"&gt;// Get extensions from the plugin&lt;/span&gt;
        &lt;span class="n"&gt;extensionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getExtensions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Greeter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;greet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Comparison with Other Solutions
&lt;/h2&gt;

&lt;p&gt;Choosing the right plugin framework depends on your specific needs. Here is how JExten compares to established alternatives:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://pf4j.org/" rel="noopener noreferrer"&gt;PF4J (Plugin Framework for Java)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;PF4J&lt;/strong&gt; is a mature, lightweight plugin framework that relies on &lt;strong&gt;ClassLoader isolation&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Isolation&lt;/strong&gt;: PF4J uses custom ClassLoaders to isolate plugins. JExten uses &lt;strong&gt;JPMS ModuleLayers&lt;/strong&gt;. The latter is the "native" Java way to handle isolation since Java 9, strictly enforcing encapsulation at the JVM level.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Modernity&lt;/strong&gt;: While PF4J is excellent, JExten is designed specifically for the modern modular Java ecosystem (Java 21+), taking advantage of module descriptors (&lt;code&gt;module-info.java&lt;/code&gt;) for defining dependencies rather than custom manifests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.osgi.org/" rel="noopener noreferrer"&gt;OSGi&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OSGi&lt;/strong&gt; is the gold standard for modularity, powering IDEs like Eclipse.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Complexity&lt;/strong&gt;: OSGi is powerful but comes with a steep learning curve and significant boilerplate (Manifest headers, Activators, complex service dynamics). JExten offers a fraction of the complexity ("OSGi Lite") by focusing on the 80% use case: strictly isolated extensions with simple dependency injection, without requiring a full OSGi container.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Runtime&lt;/strong&gt;: OSGi brings a heavy runtime. JExten is a lightweight library that sits on top of the standard JVM features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/moditect/layrry" rel="noopener noreferrer"&gt;Layrry&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Layrry&lt;/strong&gt; is a launcher and API for executing modular Java applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Scope&lt;/strong&gt;: Layrry focuses heavily on the &lt;em&gt;configuration and assembly&lt;/em&gt; of module layers (often via YAML/TOML) and acts as a runner. JExten focuses on the &lt;em&gt;programming model&lt;/em&gt; within those layers.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Features&lt;/strong&gt;: Layrry is great for constructing the layers, but it doesn't provide an opinionated application framework. JExten provides the "glue" code—Extension Points, Dependency Injection, and Lifecycle Management—that you would otherwise have to write yourself when using raw Module Layers or Layrry.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;JExten&lt;/th&gt;
&lt;th&gt;PF4J&lt;/th&gt;
&lt;th&gt;OSGi&lt;/th&gt;
&lt;th&gt;Layrry&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JPMS ModuleLayers&lt;/td&gt;
&lt;td&gt;File/ClassLoader&lt;/td&gt;
&lt;td&gt;Bundle ClassLoaders&lt;/td&gt;
&lt;td&gt;JPMS ModuleLayers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java Annotations&lt;/td&gt;
&lt;td&gt;Properties/Manifest&lt;/td&gt;
&lt;td&gt;Manifest Headers&lt;/td&gt;
&lt;td&gt;YAML/TOML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built-in (&lt;code&gt;@Inject&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;External (Spring/Guice)&lt;/td&gt;
&lt;td&gt;Declarative Services&lt;/td&gt;
&lt;td&gt;None (ServiceLoader)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;JExten is a lightweight, annotation-driven plugin framework that leverages JPMS ModuleLayers to provide isolation and dependency management. It is designed to be easy to use and understand, with a focus on simplicity and ease of use. &lt;/p&gt;

&lt;p&gt;Finally, keep in mind that JExten is still in its early stages, and there is much room for improvement. Feel free to contribute to the project on GitHub and/or engage in a discussion in the issues section. Link to the repository is &lt;a href="https://github.com/org-myjtools/jexten" rel="noopener noreferrer"&gt;here&lt;/a&gt; .&lt;/p&gt;

</description>
      <category>java</category>
      <category>opensource</category>
      <category>plugins</category>
      <category>jpms</category>
    </item>
  </channel>
</rss>
