package com.netflix.fabricator.guice; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.ProviderInstanceBinding; import com.google.inject.spi.ProviderWithExtensionVisitor; import com.google.inject.spi.Toolable; import com.netflix.fabricator.BindingComponentFactory; import com.netflix.fabricator.ConfigurationNode; import com.netflix.fabricator.InjectionSpi; import com.netflix.fabricator.PropertyBinder; import com.netflix.fabricator.PropertyBinderResolver; import com.netflix.fabricator.component.ComponentFactory; import com.netflix.fabricator.component.bind.SimplePropertyBinderFactoryResolver; import com.netflix.fabricator.guice.mapping.EmbeddedComponentManagerBinding; import com.netflix.fabricator.guice.mapping.NamedComponentManagerBinding; import com.netflix.fabricator.guice.mapping.EmbeddedComponentFactoryBinding; import com.netflix.fabricator.guice.mapping.EmbeddedMapToComponentFactoryBinding; import com.netflix.fabricator.guice.mapping.NamedMapBinding; import com.netflix.fabricator.guice.mapping.NamedBinding; import com.netflix.fabricator.guice.mapping.PropertyInjection; /** * Utility class for creating a binding between a type string name and an * implementation using the builder pattern. * * TODO: PostConstruct and PreDestroy * * @author elandau * * @param <T> * */ public class GuiceBindingComponentFactoryProvider<T> implements ProviderWithExtensionVisitor<ComponentFactory<T>>, InjectionSpi { private static final Logger LOG = LoggerFactory.getLogger(GuiceBindingComponentFactoryProvider.class); private SettableInjector injector = new SettableInjector(); private BindingComponentFactory<T> factory; private PropertyBinderResolver binderResolver; private Class<?> clazz; public GuiceBindingComponentFactoryProvider(final Class<?> clazz) { this(clazz, new SettableInjector()); } public GuiceBindingComponentFactoryProvider(final Class<?> clazz, SettableInjector injector) { this.binderResolver = new SimplePropertyBinderFactoryResolver(null, this); this.clazz = clazz; if (injector != null) initialize(injector); } @Override public ComponentFactory<T> get() { return factory.get(); } /** * This is needed for 'initialize(injector)' below to be called so the provider * can get the injector after it is instantiated. */ @Override public <B, V> V acceptExtensionVisitor( BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) { return visitor.visit(binding); } @Inject @Toolable void initialize(Injector injector) { this.injector.set(injector); this.factory = new BindingComponentFactory<T>(clazz, binderResolver, this); } @Override public PropertyBinder createInjectableProperty(final String propertyName, Class<?> argType, Method method) { // Allowable bindings for named binding final PropertyInjection namedPropertyInjection = new PropertyInjection(argType, injector, method); namedPropertyInjection .addStrategy(new NamedBinding()) // T .addStrategy(new NamedMapBinding()) // Map<String, T> .addStrategy(new NamedComponentManagerBinding(propertyName) // ComponentManager<T> ); // Allowable bindings for embedded structures final PropertyInjection embeddedPropertyInjection = new PropertyInjection(argType, injector, method); embeddedPropertyInjection .addStrategy(new EmbeddedComponentManagerBinding(propertyName)) // ComponentManager<T> .addStrategy(new EmbeddedMapToComponentFactoryBinding()) // Map<String, ComponentFactory<T>> .addStrategy(new EmbeddedComponentFactoryBinding()) // ComponentFactory<T> .addStrategy(new NamedMapBinding() // Does this belong here ); //Build up a sequence of Binding resolving and value retrieving processes. //Any successful step will terminate the sequence return new PropertyBinder() { @Override public boolean bind(Object obj, ConfigurationNode node) throws Exception { // Property value is a simple 'string' // Look for 'named' binding or 'key' in a mapbinder if (node.isSingle()) { String value = node.getValue(String.class); if (value != null) { if (namedPropertyInjection.execute(value, obj, node)) { return true; } } else { // Hmmm... } return false; } // Property is a structure else { return embeddedPropertyInjection.execute(null, obj, node); } } }; } @Override public <S> S getInstance(Class<S> clazz) { return injector.getInstance(clazz); } @Override public void injectMembers(Object obj) { injector.injectMembers(obj); } }