package com.netflix.governator; import java.util.ServiceLoader; import java.util.concurrent.Callable; import com.google.inject.AbstractModule; import com.google.inject.Module; import com.google.inject.ProvisionException; import com.google.inject.multibindings.Multibinder; /** * Builder for creating a Guice module that either installs modules loaded from the * service loader or creates multibindings for a service type. Method and field @Inject * methods of the services will also be invoked. * * @author elandau * * TODO: Lazy member injection */ public class ServiceLoaderModuleBuilder { private ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private Boolean installed = false; public ServiceLoaderModuleBuilder usingClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; return this; } public ServiceLoaderModuleBuilder forInstalledServices(Boolean installed) { this.installed = installed; return this; } private abstract class BaseModule<S> extends AbstractModule { private final Class<S> type; public BaseModule(Class<S> type) { this.type = type; } @Override protected final void configure() { Callable<ServiceLoader<S>> loader; if (installed) { if (classLoader != null) { throw new RuntimeException("Class loader may not be combined with loading installed services"); } loader = new Callable<ServiceLoader<S>>() { @Override public ServiceLoader<S> call() throws Exception { return ServiceLoader.loadInstalled(type); } }; } else if (classLoader != null) { loader = new Callable<ServiceLoader<S>>() { @Override public ServiceLoader<S> call() throws Exception { return ServiceLoader.load(type, classLoader); } }; } else { loader = new Callable<ServiceLoader<S>>() { @Override public ServiceLoader<S> call() throws Exception { return ServiceLoader.load(type); } }; } try { for (S service : loader.call()) { onService(service); } } catch (Exception e) { throw new ProvisionException("Failed to load services for '" + type + "'", e); } } protected abstract void onService(S service); } public <S extends Module> Module loadModules(final Class<S> type) { return new BaseModule<S>(type) { @Override protected void onService(S service) { install((Module)service); } }; } public <S> Module loadServices(final Class<S> type) { return new BaseModule<S>(type) { @Override protected void onService(S service) { requestInjection(service); Multibinder.newSetBinder(binder(), type).addBinding().toInstance(service); } }; } }