com.netflix.fabricator.guice.ComponentModuleBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fabricator-guice Show documentation
Show all versions of fabricator-guice Show documentation
fabricator-guice developed by Netflix
package com.netflix.fabricator.guice;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.name.Names;
import com.google.inject.util.Types;
import com.netflix.fabricator.Builder;
import com.netflix.fabricator.ComponentType;
import com.netflix.fabricator.annotations.Default;
import com.netflix.fabricator.annotations.TypeImplementation;
import com.netflix.fabricator.annotations.Type;
import com.netflix.fabricator.component.ComponentFactory;
import com.netflix.fabricator.component.ComponentManager;
import com.netflix.governator.guice.lazy.LazySingletonScope;
/**
* Utility class for creating a binding between a type string name and an
* implementation using the builder pattern.
*
* @author elandau
*
* @param
*/
public class ComponentModuleBuilder {
private Map>> bindings = Maps.newHashMap();
private Set ids = Sets.newHashSet();
private Map instances = Maps.newHashMap();
private Class extends ComponentManager> managerClass;
private String typeName;
public Module build(final Class type) {
return new AbstractModule() {
@Override
protected void configure() {
TypeLiteral componentFactoryTypeLiteral = TypeLiteral.get(Types.newParameterizedType(ComponentFactory.class, type));
if (managerClass != null) {
TypeLiteral> componentType = (TypeLiteral>) TypeLiteral.get(Types.newParameterizedType(ComponentType.class, type));
if (typeName == null) {
Type typeAnnot = type.getAnnotation(Type.class);
Preconditions.checkNotNull(typeAnnot, "Missing @Type annotation for " + type.getCanonicalName());
bind(componentType)
.toInstance(new ComponentType(typeAnnot.value()));
}
else {
bind(componentType)
.toInstance(new ComponentType(typeName));
}
TypeLiteral> managerType = (TypeLiteral>) TypeLiteral.get(Types.newParameterizedType(ComponentManager.class, type));
TypeLiteral> managerTypeImpl = (TypeLiteral>) TypeLiteral.get(Types.newParameterizedType(managerClass, type));
bind(managerType)
.to(managerTypeImpl)
.in(LazySingletonScope.get());
if (!Modifier.isAbstract(type.getModifiers() )) {
bind(componentFactoryTypeLiteral)
.annotatedWith(Default.class)
.toProvider(new GuiceBindingComponentFactoryProvider((Class) type))
.in(LazySingletonScope.get());
}
}
// Create the multi binder for this type.
MapBinder> factories = (MapBinder>) MapBinder.newMapBinder(
binder(),
TypeLiteral.get(String.class),
componentFactoryTypeLiteral
);
// Add different sub types to the multi binder
for (Entry>> entry : bindings.entrySet()) {
factories.addBinding(entry.getKey()).toProvider(entry.getValue());
}
// Add specific named ids
for (String id : ids) {
bind(type)
.annotatedWith(Names.named(id))
.toProvider(new NamedInstanceProvider(id, TypeLiteral.get(Types.newParameterizedType(ComponentManager.class, type))));
}
// Add externally provided named instances
for (Entry entry : instances.entrySet()) {
bind(type)
.annotatedWith(Names.named(entry.getKey()))
.toInstance(entry.getValue());
}
}
};
}
/**
* Identifies a specific subclass of the component type. The mapper will create
* an instance of class 'type' whenever it sees the value 'id' for the type
* field in the configuration specification (i.e. .properties or .json data)
*
* @param subTypeName
* @param subType
*/
public ComponentModuleBuilder implementation(String subTypeName, Class extends T> subType) {
bindings.put(subTypeName, new GuiceBindingComponentFactoryProvider(subType));
return this;
}
public ComponentModuleBuilder implementation(Class extends T> type) {
TypeImplementation subType = type.getAnnotation(TypeImplementation.class);
Preconditions.checkNotNull(subType, "Missing @TypeImplementation for class " + type.getCanonicalName());
bindings.put(subType.value(), new GuiceBindingComponentFactoryProvider((Class) type));
return this;
}
public ComponentModuleBuilder factory(String subType, final Class extends ComponentFactory> factory) {
bindings.put(subType, new ComponentFactoryFactoryProvider(factory));
return this;
}
public ComponentModuleBuilder typename(String typeName) {
this.typeName = typeName;
return this;
}
public ComponentModuleBuilder manager(Class extends ComponentManager> clazz) {
managerClass = clazz;
return this;
}
/**
* Specify a builder (as a Factory) on which configuration will be mapped and the
* final object created when the builder's build() method is called. Use this
* when you don't have access to the implementing class.
*
* Example usage,
*
* install(new ComponentMouldeBuilder()
* .builder("type", MyCompomentBuilder.class)
* .build();
*
* public class MyComponentBuilder implements Builder {
* public MyComponentBuilder withSomeProperty(String value) {
* ...
* }
*
* ...
*
* public SomeComponent build() {
* return new SomeComponentImpl(...);
* }
* }
*
* @param type
* @param builder
* @return
*/
public ComponentModuleBuilder builder(String type, Class extends Builder> builder) {
bindings.put(type, new GuiceBindingComponentFactoryProvider(builder));
return this;
}
/**
* Indicate a specific instance for id. This makes it possible to inject an instance
* using @Named('id') instead of the ComponentManager
*
* @param id
* @return
*/
public ComponentModuleBuilder named(String id) {
ids.add(id);
return this;
}
/**
* A a named instance of an existing component. No configuration will be done here.
* @param id
* @param instance
* @return
*/
public ComponentModuleBuilder named(String id, T instance) {
instances.put(id, instance);
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy