All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.fabricator.guice.ComponentModuleBuilder Maven / Gradle / Ivy

There is a newer version: 0.28.0
Show newest version
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 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 subType) {
        bindings.put(subTypeName, new GuiceBindingComponentFactoryProvider(subType));
        return this;
    }

    public ComponentModuleBuilder implementation(Class 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> factory) {
        bindings.put(subType, new ComponentFactoryFactoryProvider(factory));
        return this;
    }

    public ComponentModuleBuilder typename(String typeName) {
        this.typeName = typeName;
        return this;
    }
    
    public ComponentModuleBuilder manager(Class 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> 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