Plugin-Schnittstelle mit Java entwickeln

veröffentlicht am 06. November 2010

Heute bin ich endlich dazu gekommen, mich mit einer Frage zu beschäftigen, die mir schon länger im Kopf herumschwirrt: wie mache ich ein Programm Plugin-fähig?  Im Folgenden zeige ich euch nun anhand von Java die Basis für eine einfache Plugin-Schnittstelle.

Zunächst einmal benötigst du ein Interface, um den Rahmen für ein Plugin festzulegen:

public interface Plugable {
   public String getName();
   public String getVersion();
}

Als nächstes erstellst du ein kleines Plugin zum Testen:

public class AntiSpam implements Plugable {

   @Override
   public String getName() {
      return "AntiSpam";
   }

   @Override
   public String getVersion() {
      return "1.0";
   }
}

Nun erstellst du das Hauptprogramm. Um den Code kompakt zu halten, habe ich die Fehlerbehandlung komplett rausgeworfen und den Quellcode an einem Stück geschrieben. Beides sollte man bei einem echten Produkt natürlich nicht machen :)

public class PluginExample {

   private ArrayList<Plugable> plugins = new ArrayList<Plugable>();

   public static void main(String[] args) throws Exception {
      new PluginExample();
   }

   public PluginExample() throws Exception {
      File[] jarFiles = new File("./plugins").listFiles(new FileFilter() {            
         @Override
         public boolean accept(File f) {
            return f.getName().toLowerCase().endsWith(".jar");
         }
      });

      URL[] urls = new URL[jarFiles.length];
      for(int i=0; i<jarFiles.length; i++) {
         urls[i] = jarFiles[i].toURI().toURL();
      }

      ClassLoader loader = new URLClassLoader(urls);

      for(File jarFile : jarFiles) {            
         JarInputStream jaris = new JarInputStream(new FileInputStream(jarFile));
         JarEntry entry;            
         while((entry = jaris.getNextJarEntry()) != null) {                
            if(entry.getName().toLowerCase().endsWith(".class")) {
               String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.');
               Class<?> cls = loader.loadClass(className);
               for(Class<?> i : cls.getInterfaces()) {
                  if(i.equals(Plugable.class)) {
                     plugins.add((Plugable)cls.newInstance());
                     break;
                  }
               }                    
            }
         }
      }

      for(Plugable plugin : plugins) {
         System.out.println(plugin.getName() + " " + plugin.getVersion());
      }
   }
}

In dem Hauptprogramm werden zunächst alle .jar-Dateien aus dem Ordner plugins ausgelesen. Damit der URLClassLoader die .jar-Dateien einlesen kann, werden URL-Objekte für die .jar-Dateien erzeugt. Danach werden alle .class-Dateien aus den .jar-Dateien eingelesen und auf das oben erstellte Interface hin überprüft. Wenn dieses Interface implementiert wurde, wird eine Instanz der entsprechenden Klasse erzeugt und zur Plugin-Liste hinzugefügt. Zum Abschluss werden zum Test die Namen und die Versionsnummern der Plugins ausgegeben.

Fazit

Wie man sieht, ist es gar nicht so schwer, eine Plugin-Schnittstelle für ein Java-Programm zu entwickeln. Natürlich ist dies nur ein minimales Beispiel und muss noch um Fehlerbehandlung und sinnvolle Methoden ergänzt werden. Zudem sollte der Code zur besseren Lesbarkeit refaktoriert werden.

Kommentare

Ich werfe mal OSGi und Jigsaw in den Raum :)

Kommentar #1 von Basti am 06. November 2010


Hast du dazu konkrete Beispiele?

Kommentar #2 von Patrick am 06. November 2010


Puuhhh… nicht direkt, aber einige Informationen liefert unter anderem:

http://www.ralfebert.de/rcpbuch/osgi/
http://it-republik.de/jaxenter/artikel/Das-OSGi-Framework-2221.html
http://it-republik.de/jaxenter/artikel/OSGi-in-kleinen-Dosen-%96-Bundles-und-Life-Cycle-2118.html

Zu Jigsaw findet man unter http://openjdk.java.net/projects/jigsaw/ etwas

Kommentar #3 von Basti am 06. November 2010


Wenn wir schon bei IoC Frameworks sind (OSGi, Jigsaw), sollte man sich auch noch Guice und Spring anschauen.

Um bei dem Beispielcode im Blog zu bleiben, das geht einfacher und eleganter mit dem Service Provider Mechanismus: http://download.oracle.com/javase/1.3/docs/guide/jar/jar.html#Service Provider

Kommentar #4 von Fabian Ritzmann am 07. November 2010


Hinterlasse einen Kommentar