Services dynamisch laden

Unicate

Erfahrenes Mitglied
Hi!

Dank meiner Frage bin ich auf den Trichter gekommen den ServiceLoader für ein PluginSystem zu verwenden.

Meine Frage nun:

Wie kann ich die Services dynamisch laden? Hier steht das ich das Interface in "META-INF/services" als einfache Datei anlege und darin stehen dann die Klassenpfade zu den Klassen.

Wie läuft das dann in >=2 verschiedenen Projekten?
Wie bekomm ich nun die Services aus meinem Plugin in meine Applikation?
Geht das überhaupt?
 
Zuletzt bearbeitet:

Thomas Darimont

Erfahrenes Mitglied
Hallo,

sobald die jars mit den META-INF/services ... Deskriptoren im classpath liegen werden über den ServiceLoader die Services gefunden.

Gruß Tom
 

Akeshihiro

Erfahrenes Mitglied
Ich habe das auch mal versucht, das Problem dabei ist aber scheinbar, dass man in der Hauptanwendung in der MANIFEST.MF im Classpath keine Ordner angeben kann, jedenfalls klappt das bei mir nicht. Führe das Programm aber via java -cp plugins/* etc aus, klappt es ... Da man aber keinem Endbenutzer zutrauen kann, dass er das Programm auf der CMD ausführt (die meisten wissen nicht mal was das ist), müsste man für jedes BS eine Starter-Batch/Shell anlegen und das finde ich ein wenig doof muss ich gestehen.

Also weiß jemand eine Lösung, bei der man sich das Rumgebatche sparen kann? Wie gesagt, im Classpath der MANIFEST.MF klappt das (bei mir) nicht mit Ordnern, nur direkt mit Jar-Verweisen ...
 

Thomas Darimont

Erfahrenes Mitglied
Hallo,

passend zu diesem Beispiel:
http://www.tutorials.de/forum/java/357126-wieder-mal-java-und-plug-ins.html#post1850187

Hierbei wird ein Verzeichnis nach jars gescanned. Die gefundenen Jars (unsere Plugins) werden dann dem Classpath des aktuellen Classloaders hinzugefügt. Danach kann der ServiceLoader die neuen Jars "sehen".

Java:
package de.tutorials.appserver;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ServiceLoader;

public class Server {

  String defaultPluginsLocation = "c:/temp/projects/plugins/";
  String pluginsLocation = System.getProperty("de.tutorials.pluginsLocation", defaultPluginsLocation);


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


  private void boot() throws Exception {
    registerPlugins();
    run();
  }


  private void run() {
    ServiceLoader<Application> applications = ServiceLoader.load(Application.class);
    System.out.println("Starting applications");
    for (Application application : applications) {
      application.start();
    }
  }


  void registerPlugins() throws MalformedURLException {
    File pluginsFolder = new File(pluginsLocation);

    File[] plugins = pluginsFolder.listFiles(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return name.endsWith(".jar");
      }
    });

    URL[] pluginUrls = new URL[plugins.length];
    for (int i = 0; i < pluginUrls.length; i++) {
      File pluginFile = plugins[i];
      System.out.println("Found plugin: " + pluginFile);
      pluginUrls[i] = pluginFile.toURI().toURL();
    }

    addToClassPathOf((URLClassLoader) Thread.currentThread().getContextClassLoader(), pluginUrls);
  }


  void addToClassPathOf(URLClassLoader urlClassLoader, URL... urls) {
    try {
      Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
      addUrlMethod.setAccessible(true);
      for (URL url : urls) {
        try {
          addUrlMethod.invoke(urlClassLoader, url);
        } catch (Exception e) {
          System.err.println(String.format("could not add: %s to classpath", url));
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Eine einfachere Möglichkeit wäre auch einfach einen passenden Starter (.exe) zu generieren welcher schon die passenden Java Classpath angaben mitbringt. Sprich ich generiere mir eine app.exe welche dann javaw.exe mit -cp app.jar;plugins/* de.tutorials.app.Main aufruft. Der exe gibt man dann noch schönes icon und schon sind die Anwender und Admins zufrieden ;-)

Gruß Tom
 

Akeshihiro

Erfahrenes Mitglied
Ja der Code gefällt mir ;)

Das mit dem Starter wollte ich ja nach Mögichkeit umgehen ;) Das meinte ich ja mit "Rumbatchen" ^^