Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java dynamically load plugin

I want to make an application that can dynamically load plug-ins but I've not found any literature on Internet.

The difficult thing is: I don't know the name in advance.

For example I've a Plugin interface:

public interface Plugin {
    public static Plugin newPlugin();
    public void executePlugin(String args[]);
}

So that every Class implementing Plugin in the jar file are instantiated in a list:

Method method = classToLoad.getMethod ("newPlugin");
mylist.add(method.invoke(null);
  1. First problem is, I cannot have a static method in an interface.
  2. Second problem is, I don't know how to find all classes that implement an interface

Thanks for your help.

like image 224
FloFu Avatar asked Dec 13 '12 23:12

FloFu


2 Answers

So it seems like you want to dynamically discover Classes that implement a specific interface (e.g., Plugin) at runtime. You have basically two choices for this:

  1. Use a component framework like osgi
  2. Use Java's internal discovery process (ServiceLoader)

Since there are many good tutorials on osgi (also small ones), I will not detail that here. To use Java's internal discovery process, you need to do the following:

  • Bundle all "new" classes that you wish to discover into a jar file
  • Create a new file inside the jar file: META-INF/services/package.Plugin You must use the full package qualifier here
  • This file is a simple text file and contains the fully qualified name of each class implementing Plugin in that jar-file
  • Place that jar file into the classpath of your (potentially already running) application
  • Discover the services:

Service discovery is done like this:

ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class)
for (Plugin p : loader) {
    // do something with the plugin
}

There are more details here: http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html

As for static methods in interfaces: not possible. The semantics of that would also be somewhat weird as static methods are accessible without an instance of a class, and interfaces just define the methods, without any functionality. Thus, static would allow to call Interface.doSomething() whereas the interface does not define any functionality, this leads just to confusion.

edit:

added description what should be in the meta-file

like image 194
SirRichie Avatar answered Oct 14 '22 07:10

SirRichie


Regarding your first problem, not being able to have static methods in the interface, my suggestion is to simply use the interface has a marker and instantiate it.

You're plugin interface can simply be:

public interface Plugin {
  public void executePlugin(String args[]);
}

And then you can do:

if (someClass instanceOf Plugin) {
  mylist.add(someClass.newInstance());
}

This leads to the second question, how will you get the someClass reference. There is no standard way to find all classes implementing a given interface in your classpath, although, an approach you can do is scan the jars in your classpath, if a given file ends with .class determine it's full qualified name through it's path inside the jar and use Class.forName() method to materialize the Class.

In pseudo code something like this:

for each jar in your classpath {
  for each file in JarFile {
    if (file ends with .class) {
       materialize class using Class.forName
     } 
  }
}

With the Class instance you can check if it's implementing your Plugin interface.

Also keep in mind that if you need to add any context to your plugins you can create a constructor in every plugin that receives your context object, instead of having the default constructor. In such case instead of using newInstance() you would have to get the constructor with arguments you wanted via reflection.

like image 27
pabrantes Avatar answered Oct 14 '22 05:10

pabrantes