views:

450

answers:

2

I refer to "service provider framework" as discussed in Chapter 2 of Effective Java, which seems like exactly the right way to handle a problem I am having, where I need to instantiate one of several classes at runtime, based on a String to select which service, and an Configuration object (essentially an XML snippet):

But how do I get the individual service providers (e.g. a bunch of default providers + some custom providers) to register themselves?

 interface FooAlgorithm
 {
     /* methods particular to this class of algorithms */
 }

 interface FooAlgorithmProvider
 {
     public FooAlgorithm getAlgorithm(Configuration c);
 }

 class FooAlgorithmRegistry
 {
     private FooAlgorithmRegistry() {}
     static private final Map<String, FooAlgorithmProvider> directory =
        new HashMap<String, FooAlgorithmProvider>();
     static public FooAlgorithmProvider getProvider(String name)
     {
         return directory.get(serviceName);
     }
     static public boolean registerProvider(String name, 
         FooAlgorithmProvider provider)
     {
         if (directory.containsKey(name))
            return false;
         directory.put(name, provider);
         return true;
     }
 }

e.g. if I write custom classes MyFooAlgorithm and MyFooAlgorithmProvider to implement FooAlgorithm, and I distribute them in a jar, is there any way to get registerProvider to be called automatically, or will my client programs that use the algorithm have to explicitly call FooAlgorithmRegistry.registerProvider() for each class they want to use?

+4  A: 

I think you need to create a META-INF/services/fully.qualified.ClassName and list things there, but I don't remember the spec (this or this).

The Practical API design confessions of a Java architect book chapter 8 is about SPI.

kd304
as footnote: this requires Java6
dfa
+1 for the book reference, thanks
Jason S
interesting: so the answer seems to be that there's a JAR file mechanism for including service provider configuration files, + various classes/methods for instantiating them (e.g. java.util.ServiceLoader)
Jason S
@Jason S: Yes, but I've never had to use it before.
kd304
A: 

You could have the client JAR register the providers in a static initializer block within some class that you know will be called before FooAlgorithmRegistry.getProvider(), something like:

static {
    FooAlgorithmRegistry.registerProvider("test", new MyFooAlgorithmProvider());
}

But, it might be pretty hard to find a way to guarantee that this will run (static initializers are guaranteed to be run once and only once, when the class is first loaded) before the accessor method of the factory.

matt b
But you need to explicitly reference your class somewhere to get it initialized. Alternatively, you could find your own jar file at runtime and traverse it to find classes with \*Provider in the name and Class.forName it. Ugly.
kd304