title: Dynamically loading plugin jars using ServiceLoader
body: <p>I'm trying to create a plugin system for my application, and I want to start with something simple. Every plugin should be packed in a .jar file and implement the <code>SimplePlugin</code> interface:</p>
  
  <pre><code>package plugintest;
  
  public interface SimplePlugin {
      public String getName();
  }
  </code></pre>
  
  <p>Now I've created an implementation of <code>SimplePlugin</code>, packed in a .jar and put it in the plugin/ subdirectory of the main application:</p>
  
  <pre><code>package plugintest;
  
  public class PluginTest implements SimplePlugin {
      public String getName() {
          return "I'm the plugin!";
      }
  }
  </code></pre>
  
  <p>In the main application, I want to get an instance of <code>PluginTest</code>. I've tried two alternatives, both using <code>java.util.ServiceLoader</code>.</p>
  
  <p><strong>1. Dynamically extending the classpath</strong></p>
  
  <p>This uses the known hack to use reflection on the system class loader to avoid encapsulation, in order to add <code>URL</code>s the the classpath.</p>
  
  <pre><code>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&lt;SimplePlugin&gt; sl = ServiceLoader.load(SimplePlugin.class);
          Iterator&lt;SimplePlugin&gt; 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();
          for (int i = 0; i &lt; urls.length; i++)
              if (urls[i].toString().equalsIgnoreCase(udirs)) return;
          Class&lt;URLClassLoader&gt; 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();
          }
      }
  }
  </code></pre>
  
  <p>The plugins/ directory is added as expected (as one can check calling <code>sysLoader.getURLs()</code>), but then the iterator given by the <code>ServiceLoader</code> object is empty.</p>
  
  <p><strong>2. Using URLClassLoader</strong></p>
  
  <p>This uses another definition of <code>ServiceLoader.load</code> with a second argument of the class <code>ClassLoader</code>.</p>
  
  <pre><code>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];
          for (int i = 0; i &lt; flist.length; i++)
              urls[i] = flist[i].toURI().toURL();
          URLClassLoader ucl = new URLClassLoader(urls);
  
          ServiceLoader&lt;SimplePlugin&gt; sl = ServiceLoader.load(SimplePlugin.class, ucl);
          Iterator&lt;SimplePlugin&gt; apit = sl.iterator();
          while (apit.hasNext())
              System.out.println(apit.next().getName());
      }
  }
  </code></pre>
  
  <p>Once again, the iterator has never a "next" element.</p>
  
  <p>There's surely something I'm missing since it's the first time I'm "playing" with class paths and loading.</p>