David Gallardo on the Service Provider Interface


News: David Gallardo on the Service Provider Interface

  1. David Gallardo sent this in, about the Service Provider Interface: I was looking for a simple, low-overhead way to do plug-ins and the service provider mechanism in the jar file specification seems to fit the bill, is dead-simple (once you grok the doc), and requires almost no coding on my part: Java automatically finds the plug-ins at start-up and they can be retrieved by a call to the static ServiceRegistry.lookupProviders() method. I can't help but wonder if something's wrong with this since I haven't ever run across it before, (though, I must admit, it hadn't ever occurred to me to read the Jar file spec), and googling for service discovery strategies brought up little useful information about it. Here's an example: Assume you have this interface:package com.example.plugins; public interface Plugin { void start(); }and a couple of implementations;package com.example.plugins; public class BluePlugin implements Plugin { //.. } package com.example.plugins; public class RedPlugin implements Plugin { //.. }Now for the jar file magic: Create your META-INF directory at what will be the root of your jar file, and a subdirectory below that names "services'. In this directory, put a file named the fully qualified name of your interface--in this example, "com.example.plugins.Plugin". Inside that file, on separate lines, write the fully qualified names of the concrete implementations. (You can use hash marks for comments).# File: META-INF/services/com.example.plugins.Plugin com.example.plugins.BluePlugin com.example.plugins.RedPluginNow, all you need to do get an a iterator over instances of these classes is to call the static method ServiceRegistry.lookupProviders() with a class identifying the interface for which you want implementations:Iterator iter = ServiceRegistry.lookupProviders(Class.forName("com.example.plugins.Plugin")); while(iter.hasNext()) { Plugin service = (Plugin)iter.next(); service.start(); }This will instantiate and start a RedPlugin and a BluePlugin. BTW, I find that at least in Eclipse I don't need to actually create a jar file to get this to work, I can just create the META-INF/services directory at the root of my project.
  2. Yea, it's been internally adopted as an idiom and pattern within Sun since 1.3, and "formalized" in Java 6. But in truth, in seemingly typical Sun fashion, they could have gone a bit better. The ServiceRegistry isn't really that flexible, which is too bad. Most annoying, is that they already had a "better" solution. In Netbeans. http://openide.netbeans.org/lookup/ That's basically ServiceRegistry++. It's powerful, bone simple, and flexible. Anyone looking to adopt ServiceRegistry I think should instead adopt Netbeans lookup. Save for the class names, Netbeans Lookup is a drop in replacement for ServiceRegistry -- exact same mechanic. I, too, was recently looking some kind of module system, and end up with Netbeans Lookup. It's got the simplicity I was after, and also the flexibility. It's no OSGi, but that's pretty much overkill for what I wanted. Now, I can write stuff as jars, plop them in the web app WEB-INF/lib dir, or wherever, and They Just Work. Really simple. But if I ever need anything more sophisticated, I can always upgrade at my leisure. One of the crafty things that they did with Netbeans Lookup is that the Lookup mechanism is, itself, a service that's, well, looked. So dropping in a jar with a new Lookup implementation, and shazam, new Lookup system. No code change. Obviously, there could be issues of conflicting projects using conflicting Lookup systems. But, then, you could always write a proxy to manage them both and plug that in :-). Pretty clever and simple. You should check it out. (They say to download Netbeans, and grab the jar out of the installation. I suggest, rather, you get it out of their SVN (or are they CVS?, no matter) and build it. No dependencies, gives you a little more control.)
  3. That's pretty cool. Wrote something similar (currently residing in xbean-finder) which is one big class I've copied around a few projects I work on. It doesn't do the instantiations, but you could add that for yourself very easily. Using the same META-INF layout and files as you posted, you can do like: ResourceFinder finder = new ResourceFinder("META-INF/services/"); List plugins = finder.findAllImplementations(Plugin.class); A little neater if you adjusted your META-INF layout as follows META-INF/com.example.plugins.Plugins/red META-INF/com.example.plugins.Plugins/blue ...where the "red" file contained the text "com.example.plugins.RedPlugin" and the "blue" file contained the text "com.example.plugins.BluePlugin", you could then get them in a map like so: Map plugins = finder.mapAvailableImplementations(Plugin.class); Class red = plugins.get("red"); Class blue = plugins.get("blue"); Now say you want to do something similar, but the "red" and "blue" files are properties files which contain the name of the implementation class and other configurable properties for your red and blue plugins. ResourceFinder finder = new ResourceFinder("META-INF/services/"); Map pluginConfigs = finder.mapAllProperties(Plugin.class.getName()); Properties redConfig = pluginConfigs.get("red"); Properties blueConfig = pluginConfigs.get("blue"); Source is available here. Feel free to grab a copy for yourself, or grab the jar. There's a neat annotation scanner I wrote in there which you may actually like better. It uses ASM so that's one more dep for you, but it's pretty simple to use. Say you had an @Plugin annotation you used, you could do as follows and skip the whole META-INF business: ClassFinder finder = new ClassFinder(myClassLoader); List plugins = finder.findAnnotatedClasses(Plugin.class); I never wrote object instantiation into any of those libraries because I'm a big fan of xbean-reflect package, which is a real "don't tell me what to do" library for when you just want to create a simple object and would like to get real basic field/setter/constructor injection without choking down a whole "i control everything" framework. You just: ObjectRecipe recpie = new ObjectRecipe("com.example.plugins.RedPlugin"); recpie.setProperty("myDateField","2008-04-17"); recpie.setProperty("myIntField","100"); recpie.setProperty("myBooleanField","true"); recpie.setProperty("myUrlField","https://www.theserverside.com"); Plugin red = (Plugin) recpie.create(); red.start(); Obviously, the above style to object creation couples really well to the ResourceFinder method that gives you Properties objects back. You put the class name and config for your plugin in the properties files and pass the properties right into the ObjectRecipe and more or less get a little do-it-yourself IoC plugin system.
  4. I've been using this API for a while. For example Apache WSIF used this a while ago, and Apache Synapse uses this for registering new mediators into the runtime. It makes it nice - just drop a JAR file into the classpath. However, the original Service Provider interface as described in the JAR file spec uses com.sun.misc code which turns out to be in the Sun JDK but not all JDKs. Thanks for the pointers to the other options. Paul
  5. While there are new things -- OSGi, and ust heard about the NetBeans lookup -- that may also fit the bill, I've been using the ServiceRegistry for some time in many projects. Its been extremely helpful.
  6. Just a quick link to an article talking about this: http://java.sun.com/developer/technicalArticles/javase/extensible/index.html I also think this chapter from the Netbean Rich Client Programming book is relevant: http://usersguide.netbeans.org/files/documents/40/1315/rcp-book-ch2.pdf
  7. This mechanism is being used all over the place, AFAIK. It's simply not wildly advertised. :-) I'm using a slightly enhanced version of this service provider in MessAdmin, my very own monitoring utility (shameless plug), and it's been serving us very well. The specification is indeed very easy to grok, even if a bit on the simplistic side. ________ MessAdmin, Java EE monitoring made easy!