Posted by enicholas
on April 7, 2006 at 9:04 AM PDT
A Service Provider Interface is like magic -- just drop a special JAR file onto the class path, and suddenly you have extra capabilities. But how do you actually make one?
While working on my XML user interface language, JAXX , I wanted to provide a mechanism for users to add support for additional tags and data types. As JAXX is a command-line compiler, a run-time API to add additional features would be very awkward to access, so a Service Provider Interface (SPI) seemed like a natural fit.
In case you aren't familiar with SPIs, they have a tantalizing proposition: just put a special JAR file somewhere on your class path, and the features in the JAR file will be found and made available. Many of Java's internal APIs, such as JNDI and javax.imageio, have SPIs, which makes it a snap to plug in new features.
So, great, I needed to write an SPI. I spent some time wrestling with search engines to find documentation on how to actually create an SPI, and found nothing. Tons of information about how to interact with the SPIs built into Java, but absolutely nothing about how to create one. There was nothing left to do but dig through Java's source code to figure out what Sun was doing, so I could shamelessly copy it.
The answer turned out to be embarrassingly simple. The method ClassLoader.getResources() returns an Enumeration of all resources with a given name, like myspi.properties. To implement an SPI, you have each SPI JAR contain (say) a properties file with the same name in the same location. When you use ClassLoader.getResources() to get a list of all such properties files, you will find one properties file for each JAR on the class path which implements your SPI.
The properties file should contain whatever information is necessary for you to make use of the features in the JAR file. This typically takes the form of properties set to the names of classes found in the JAR file, which can then be loaded and used.
In the case of JAXX, the properties file is named jaxx.properties and is located at the root of the JAR file. It contains a single property jaxx.initializer, which should be set to the name of a class in the JAR file. This class must implement the interface jaxx.spi.Initializer. JAXX can then, via the magic of ClassLoader.getResources(), easily find all such initializer classes and load the support they contain.
Presto! A fully-functioning SPI, just like the ones Sun uses.