Using the ServiceLoader API to build a modular applications
For a while I've been wanting to move some of the code in my ${work} applications to be more modular and making use of some form of plugin architecture to allow certain parts of the system to have a life cycle of their own.
I was reminded of this desire when I was adding a small RESTful/Atom feed based API to one of our services recently (against the trunk branch) when shortly after the first set of commits we needed to make a small bug fix release of the system...
Since work on this API had already been committed to trunk we had to branch off the release tag and head into the realm of multiple-branch builds (not really a big problem - its why we tag releases and use version control right?).
As I was saying; I was reminded about plugins and how it would have been preferable to develop the API as a pluggable module which lived on its own and didn't effect the main application (thus negating the need to branch the code).
Without going down the OSGi rabbit hole I started looking for a simple solution and Java's built in ServiceLoader API seems to provide 90% of what I'd currently want. The API has been around since JDK 1.3 but mostly by internal components, JDK6 promotes the API and makes it extremely easy to use with a minimal set of requirements:
- An interface or abstract class (which defines your service)
- Implementations of said interface/abstract class
- Configuration file in META-INF/services/<interface name>
- Something to use them all
The interface simply defines our service contract:
public interface RequestProcessor {
String getName();
}
and then an implementation:
public class TestServiceNumberOne implements RequestProcessor {
public String getName() {
return TestServiceNumberOne.class.getName();
}
public Object processRequest() {
return null;
}
}
Every service needs to be declared in a text file under META-INF/services/{interfacename} (in this case META-INF/services/RequestProcessor):
TestServiceNumberOne # Test Service
Then finally - some code to load and use the defined services:
public class ServiceProcessor {
public void loadProcessors() {
ServiceLoader<RequestProcessor> requestProcessors = ServiceLoader.load(RequestProcessor.class);
int count = 0;
System.out.println("Found processor:");
for (RequestProcessor requestProcessor : requestProcessors) {
System.out.println(" * " + requestProcessor.getName());
count++;
}
System.out.println(count + " processors found.");
}
}
The call to ServiceLoader.load() finds all of the text files in META-INF/services that match the requested interface name, and returns a ServiceLoader instance containing instances of each service - from here it's a simple matter of using the services in what ever manner your application requires.
Unfortunately the project I was wanting to use something like this on is still stuck on JDK1.4 so I'm basically back to square one and can't actually make use of the ServiceLoader API :( If OSGi/Felix isn't too heavy and supports the older JVMs maybe it's time I took the plunge and had a look.
Comments (9)
Take a look at the Lookup API in Netbeans at http://openide.netbeans.org/lookup. It's part of Netbeans RCP but you can use it in regular apps too.
Have you taken a look at commons.Discovery http://jakarta.apache.org/commons/discovery/
Guice won't work here, because it requires Java 5 (annotations).
You can use ServiceLoader with Java 1.3 and more. Only it's called sun.misc.Service! See http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider for details.
This is the very mechanism I used in my open-source monitoring project: MessAdmin (http://messadmin.sourceforge.net/). Feel free to download the source and have a look at it!You can use ServiceLoader with Java 1.3 and more. Only it's called sun.misc.Service! See http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider for details.
This is the very mechanism I used in my open-source monitoring project: MessAdmin (http://messadmin.sourceforge.net/). Feel free to download the source and have a look at it!OSGI is a dog. Who wants to be wiring your application by hand when an IDE can code complete? Why not have your configuration all loaded in one class per application (perhaps that needs to be hand-coded) instead?
Eg: class MyAppConfiguration extends AbstractConfigurable {public void configure(){
register(AppKeys.FRONT_PAGE, CustomFrontPage.class);
register(AppKeys.REPORTS_PAGE, StandardReportPage.class);
}
This looks like a solution that's solved very elegantly by Guice, http://code.google.com/p/google-guice/
You should definitely take a look at OSGi for this sort of requirement. It's not heavyweight at all, and it works with Java version 1.2 and upwards.
I have some basic tutorial articles that will get you started: http://neilbartlett.name/blog/osgi-articles/ - Neil
"OSGI is a dog. Who wants to be wiring your application by hand when an IDE can code complete? Why not have your configuration all loaded in one class per application (perhaps that needs to be hand-coded) instead?"
That's a pretty lame assumption, I hope you are a troll. Eclipse has plugin tools for both equinox and knopflerfish. Code completion and bundle class boundary checking are both available. There are also GUI aides for creating manifests. It makes writing OSGi apps a breeze, if you like Eclipse ;) The way I look at it the ServiceLoader is a trimmed down version of the OSGi framework. If you write your code correctly, you should be able to change between the two fairly easily. Deciding which one to start with should be based on your requirements not that 'OSGi is too hard and complex so ServiceLoader is better'. Thats crazy, use what works for you. Anyways, nice concise overview of ServiceLoader =)