javapluginscdipluggable

How to write a pluggable application using CDI (weld)?


I want to write a small SE application to run OS-specific commands. These commands are supplied to the main application as "plugins", in order to be able to add new command implementation at runtime. This is a mandatory request: that no redeploy of the main application be required to execute new plugins.

So, I went about trying to setup something using CDI:

// On a common dependency 
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Plugin {
    String value();
}

public interface Pluggable {
    void execute(PluginContext context);    
} 

A plugin implementation would be something like this (in a separate jar):

@Plugin("greeting")
public class GreetingPlugin implements Pluggable {
    public void execute(PluginContext context) {
        String greet = context.get("hello.world");
        System.out.println(String.format("Hello, %s", greet));
    }
}

And that works fine, when loaded using the following injection point, plus a select() call:

@Inject @Any Instance<Pluggable> plugin;

However, I wonder what's the best approach to add the ability to add classes at runtime, so that the event of adding a new file to the "plugins" directory automatically registers it on the ClassLoader and the Weld container.

Any suggestions? Pitfalls I've not yet considered? My experience with CDI is rather limited, and perhaps it might not even be a suitable choice for this problem.

Disclaimer OSGI is ruled out, due to company licensing policy. Can't help on that front.


Solution

  • It seems to me that what you're looking for has been feature requested for CDI 1.1, but it is very unlikely that it will make its way in even for CDI 2.0, see this JIRA. There are even several alternatives discussed in there that you might want to consider.

    The simple answer is - no, CDI doesn't provide such functionality by itself. That said, assuming you can manage to implement the dynamic class loading yourself, in an SE environment it is trivial to simply restart the CDI container essentially dynamically re-configuring your application with the newly loaded plugins - see Bootstrapping CDI.

    So you'd watch your /plugins directory for changes. This would in turn trigger the dynamic class loading and then a Weld restart. The dynamic class loading part can get hairy so I'll let you figure that out.

    Hope this helps.