Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ServiceLoader.next causing a NoClassDefFoundError

I'm asking because I'm totally not sure I've done the right thing. I'm using Eclipse for a web project. Let's call it WebProject (duh) in the package com.web.project.

I want WebProject to load JAR plugins at runtime, so I thought I could take advantage of java.util.ServiceLoader. So I created an interface com.web.project.WebProjectPlugin in the WebProject project with all the methods the plugins must implement.

Then I created the project PluginProject, adding WebProbject/build/classes in its Build path as a class folder:

package com.web.project.plugin;

import com.web.project.WebProjectPlugin;

public class TestPlugin implements WebProjectPlugin {
    // Implementation of the interface methods...
}

Then I created a META-INF/services folder in the plugin project, put the text file com.web.project.WebProjectPlugin inside, containing the sole line "com.web.project.plugin.TestPlugin".

I exported the JAR file checking out the added build/classes folder and put it somewhere in the hard drive. When WebProject starts up, it does the following:

File[] jlist = pluginsDir.listFiles(new FileFilter() {
    public boolean accept(File file) {
        return file.getPath().toLowerCase().endsWith(".jar");
    }
});
URL[] urls = new URL[jlist.length];
for (int i = 0; i < jlist.length; i++)
    urls[i] = jlist[i].toURI().toURL();
URLClassLoader ucl = new URLClassLoader(urls);

ServiceLoader<WebProjectPlugin> srvl =
    ServiceLoader.load(WebProjectPlugin.class, ucl);
Iterator<WebProjectPlugin> iter = srvl.iterator();
while (iter.hasNext()) {
    WebProjectPlugin plugin = iter.next();
    plugins.add(plugin);
}

pluginsDir is a File object pointing to the directory the JAR file is in. At first it seems that srvl does its job, since iter isn't empty, but then it throws the dreaded NoClassDefFoundError when it reaches iter.next().

I've already managed to create a plugin manager project to test ServiceLoader, and it runs just fine, but it's a plain console Java application, not a web project. So, what am I doing wrong here?

I'm a little puzzled: how can it not find the class definition for com.web.project.WebProjectPlugin, since it's in the same project that is running? Has that something to do with the URLClassLoader object I'm using?

This is the stack trace.

like image 722
MaxArt Avatar asked Apr 30 '13 08:04

MaxArt


1 Answers

Try assigning the parent classloader to your URLClassLoader

URLClassLoader loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());

A WebProject expects a certain hierarchy of class loaders, so it might be that your classes are not visible to each other if the parent/child hieararchy is not set properly.

like image 179
NilsH Avatar answered Oct 24 '22 13:10

NilsH