java.util.ServiceLoader not loading my provider class
Asked Answered
H

1

9

I am trying to put together a basic SPI-based registry of Handlers, which I lookup from a HandlerRegistry. When I use the ServiceLoader.load(Handler.class) to initialize the providers, and then iterate the list to lazily load them, I am not seeing any instances of the class. Keeping this as simple as possible, my HandlerRegistry class is:

public class HandlerRegistry 
{
  private static HandlerRegistry registry;

  private ServiceLoader<Handler> handlerLoader;

  private HandlerRegistry()
  {
    handlerLoader = ServiceLoader.load(Handler.class);
  }

  public static synchronized HandlerRegistry getRegistry()
  {
    if (registry == null) {
      registry = new HandlerRegistry();
      registry.init();
    }
    return registry;
  }

  private void init()
  {
System.out.println("HandlerRegistry.init()");
  }

  public Handler lookup(String item)
  {
System.out.println("lookup("+item+")");
    try {
      Iterator<Handler> it = handlerLoader.iterator();
      while (it.hasNext()) {
        Handler handler = it.next();
System.out.println("found handler "+handler);
      }
    }
    catch (ServiceConfigurationError err) {
      err.printStackTrace();
    }
    return null;
  }
}

I have a com.example.handler.Handler interface (empty for now for simplicity), and a com.example.handler.handlers.DummyHandler class which implements that interface. I have created a file in my jar called META-INF/services/com.example.handler.Handler, which contains the single line

com.example.handler.handlers.DummyHandler

according to the javadoc. My unit test simply calls the lookup() method to verify looking up the handler for an item. Of course there will eventulaly need to be a check of some kind to see if this is the right handler for this item, but at this point I am not even seeing my DummyHandler class get loaded by the registry. Am I doing something wrong here?

Thanks!

Hooker answered 7/8, 2013 at 13:46 Comment(0)
H
4

The answer appears to be in the sensitivity to exactly how this is configured. I had been placing my provider name resource file (the one named com.example.handler.Handler) directly in the top level project directory, i.e., /resources/META-INF/services/com.example.handler.Handler. I had configured my build.gradle to pull the file out and put it into the jar:

jar { from('resources') { include 'META-INF/services/*.*' } }

When I inspected the jar file, the file was there, right where I expected it to be, so I thought all was well. On a kick, I happened to move the resources folder from down under src/main, and presto! it works. I inspected the jar file and it appears identical to one built the previous way, but for some reason this one works. I will update further if I can determine a difference, but at least my test case works now.

Hooker answered 8/8, 2013 at 18:1 Comment(3)
Another key was making sure to use UTF-8 encoding without the byte-order mark for the resource file. When the BOM was included, it got interpreted as part of the class name and caused build problems.Hooker
In case anyone else revisits this, I have it working in one Java project, but if I bundle it up in a utility jar and try to use it in another project, java.nio.Files#probeContentType() doesn't find my implementation of java.nio.file.spi.FileTypeDetector. The service provider goes off looking for my META-INF/java.nio.file.spi.FileTypeDetector service implementation list, but doesn't find it.Bateau
Hi @bachman, I'm having the same problem. I have a Provider (super class), and there are three child classes. One from axis2-jaxws jar, second from jdk's rt.jar and the third one is from jaxws-rt.jar. I want the child from axis2-jaxws.jar but the ServideLoader is returning the one from jaxws-rt.jar. When I remove the jaxws-rt.jar, everything works fine. But I'm not sure if this is a good idea, as SOAP webservice require some jars during the runtime only. Do you or anyone remember a solution for this?Roundhead

© 2022 - 2024 — McMap. All rights reserved.