3

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?

m_pGladiator
  • 8,462
  • 7
  • 43
  • 61
  • Is the 'spi' in 'spi/META-INF/services/' a typo? The META-INF folder should be in the root directory of the classpath. – Jörn Horstmann Jul 02 '10 at 15:01
  • Yes, sorry, I pasted the "source" path, from where the file is taken from the build script. It goes in the root of the jar, I checked. If it wasn't there, it shouldn't have worked in Eclipse either :) – m_pGladiator Jul 05 '10 at 14:59

3 Answers3

2

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.

FabienB
  • 1,104
  • 2
  • 11
  • 22
0

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.
naikus
  • 24,302
  • 4
  • 42
  • 43
  • Hi, thanks for the answer! I'm going to try the System class loader first. The jars should be in the netbeans module classpath, since I'm adding them as libraries to the module which will use them and not to the whole project. I think it is by default that your jars then go in the ext folder and I don't think this matters so much, since the netbeans generates this Class-Path manifest attribute automatcally, which you described. I checked this attribute and it correctly points to all jars in the ext folder, and the jars are there. – m_pGladiator Jul 05 '10 at 07:38
  • system class loader does not see them either :( – m_pGladiator Jul 05 '10 at 14:06
  • May be @Jörn Horstmann's comment may have some clue? about the spi directory? Is the META-INF directory at the root of the jar? The last resort is trying the URLClassLoader approach. – naikus Jul 05 '10 at 14:44
  • yes, it is in the root. The URLClasLoader approach is really the last resort maybe... I'll try it tommorow – m_pGladiator Jul 05 '10 at 14:59
  • I found out that this module uses some custom class loaders in the code, with magical methods inside like "resolveHiddenClasspath"... I guess I should look deeply in the source of that moudle to have a clue what is going on – m_pGladiator Jul 05 '10 at 15:02
  • @m_pGladiator: Did you ever solve this problem? I have the same issue. My question is posted here: http://stackoverflow.com/q/34619176/1735836 – Patricia Jan 05 '16 at 20:34
0

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.

m_pGladiator
  • 8,462
  • 7
  • 43
  • 61