Skip to main content

Simple Dependency Injection with ServiceLoader in JDK 6

Posted by timboudreau on August 12, 2008 at 1:57 PM PDT

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
     

carboat.jpg
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 Lookup and
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.

Comments

Huh. Some update to java.net turned the first half of this ...

Huh. Some update to java.net turned the first half of this blog into a sort of free-verse. Kind of cool.

> For completeness: In a true DI setup, you would never have to issue a > getDefault() call anywhere. The container that you're running in would > call your setHelloProvider() method with "the right object" > automatically. As the art is today, this probably is considered "true DI". The problem I have with that situation is the need to have that setter. If it's there, someone can call it - someone you don't expect, when you don't expect it, from a thread you don't expect it on. As soon as you have setters, you have threading issues. This is the thing that still creeps me out about setter-based objection. APIs are statements about the intended use of code; having a setter that nobody is supposed to call except a DI framework means the injection is not transparent, and the API gets warped to accomodate it. Guice seems to do a pretty good job of making this sort of thing less awful; but then, you can't just pick up the code and use another framework... At least it doesn't involve XML :-)

@kblisted: As I said, this isn't full-blown dependency injection (I'm still making up my mind as to whether I think field injection is a good thing or not). I agree, there's not much control over the lifecycle (singleton, non-singleton, etc.) - another reason NetBeans Lookup library might be a better choice. Re its value, it is extremely valuable for decoupling implementation and API - search NetBeans sources for Lookup.getDefault().lookup(SomeClass.class) to get a sense of that. I did say *simple* dependency injection in the title here. @swv: I won't answer for mrmorris, but I'll give you my own thoughts on that: JNDI has its origins in telecom - a situation where, as you say, you might be able to do something (or at least notify someone who can with detailed info) when there's an exception. It's the perversion of JNDI into a lookup mechanism for enterprise applications that's the bug - most of these error conditions are going to be either meaningless or unrecoverable, so to someone using JNDI outside of the kind of environment it was intended for, it's pure noise in the API.

@mrmorris: what's wrong with checked exceptions? Your post seems to take the fact that there are 41 of them or so in the JNDI libs as axiomatically bad. With all respect, I am not on board. If something goes wrong, the more specific knowledge I can get about what went wrong, the more intelligently I can handle the situation, the happier my customers are. I have no doubt that many more than 41 different and meaningfully distinguishable things can go wrong when I reach out into the ether for *something*. Way back in time, I once had to write exception processing of resource location attempts gone wrong and having a *reasonable* number of distinguishable states made my life unreasonably hard. 41 sounds like a good number to me...

Have I missed something or what? This isn't dependency injection/IOC as stated in the beginning. This is merely a simple service locator pattern. Where is the dependency resolution for example? How do you control instantiation to be singleton, non-singleton, pooled instantiation etc?? I have a hard time seeing anything novel here. It's dynamic class loading in a simple wrapper. Dynamic class loading has been around since java 1.0. Fine it's a toy example, but is this mechanism really worth anything outside the playground?? Maybe it's just me understanding the whole purpose of this article.. http://firstclassthoughts.co.uk

@timboudreau: I think mrmorris is contrasting dependency injection (where the "right" objects show up magically throughout your code) with the service locator pattern, of which JNDI, NetBeans' Lookup and the ServiceLoader are examples. For completeness: In a true DI setup, you would never have to issue a getDefault() call anywhere. The container that you're running in would call your setHelloProvider() method with "the right object" automatically. Best, Laird

