Tim Boudreau is a noted technology consultant, evangelist and author. While perhaps most broadly known for his leadership on Sun Microsystems’ NetBeans, those who’ve worked with Tim remark most on his technical chops, passion for a great challenge and rare gift of great communication. And, as a former troubadour, he’s pretty tough when it comes to bad 70s rock lyrics too. A real renaissance programmer. Tim has posted 25 posts at DZone. You can read more from them at their website. View Full User Profile

Simple Dependency Injection with ServiceLoader in JDK 6

08.22.2008
| 17017 views |
  • submit to reddit
There are a lot of dependency injection/IOC frameworks out there. What you may not know is that there is a very simple yet useful one built into the JDK. And it's type-safe.

JDK 6 introduces ServiceLoader. ServiceLoader loads things based on flat files in the directory META-INF/services from the Java classpath. The mechanism of using META-INF/services has been around since JDK 1.3, under the name the Java Extension Mechanism. ServiceLoader makes it actually easy to use.

For those familiar with NetBeans Lookup API, ServiceLoader is like a severely crippled version of that (specifically the default lookup). But what ServiceLoader does, it does adequately. See below for details of the differences.

Using ServiceLoader is simple - like the capability pattern, it is based around querying for a type with a Class object and getting back zero or more instances (which may be subclasses) of that type.

Here is a toy example of using ServiceLoader for injection:

package serviceloaderdemo;
import java.util.ServiceLoader;
public abstract class HelloProvider {

public static HelloProvider getDefault() {
ServiceLoader<HelloProvider> ldr = ServiceLoader.load(HelloProvider.class);
for (HelloProvider provider : ldr) {
//We are only expecting one
return provider;
}
throw new Error ("No HelloProvider registered");
}

public abstract String getMessage();

public static void main(String[] ignored) {
HelloProvider provider = HelloProvider.getDefault();
System.out.println(provider.getMessage());
}
}

package serviceloaderdemo;
public class HelloImpl extends HelloProvider {
@Override
public String getMessage() {
return "Hello World";
}
}
To register HelloImpl as the injected implementation of HelloProvider, we just create a folder called META-INF/services in our source directory, so it ends up in our JAR file. In that directory we create a text file named serviceloaderdemo.HelloProvider. That file contains a single line of text - the name of the actual implementation of HelloProvider:
serviceloaderdemo.HelloImpl
Now, this is a toy example. But imagine that HelloImpl were in a completely different JAR file. All you have to do to change the default implementation of HelloProvider is to put a different JAR file on the classpath.

Let's look at a more real example. A couple of blogs ago I showed a cleaned up design for something like SwingWorker. It has an interface called TaskStatus with a bunch of setters which a background task can use to update status information, presumably in a UI of some kind. What would be very nice is if someone could provide a UI for showing progress of background tasks just by putting a JAR on the classpath, and fetching a component and adding it to their window. So it's the perfect case for dependency injection — somebody should be able to write such a UI implementation, and then just put it on the classpath, and an application can pick it up and use it without changing a line of code, and it can be reused across many applications with almost no code added to those applications.

We are going to need a factory for TaskStatus objects, because there might be more than one background process runnning at a time, and the UI should show all running background tasks. So we'll create two classes, using the mirror-class pattern discussed in my previous blog - we will separate the API and SPI. The UI will get a ProgressMonitorFactory and get the UI component to add to its status bar by calling getCapability(Component.class). All ProgressMonitorFactory will do is sanity check arguments and thread state, and then delegate to the implementation which is looked up using ServiceLoader.

But there's an added wrinkle here: There are at least two flavors of status UI we will want - one which just displays in the statusbar, and one which blocks the UI with a modal dialog.

That's okay: ServiceLoader can provide us with more than one implementation - we just create multiple implementations, and add more lines to that file in META-INF/services.

So, to indicate different kinds of UI, we'll use annotations, and look up an instance using strings (we actually don't want type-safety here - someone might want to provide still another kind of progress monitor UI - but we will define two defaults):

