3

I'm trying to make my Java app modular, so that there will be the core base module that a client will have to aqcuire, but he/ she will be able to add additional features/ plugins as they come up, or as her needs extend. Like how IDEs like NetBeans go about it.

I plan on having a sub-directory, called modules/ plugins, in the clients pc where any plugin applications will be included as .jar files. The main module will, when a user launches the application, include these other plugins in the final item, so that, for example, a Stage will have buttons from both the main module and plugin modules on the same scene.

I'm fluent with elgg, which uses views to do this. Borrowing from this, I could have a HBox, for example on the main module that gets populated with Buttons from the different plugins on launching the application.

How can I go about having core_base_module.jar below call all plugins in the plugins directory below. In other words, how can I go about having one .jar file calling another .jar file?

Sample directory structure:

main dir/---core_base_module.jar
        /---plugins ---/chat.jar
                       /videoplayer.jar
                       /like.jar
                       /unlike.jar
                       /etc.jar
                       /etc_etc.jar
Program-Me-Rev
  • 6,184
  • 18
  • 58
  • 142
  • 1
    Look into build automation tools for the JVM, such as [Gradle](http://gradle.org/) or [Maven](http://maven.apache.org/) . – mikołak Aug 05 '15 at 15:27
  • @mikotak This is not a compile-time setup. The question is about loading new modules at runtime. – RealSkeptic Aug 05 '15 at 15:29
  • 1
    Well, I guess your core_module will have to scan the plugins directory for plugins. When you discover jar you should know what you want to load (some kind of class that extends from another that is part of your contract, i.e. you should know what you expect). Class loading should be done something like: http://stackoverflow.com/questions/11016092/how-to-load-classes-at-runtime-from-a-folder-or-jar – darijan Aug 05 '15 at 15:31
  • 1
    Look at this answer too: http://stackoverflow.com/questions/194698/how-to-load-a-jar-file-at-runtime – darijan Aug 05 '15 at 15:32
  • Look at this utility also https://github.com/decebals/pf4j – Daniel Jipa Aug 05 '15 at 15:37
  • I am curious why no one is suggesting OSGI ? – Saky Jun 02 '16 at 09:21

2 Answers2

4

You could do this manually by:

  1. Finding the plugins folder
  2. Scan the folder to get a list of all jars
  3. Create a classloader, hook up the jar file, then look in each jar for an implementation of the interface of your plugin API.
  4. Create an instance, and register it in your plugin registry

While you could do that all yourself, it might be easier to use the Service Loader API. This API is part of Java, and was created for this purpose. This is what spring uses to load plugins that can handle different XML tags in the spring XML config files. For instance look in spring-web.jar!/META-INF/services

Then you'll want your startup script to add all the JARs to your main class path like this:

java -cp plugins/* -jar app.jar

Or if you don't have a lauch script, you can do the same in java but creating a URLClassLoader passing it all the jar files that your want, then invoke your main class inside this new classloader.

More information:

David Roussel
  • 5,788
  • 1
  • 30
  • 35
1

If you really want to create a professional grade application, the ServiceLoader API suggested by @DavidRoussel is the way to go.

If you prefer something more lightweight, that you can do by hand, you can just use an API package containing only interfaces of abstract classes that you include in your application, and an implementation package containing actual classes that you provide as a separate jar. As you cannot create object only know by an interface through new, you will have to use a factory pattern and reflection. You should also use Class.forName("package.to.class") to test the existance of implementation at the beginning of your program.

Example assuming an interface Foo optionally implemented by FooImpl whose constructor takes a string argument the factory could be :

public class FooFactory {
    public static final String CLASSNAME = "...FooImpl";
    public static Foo createFoo(String key) {
        try {
            Class<?> fooclazz = Class.forName(CLASSNAME);
            Foo foo = (Foo) fooclazz.getConstructor(String.class).newInstance(key);
            return foo;
        }
        catch (Exception e) {
            // eventually process some exceptions
        }
        return null;
    }
}

You initially set a global boolean hasFoo (static member of main class for example) with :

    try {
        Class.forName(FooFactory.CLASSNAME);
        hasOpt = true;
    }
    catch (Exception e) {
        hasOpt = false;
    }

And you optionnaly use it :

    if (MainClass.hasOpt) {
        Foo foo = FooFactory.createFoo("bar");
        // do something with foo
    }
    else {
        // warning message : Option not present
    }

With this method, it is enough to add the implementation jar in classpath and restart the application to use it.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • But how do you know which class to load? What are the names of the plugins? – David Roussel Aug 10 '15 at 08:49
  • This would only have sense in a reasonably simple use case where all plugins are already known by main application. So it just try to load *classes* by fully qualified name. `FooFactory` must be included in main application – Serge Ballesta Aug 10 '15 at 08:57