views:

493

answers:

2

I keep wanting to try this on a larger scale with our app + build system, but higher priorities keep pushing it to the back burner. It seems like a nice way to load Guice modules and avoids the common complaint about "hard coded configuration". Individual configuration properties rarely change on their own, but you will almost always have a set of profiles, usually for different environments (Debug, Production, etc).

ServiceLoader lets you pull a list of all implementations defined as a service for a given type. Putting this together with Guice, you end up with:

import java.util.ServiceLoader;

import com.google.inject.AbstractModule;
import com.google.inject.Module;

public class ModuleLoader<M extends Module> extends AbstractModule {

    private final Class<M> type;

    public ModuleLoader(Class<M> type) {
        this.type = type;
    }

    public static <M extends Module> ModuleLoader<M> of(Class<M> type) {
        return new ModuleLoader<M>(type);
    }

    @Override
    protected void configure() {
        ServiceLoader<M> modules = ServiceLoader.load(type);
        for (Module module : modules) {
            install(module);
        }
    }
}

Usage example (as a dynamic servlet loader in a guice-servlet project):

import com.google.inject.servlet.ServletModule;

public class ServletLoader extends GuiceServletContextListener {
    @Override
    protected final Injector getInjector() {
       return Guice.createInjector(ModuleLoader.of(ServletModule.class);
    }
}

The services (packaged as modules) would be packaged in seperate jar files. Within each one you'd define the class(es) in the meta-data:

Within servlets.jar: META-INF/services/com.google.inject.Module

com.example.webapps.MyServletModuleA
com.example.webapps.MyServletModuleB

Since we use Maven, we think this would be ideal as we could pull in different implementations at runtime via profile dependencies. Is anyone using Guice like this?

If not, feel free to use this example and see how it works for you. (ServiceLoader is only supported in JDK6+)

+1  A: 

We're doing almost exactly this at my work. We're currently stuck in java 5 due to some internal limitations, so we do it a bit differently using Service Provider (due to not having access to ServiceLocator until java 6 like you mentioned), but it essentially works the same.

I remember reading somewhere that this was one of the preferred ways the Guice developers recommended, though they want to leave this open for flexibility.

A: 

I considered this way already but i didnt use it because i was afraid, that i have to keep my modules extremely small, because its impossible to bind the same interface twice. My problem is, that if i want to use an interface/class/enum/whatever from another jar and that jar defined a services/* file i am screwed, because i cant make use of the content of the jar without loading it as a module.

I hope my concern is clear.

Willi