问题
I have troubles using the java ServiceLoader in a NetBeans module application. Here is what I'm trying to do (and it works in a normal java application in Eclipse):
I have an interface.jar, which declares the interface. And I have implementations.jar, which has several implementations of this interface, all specified in the spi/META-INF/services/my.package.name.MyInteface file (this file is in the implemenations.jar).
I also have a class ImplementationHandler (in yet another handler.jar), which has the following method to load all implementations:
private static List<MyInterface<?>> loadAllImplementations() {
List<MyInterface<?>> foundImplementations = Lists.newArrayList();
try {
for (MyInterface implementation : ServiceLoader.load(MyInterface.class)) {
foundImplementations.add(implementation);
}
} catch (ServiceConfigurationError error) {
system.out.println("Exception happened");
}
return foundImplementations;
}
This code returns all implementations in Eclipse normal application (the foundImplementations.size() > 0).
However under NetBeans, it can't find anything (foundImplementations.size() == 0).
More details:
I have the source of a NetBeans module application (open source, not written by me), which I need to extend by using some of MyInterface implementations. The interface.jar, implementations.jar and the handler.jar are created in Eclipse and they are part of another application.
In the NetBeans, I opened the module which needs to use the new impplementations and I added all my 3 jars as external libraries (NetBeans copied them into its ext folder, which I don't want but I can't do anything about - I want them in another myext folder, but that's another story). Then I rebuilt everything and tried to use one of my implementations, but it was not found... The code that gets an implementation is in the ImplementationHandler class and looks like:
public static final <T> MyInteface<T> getByName(String name) {
for (MyInteface implementation : loadAllImplementations()) {
if (implementation.getName().equals(name)) {
return implementation;
}
}
throw new IllegalArgumentException("Unable to find MyInterface class for: " + name);
}
I got my exception "Unable to find MyInteface class for: myImplementationName"...
My knowledge about NetBeans is very limited and I was wondering is there something more that I need to do in order to get this working?
回答1:
In order to work, you have to make Netbeans create this services sub folder inside META-INF. It's very easy to do, but the information is easily accessible.
To add something to META-INF, you need to create a folder of this name in your src/ (the source directory [spi?]) folder. In this case you also need the services folder and in it, create a text file with the same fully qualified name as your service interface. In the end you should have this structure: src/META-INF/services/my.package.MyInterface.
Finally, this [my.package.MyInterface] file's content should list all the implementation classes (one per line).
With this setup, Netbeans will create the appropriate jar when building your app.
Take a look at this ServiceLoader example. It's a complete example, although it does not explain the Netbeans integration I just described.
回答2:
The ServiceLoader.load(Class) uses the current thread's context class loader to load all the implementations. It may be that in your case your implementation classes in your jar file (implementation.jar) are not in that class loader's classpath.
You may have to try different approaches for this :
- You may either need to have all the jars in the netbeans module's classpath or,
- You may need to create a class loader (probably a URLClassLoader having those jars in its classpath) and use the
ServiceLoader.load(Class, ClassLoader)
and pass a that classloader. - There is another option you could try but I am not sure about this: The jar file spec allows you to specify Class-Path manifest attribute, to which you can add 'implementation.jar' entry. More details here
Most likely, the handler.jar and implementations.jar are not loaded by the same class loader. Also you may want to take a look as to why your files are getting to ext folder.
Hope this helps.
Edit:
- Also try calling the
ServiceLoader.load(Class, null)
which uses the System class loader (the one that started the application). Probably that classloader may be able to find classes in jars located in the ext directory.
回答3:
I gave up, and replaced ClassLoader with JSPF. This works out of the box and I don't need to know about the internals of a third party program, written as NetBeans module and how this affects the classpath given to the class loaders.
来源:https://stackoverflow.com/questions/3142515/how-to-make-the-java-serviceloader-work-in-a-netbeans-6-9-module-application