@Retention (value=RUNTIME)
public @interface MonitorKind {
public static final String DEFAULT = "default";
public static final String BLOCKING = "blocking";
String kind();
}
Next we define the SPI class that creates instances of ProgressImpl that can move a progress bar in the UI:
public abstract class ProgressImplFactory {
public abstract ProgressImpl create (Task task);

public <T> T getCapability (Class<T> type) {
return null;
}
}
The last thing we need is the final API mirror-class for our SPI class. The important method for lookup via annotations is getInstance(String):
public final class ProgressMonitorFactory {
private static final Set<String> notFoundKinds = new HashSet<String>();
private final ProgressImplFactory delegate;

private ProgressMonitorFactory(ProgressImplFactory p) {
this.delegate = p;
}

public ProgressMonitor create (Task task) {
return new ProgressMonitor (delegate.create(task));
}

public static ProgressMonitorFactory getInstance(String kind) {
if (kind == null) {
throw new NullPointerException ("Kind null");
}
for (ProgressImplFactory p : ServiceLoader.load(ProgressImplFactory.class)) {
//Would be nicer if ServiceLoader let you get the type
//without creating an instance, ala NetBeans Lookup
Class type = p.getClass();
MonitorKind mk = (MonitorKind) type.getAnnotation(MonitorKind.class);
if (mk != null && kind.equals(mk.kind())) {
return new ProgressMonitorFactory(p);
}
}
if (!notFoundKinds.contains(kind)) {
notFoundKinds.add (kind);
Logger.getLogger(ProgressMonitor.class.getName()).log (Level.FINE,
"Requested ProgressMonitor kind '" + kind + "', but it is" +
" not registered on the classpath.",
new IllegalArgumentException());
}
return null;
}

//implementation omitted...
}

Now we just need a JAR that registers an actual implementation of ProgressImplFactory on the classpath, which provides a UI component. All an application needs to do to use it and have a nice UI for background tasks is

ProgressMonitorFactory factory = ProgressMonitorFactory.getDefault();
Component statusComp = factory.getCapability (Component.class);
if (statusComp != null) {
statusBar.add (statusComp);
}
Then all the application author has to do is implement Task and send it off to be run in the background. The UI will work correctly and transparently.

The main point here is that the JDK has a useful built-in dependency injection mechanism, and that you can create APIs and implementations of them that are a joy to use and easy to update by using it. While it's not as rich as some of the dependency injection frameworks out there, it has the benefit of being type-safe, and is more than enough for many purposes.

Why You Might Want To Use NetBeans Lookup Instead of ServiceLoader

The differences between NetBeans Lookup and the JDK's ServiceLoader are as follows:

  • You can't instantiate a ServiceLoader yourself; you can use Lookup for inter-object communication, not just loading services from the classpath, and it is very useful for that
  • You can listen for changes in a Lookup - really, subscribe to changes the presence/absence/identity of a particular type in that Lookup. That means, if the classpath changes dynamically, you will actually be able to unload and reload objects. This makes it possible to hot-reload JARs without restarting the application.
  • You can find out if a Lookup contains an instance of a type without actually instantiating an object of that type. With ServiceLoader you can only query by actually instantiating objects.
  • You can use Lookups.forPath(String) to register instances in other subdirectories of META-INF. This makes some interesting things possible, such as using path information as metadata. For example, if you wanted to register different implementations of the same type for different MIME types, you could simply call
                Lookup lkp = Lookups.forPath ("text/plain");
    SomeObject thingy = lkp.lookup (SomeObject.class);
  • You can compose multiple lookups together with ProxyLookup, and even change the set of lookups being proxied on the fly and get appropriate events.


If you want to use Lookup and you have a copy of NetBeans, you already have a copy of Lookup — look in $NB_HOME/platform9/modules/org-openide-util.jar. The Javadoc can be found here. Lookup is not tied to any other NetBeans APIs, nor specifically to GUI applications. You can use it anywhere you want this sort of dependency injection.

From http://weblogs.java.net/blog/timboudreau/

Published at DZone with permission of its author, Tim Boudreau.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)