@mrmorris: I think checked exceptions make sense in environments where there is actually something that can be done to handle them; or representing hardware or network failures or I/O failures. A null check is sufficient in a lookup mechanism where the looking-up process does not go outside the Java VM (i.e. not looking things up over the network or something - though any such system should log what went wrong if something failed that shouldn't have). If you're trying to make some kind of object registry for things that might be remote, you probably do need to handle various failure conditions explicitly. Pressing a registry into serving as a local lookup mechanism when it was designed to solve a much larger and more complicated problem seems like not using the right tool for the job.

> Why do you call it "dependency injection" Well, something is being injected. If you want to change what is being injected, you simply change the classpath - put a different JAR on the classpath that injects a different implementation. For example, in unit tests I will frequently create $TEST_SRC_ROOT/META-INF/services/org.foo.Interface with a mock object for testing. It's definitely not a full-blown injection system like Giuce, but for global singletons or services it is quite useful (field injection still makes my skin crawl a little).

> I'm not saying that's an ideal situation, but it's an imperfect world. Got ya. Thanks for the detailed answer, clears things up. > Checked exceptions for simple lookup activities Aha, I was wondering about you guys position on this. Particularly since your friend Jaroslav Tulach's book on the subject of API design steers remarkable clear of this topic. Whether it scales or not to a large application like NetBeans would certainly have been experiences I would've liked to have read. Perhaps the subject of another post? ;)

Why do you call it "dependency injection"? Perhaps it has to do with the "Service Locator", as one has noted. The fact of using a Service Locator does not guaranty that the project follows the DI pattern. Developers might embrace the ServiceLocator and start calling it from all over the code: from constructors, from business logic... and that won't be any closer to the DI... And yes, the ServiceLocator might help putting together (orchestrating?) classes which are designed with the DI in mind. Following your logic one can state the ClassLoader as the simplest form of DI in JDK.

@ ljnelson > Tim, what's your opinion on JNDI, which was supposed to be the service locator > above all other service locators? Flippant answer: Great to do LDAP with, probably shouldn't ever have been used for anything else. More thoughtful answer: A number of people have mentioned JNDI. The big problems I have with it are: 1. It's not type-safe, which at least to me is a showstopper 2. Checked exceptions for simple lookup activities 3. The fact that the API contains 41 Exception classes (if I counted correctly - there are almost twice as many exception types as there are types in the rest of the API). That's just insane. If there are that many things that can go wrong, you find another way to communicate what went wrong. This is just abusing the type system due to the fact that a type communicates something, at the expense of massively bloating the API. It's there, probably some of it made some sense at the time, and it does the job, but I couldn't see writing something new that uses it unless it's going to be used in conjunction with something that requires it anyway. Generally, libraries that set out to be the One Ring To Rule Them All end up turning into things nobody would choose to use if they could avoid it. There, I didn't use the words "steaming" or "pile" once. Oops!

> Let me rephrase, I am trying to understand the observed duality in the NetBeans API's History pretty much sums it up. Today you could do most of what is done with layer files with Lookup - since Lookups.forPath() was introduced in NetBeans 6, so you can now register things in META-INF/what/ever. A layer file lets you associate more metadata with an object than just the path - for example, localized display names and icons. And a file defined in a layer has attributes (ad/hoc key-value pairs). Some pieces of UI in NetBeans are simply views over a folder in the system filesystem, (ideally) with icons and display names done declaratively so that no classes are actually loaded until an object is actually needed (we drank the POJO kool-aid a bit earlier than the rest of the world and got bitten by issues such as showing a dialog triggering hundreds of classes being loaded and initialized). At this point, there are a lot of things that are currently done with layer files which can be done with Lookup.getDefault() (bear in mind, when layer files were created, Lookup didn't exist yet). I don't know that anybody's using alternative META-INF/* paths and Lookups.forPath() yet, but they could. So, the factors affecting what kind of registration mechanism will be used are: 1. The age of the API in question; 2. Whether additional declarative metadata which you can't do in a META-INF/* file is desired; and 3. What the developer of that API thought was the right way to do it (Lookups.forPath() hasn't really filtered into the collective NetBeans-developer-unconscious yet). It would be nice to unify some of this stuff at some point, but since the layer-based registration mechanisms work and compatibility would have to be kept anyway, there isn't a huge incentive to unify everything around, say, Lookups.forPath() or get rid of layer files. I'm not saying that's an ideal situation, but it's an imperfect world.

@Tim Let me rephrase, I am trying to understand the observed duality in the NetBeans API's. Why would you register a org.netbeans.api.java.source.JavaSourceTaskFactory in /META-INF/services as dictated by the Service Locator, while with org.netbeans.spi.editor.completion.CompletionProvider you'd register it in the module layer file. Is there a good reason for not consistently use layer files when they are, as your post concludes, superior to the Service Locator?

@whartung > I'm not asking for Lookup to be part of the JDK, but I don't think there's a compelling reason > to use the JDK's versions and not just use Lookup I to agree. On the other hand, if I'd said "use lookup" there would be howls of "You're trying to force NetBeans on the world!" - on the other hand, it's worth making people aware that there's something that can do the basics right in the jdk. @mrmorris: NB doesn't rely on serviceloader. Lookup.getDefault() does the same thing and more. I'm just using it here so people are aware that they've got something like that right in the JDK and don't think I'm talking about some esoteric library. @kawazu Should work anywhere Java does if you're running on JDK 6. Not sure exactly what ServiceLoader does with classloaders and so forth, but should work.

Will this also work, say, inside an EJB3 module in order to dynamically load "helper class" dependencies and such stuff? This would be a helpful mechanism there indeed...

Hi Tim, Although it may already be a bit outdated, I wrote an article with examples and source code that uses the ServiceLoader API. Then I show how to improve that with the NetBeans Lookup API. It may be helpful to others reading your blog. The link is here: http://java.sun.com/developer/technicalArticles/javase/extensible/index....

I don't know the history of why it was adopted for use as the Service Locator provider for EJB, other than it was a handy hammer to have. I think also it was created back in the time when folks were making these enormous abstractions. JNDI maps to "everything": LDAP servers, internal registries, file systems, whatever. Now, when comparing it to something like Lookup, JNDI is the Goliath to the little David that is Lookup and ServiceLoader. Notably, JNDI is a generic registry interface, whereas ServiceLoader and Lookup are not. Obviously there must be some registration mechanism, but those mechanisms are not exposed. This, of course, helps simplify the interface. One could readily create a JNDI context that leverages the ServiceLoader pattern, just like you could create an implementation of Lookup that relies on JNDI. In truth, particularly for the basic functionality of Lookup, the two are interchangable. You could write your code using either system, and your code would age gracefully, and be reusable, and nimble -- all of the values a Locator pattern offer you. Both are extensible. If you had code that used both, you could write an appropriate adapter class to make one work with the other. The underlying feature sets differ (like listeners on state change with Lookup), but if you're trying to trying to just find something, either will suit you. In JEE, JNDI can win because the container populates it for you. In JRE, Lookup/ServiceLocator can win because they come "for free" in the basic service use case, and are populated "automatically".

Interesting side note: The capability pattern, as you call it, is actually built into the JDK, but it is broken and has been broken since JDK 1.1. Have a look in particular at the Beans.getInstanceOf(Object, Class) method. A pity that it always returns the first argument and has since JDK 1.1. Tim, what's your opinion on JNDI, which was supposed to be the service locator above all other service locators?

It puzzles me that so many scream DI without actually knowing this pattern. The fact that it's supported by the JRE makes it simpler and de-facto. Even before ServiceLoader, we had various incarnations of it, i.e. javax.imagein.spi.ServiceRegistry. The service provider and the capability pattern is tremendously powerful when connecting sub-systems. For instance, we use it to provide a hook in our SSO system, where in effect the session object can have context associated with it according to whichever sub-systems are in the class path. Beautiful, because that can really keep the dependencies of EJB3 entities down when dealing with large databases. One thing I can't understand though, is why does NetBeans rely both on Lookup as well as ServiceLoader (i.e. registering org.netbeans.api.java.source.JavaSourceTaskFactory). Isn't Lookup orthogonal to (if not a superset) of ServiceLoader? /Casper

Isn't this pattern named 'Service Locator' instead of 'Dependency Injection'?

I'm not asking for Lookup to be part of the JDK, but I don't think there's a compelling reason to use the JDK's versions and not just use Lookup (unless you're, you know, coding the JDK). It's a teeny dependency, it's standalone, flexible, powerful and conceptually simple. The other nifty part of Lookup is that you can change it's behavior at runtime by replacing it's lookup mechanism. Out of the box is performs like ServiceLoader. But even that is pluggable from the java command line, so you can use the system without being committed to much of anything. It also works peachy with Java 5. I DO think, tho, that y'all should make it available beyond having to yank it out of Netbeans. Just kind of harsh to have to download 60+MB of stuff to get a 500K jar.