java - Dynamically loading plugin jars using ServiceLoader -
i'm trying create plugin system application, , want start simple. every plugin should packed in .jar file , implement simpleplugin interface:
package plugintest; public interface simpleplugin { public string getname(); } now i've created implementation of simpleplugin, packed in .jar , put in plugin/ subdirectory of main application:
package plugintest; public class plugintest implements simpleplugin { public string getname() { return "i'm plugin!"; } } in main application, want instance of plugintest. i've tried 2 alternatives, both using java.util.serviceloader.
1. dynamically extending classpath
this uses known hack use reflection on system class loader avoid encapsulation, in order add urls the classpath.
package plugintest.system; import plugintest.simpleplugin; import java.io.file; import java.io.ioexception; import java.net.url; import java.net.urlclassloader; import java.util.iterator; import java.util.serviceloader; public class manageplugins { public static void main(string[] args) throws ioexception { file loc = new file("plugins"); extendclasspath(loc); serviceloader<simpleplugin> sl = serviceloader.load(simpleplugin.class); iterator<simpleplugin> apit = sl.iterator(); while (apit.hasnext()) system.out.println(apit.next().getname()); } private static void extendclasspath(file dir) throws ioexception { urlclassloader sysloader = (urlclassloader) classloader.getsystemclassloader(); url urls[] = sysloader.geturls(), udir = dir.touri().tourl(); string udirs = udir.tostring(); (int = 0; < urls.length; i++) if (urls[i].tostring().equalsignorecase(udirs)) return; class<urlclassloader> sysclass = urlclassloader.class; try { method method = sysclass.getdeclaredmethod("addurl", new class[]{url.class}); method.setaccessible(true); method.invoke(sysloader, new object[] {udir}); } catch (throwable t) { t.printstacktrace(); } } } the plugins/ directory added expected (as 1 can check calling sysloader.geturls()), iterator given serviceloader object empty.
2. using urlclassloader
this uses definition of serviceloader.load second argument of class classloader.
package plugintest.system; import plugintest.simpleplugin; import java.io.file; import java.io.filefilter; import java.io.ioexception; import java.net.url; import java.net.urlclassloader; import java.util.iterator; import java.util.serviceloader; public class manageplugins { public static void main(string[] args) throws ioexception { file loc = new file("plugins"); file[] flist = loc.listfiles(new filefilter() { public boolean accept(file file) {return file.getpath().tolowercase().endswith(".jar");} }); url[] urls = new url[flist.length]; (int = 0; < flist.length; i++) urls[i] = flist[i].touri().tourl(); urlclassloader ucl = new urlclassloader(urls); serviceloader<simpleplugin> sl = serviceloader.load(simpleplugin.class, ucl); iterator<simpleplugin> apit = sl.iterator(); while (apit.hasnext()) system.out.println(apit.next().getname()); } } once again, iterator has never "next" element.
there's surely i'm missing since it's first time i'm "playing" class paths , loading.
the problem simple. , stupid. in plugin .jar files /services/plugintest.simpleplugin file missing inside meta-inf directory, serviceloader couldn't identify jars services , load class.
that's pretty all, second (and cleaner) way works charm.
i thought had cheer 200th answer... it's question of mine =__=
Comments
Post a Comment