10

I am attempting to load classes dynamically into a component. I am using a file chooser to select the .JAR file that will be loaded and then a option pane to get the name of the class.

I have trawled the internet looking for how to convert a java file to a URL in order to load it in URLClassLoader and I have come up with:

File myFile = filechooser.getSelectedFile();
String className = JOptionPane.showInputDialog(
    this, "Class Name:", "Class Name", JOptionPane.QUESTION_MESSAGE);

URL myUrl= null;
try {
    myUrl = myFile.toURL();
} catch (MalformedURLException e) {
}

URLClassLoader loader = new URLClassLoader(myUrl);
loader.loadClass(className);

I am now getting a 'cannot find symbol' error for loading the URL into the URLClassLoader

4

5 Answers 5

8

I like the ClassPathHacker class mentioned in the answer by Zellus, but it's full of deprecated calls and bad practices, so here's a rewritten version that also caches the Classloader and the addUrl method:

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.io.IOException;
import java.io.File;

public class ClassPathHacker{

    private static final Class<URLClassLoader> URLCLASSLOADER =
        URLClassLoader.class;
    private static final Class<?>[] PARAMS = new Class[] { URL.class };

    public static void addFile(final String s) throws IOException{
        addFile(new File(s));
    }

    public static void addFile(final File f) throws IOException{
        addURL(f.toURI().toURL());
    }

    public static void addURL(final URL u) throws IOException{

        final URLClassLoader urlClassLoader = getUrlClassLoader();

        try{
            final Method method = getAddUrlMethod();
            method.setAccessible(true);
            method.invoke(urlClassLoader, new Object[] { u });
        } catch(final Exception e){
            throw new IOException(
                "Error, could not add URL to system classloader");
        }

    }

    private static Method getAddUrlMethod()
        throws NoSuchMethodException{
        if(addUrlMethod == null){
            addUrlMethod =
                URLCLASSLOADER.getDeclaredMethod("addURL", PARAMS);
        }
        return addUrlMethod;
    }

    private static URLClassLoader urlClassLoader;
    private static Method addUrlMethod;

    private static URLClassLoader getUrlClassLoader(){
        if(urlClassLoader == null){
            final ClassLoader sysloader = 
                ClassLoader.getSystemClassLoader();
            if(sysloader instanceof URLClassLoader){
                urlClassLoader = (URLClassLoader) sysloader;
            } else{
                throw new IllegalStateException(
                    "Not an UrlClassLoader: "
                    + sysloader);
            }
        }
        return urlClassLoader;
    }

}
Sign up to request clarification or add additional context in comments.

Comments

5

ClassPathHacker.java found in this forum thread, is an option to load classes dynamically.

import java.lang.reflect.*;
import java.io.*;
import java.net.*;


public class ClassPathHacker {

private static final Class[] parameters = new Class[]{URL.class};

public static void addFile(String s) throws IOException {
    File f = new File(s);
    addFile(f);
}//end method

public static void addFile(File f) throws IOException {
    addURL(f.toURL());
}//end method


public static void addURL(URL u) throws IOException {

    URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
    Class sysclass = URLClassLoader.class;

    try {
        Method method = sysclass.getDeclaredMethod("addURL",parameters);
        method.setAccessible(true);
        method.invoke(sysloader,new Object[]{ u });
    } catch (Throwable t) {
        t.printStackTrace();
        throw new IOException("Error, could not add URL to system classloader");
    }//end try catch

}//end method

}//end class

Comments

2

Take a look at this related question: How should I load Jars dynamically at runtime?

Comments

1

The constructor of URLClassLoader takes an array of URLs, not a single URL.

Comments

0

I rewrote this in scala in case anyone needs as it isn't 100% trivial :)

/*
 * Class which allows URLS to be "dynamically" added to system class loader
 */
object class_path_updater {
  val URLCLASSLOADER = classOf[URLClassLoader]

  var urlClassLoader = getUrlClassLoader
  var addUrlMethod = getAddUrlMethod

  /*
   * addFile - have to use reflection to retrieve and call class loader addURL method as it is protected
  */
  def addFile(s: String) = {
    val urlClassLoader = getUrlClassLoader
    try {
      val method = getAddUrlMethod
      method.setAccessible(true)
      val v = (new File(s)).toURI.toURL
      invoke(urlClassLoader, method, Array[AnyRef](v))
      def invoke(proxy: AnyRef, m: Method, args: Array[AnyRef]) = m.invoke(proxy, args: _*)
    }

  }

  private def getAddUrlMethod: Method = {
    if (addUrlMethod == null) addUrlMethod = URLCLASSLOADER.getDeclaredMethod("addURL", classOf[URL])
    addUrlMethod
  }

  private def getUrlClassLoader: URLClassLoader = {
    if (urlClassLoader == null) {
      val sysLoader = ClassLoader.getSystemClassLoader
      sysLoader match {
        case x: URLClassLoader => urlClassLoader = sysLoader.asInstanceOf[URLClassLoader]
        case _ => throw new IllegalStateException("Not a UrlClassLoader: " + sysLoader)
      }
    }
    urlClassLoader
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.