简体   繁体   中英

How to load .class from working directory at runtime from jar?

Attempting to implement a plugin system for my application, the code of which extends my Plugin class to define custom functionality.

This code to load a class from within a jar, in the same directory as the .jar of the application, works fine:

if(pluginName.endsWith(".jar"))
{
    JarFile jarFile = new JarFile(pluginName);
    Enumeration jarComponents = jarFile.entries();

    while (jarComponents.hasMoreElements())
    {
        JarEntry entry = (JarEntry) jarComponents.nextElement();
        if(entry.getName().endsWith(".class"))
        {
            className = entry.getName();
        }
    }

    File file = new File(System.getProperty("user.dir")+"/"+pluginName);
    URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()});

    Class newClass = Class.forName(className.replace(".class", ""), true, loader);
    newPlugin = (Plugin) newClass.newInstance();
}

Which results in the correct response of:

benpalmer$ java -jar assignment.jar test_subproject.jar
- INFO: Add new plugin: source.com

The issue is I also want to provide the user the option to specify a .class file instead, as that is realistically all that is packed into a .jar for the purposes of my plugin system.

else if(pluginName.endsWith(".class"))
{
    File file = new File(System.getProperty("user.dir")+"/"+pluginName);
    URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()});

    Class newClass = Class.forName(className.replace(".class", ""), true, loader);
    newPlugin = (Plugin) newClass.newInstance();
}   
... exception handling

Which results in the following:

benpalmer$ java -jar assignment.jar TestPlugin.class
SEVERE: Error: Plugin class not found. Class name: 
java.lang.ClassNotFoundException: 
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at newsfeed.model.Plugin.loadClassFromJarFile(Plugin.java:144)
    at newsfeed.controller.NFWindowController.initPlugins(NFWindowController.java:81)
    at newsfeed.controller.NewsFeed$1.run(NewsFeed.java:20)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

I've tried the code above and many variations of path names/other options, but get ClassNotFoundException every time. Originally had trouble loading the jars but using the fully qualified path fixed that, now I can't load the Classes.

Any ideas? :)

This is a feature of URLClassLoader it works with directories of classes (you may assume .jar as packed directory).

The constructor you are using has the following javadoc

Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader. The URLs will be searched in the order specified for classes and resources after first searching in the parent class loader. Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be downloaded and opened as needed.

If you absolutely want to create class loaders for each separate class you should consider subclassing SecureClassLoader and defining new class manually with defineClass method.

The basics of Java class loaders article should be of help to you.

Saw my question again and realised I'd forgotten to post a solution I found to my problem. I hope this helps someone in the future, as it's the only working example I ever found for my situation - a university assignment problem. I can't believe it was this simple:

byte[] classData = Files.readAllBytes(Paths.get(fileName));
Class<?> cls = defineClass(null, classData, 0, classData.length); 
return (PluginClassName)cls.newInstance();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM