Package com.netflix.governator.guice

Source Code of com.netflix.governator.guice.ModuleListBuilder$ModuleProvider

package com.netflix.governator.guice;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.ProvisionException;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import com.netflix.governator.annotations.Modules;

/**
* The {@link ModuleListBuilder} keeps track of modules and their transitive dependencies
* and provides a mechanism to replace or exclude modules.
*
* When {@link build()} is called a list of modules is created and modules will be ordered
* in the order in which they were added while allowing for dependent modules to be listed
* first.
*
* TODO: Provide exclude source
* TODO: Provide include source
* TODO: Force include
* TODO: Guard against circular dependencies
*
* @author elandau
*/
public class ModuleListBuilder {
    private static final Logger LOG = LoggerFactory.getLogger(ModuleListBuilder.class);
   
    /**
     * Internal class to track either a module class or instance
     * @author elandau
     */
    public class ModuleProvider {
        Class<? extends Module> type;
        Module instance;
        boolean isExcluded = false;
       
        public ModuleProvider(Class<? extends Module> type) {
            this.type = type;
            instance = null;
        }
       
        public ModuleProvider(Module instance) {
            this.instance = instance;
            if (instance instanceof AbstractModule) {
                type = null;
            }
        }
       
        public void setInstance(Module module) {
            Preconditions.checkState(instance == null, "Instance already exists");
            this.instance = module;
        }
       
        public Module getInstance(Injector injector) throws Exception {
            try {
                // Already created
                if (instance != null) {
                    return instance;
                }
               
                LOG.info("Getting instace of : " + type.getName());
                if (excludes.contains(type)) {
                    LOG.info("Module '" + type.getName() + "' is excluded");
                    return null;
                }
               
                // Create all of this modules dependencies.  This includes both @Modules and injected
                // dependencies
                for (Class<? extends Module> dep : getIncludeList()) {
                    ModuleProvider provider = includes.get(dep);
                    provider.getInstance(injector);
                }
               
                // If @Inject is present then instantiate using that constructor and manually inject
                // the dependencies.  Note that a null will be injected for excluded modules
                try {
                    InjectionPoint ip = InjectionPoint.forConstructorOf(type);
                    if (ip != null) {
                        Constructor<?> c = (Constructor<?>) ip.getMember();
                        c.setAccessible(true);
                        List<Dependency<?>> deps = ip.getDependencies();
                        if (!deps.isEmpty()) {
                            Object[] args = new Object[deps.size()];
                            for (Dependency<?> dep : deps) {
                                Class<?> type = dep.getKey().getTypeLiteral().getRawType();
                                if (Module.class.isAssignableFrom(type)) {
                                    args[dep.getParameterIndex()] = includes.get(dep.getKey().getTypeLiteral().getRawType()).getInstance(injector);
                                }
                                else {
                                    args[dep.getParameterIndex()] = injector.getInstance(dep.getKey());
                                }
                            }
                            c.setAccessible(true);
                           
                            instance = (Module) c.newInstance(args);
                        }
                        else {
                          instance = (Module) c.newInstance();
                        }
                    }
                    else {
                      // Empty constructor
                      Constructor<?> c = type.getConstructor();
                      if (c != null) {
                        c.setAccessible(true);
                        instance = (Module) c.newInstance();
                      }
                      // Default constructor
                      else {
                        instance = type.newInstance();
                      }
                    }
                    injector.injectMembers(instance);
                    return instance;
                }
                catch (Exception e) {
                    throw new ProvisionException("Failed to create module '" + type.getName() + "'", e);
                }
            }
            finally {
                if (instance != null) {
                    registerModule(instance);
                }
            }
        }
       
        private List<Class<? extends Module>> getIncludeList() {
            // Look for @Modules(includes={..})
            Builder<Class<? extends Module>> builder = ImmutableList.<Class<? extends Module>>builder();
            if (type != null) {
                Modules annot = type.getAnnotation(Modules.class);
                if (annot != null && annot.include() != null) {
                    builder.add(annot.include());
                }
               
                // Look for injected modules
                for (Dependency<?> dep : InjectionPoint.forConstructorOf(type).getDependencies()) {
                    Class<?> depType = dep.getKey().getTypeLiteral().getRawType();
                    if (Module.class.isAssignableFrom(depType)) {
                        builder.add((Class<? extends Module>) depType);
                    }
                }
            }
           
            return builder.build();
        }
       
        private List<Class<? extends Module>> getExcludeList() {
            Builder<Class<? extends Module>> builder = ImmutableList.<Class<? extends Module>>builder();
            if (type != null) {
                Modules annot = type.getAnnotation(Modules.class);
                if (annot != null && annot.exclude() != null) {
                    builder.add(annot.exclude());
                }
            }
            return builder.build();
        }

    }
   
    // List of all identified Modules in the order in which they were added and identified
    private List<ModuleProvider> providers = Lists.newArrayList();
   
    // Map of seen class to the provider.  Note that this map will not include any module
    // that is a simple
    private Map<Class<? extends Module>, ModuleProvider> includes = Maps.newIdentityHashMap();
   
    // Set of module classes to exclude
    private Set<Class<? extends Module>> excludes = Sets.newIdentityHashSet();
   
    // Final list of modules to install
    private List<Module> resolvedModules = Lists.newArrayList();
   
    // Lookup of resolved modules for duplicate check
    private Set<Class<? extends Module>> resolvedModuleLookup = Sets.newIdentityHashSet();
   
    public ModuleListBuilder includeModules(Iterable<? extends Module> modules) {
        for (Module module : modules) {
            include (module);
        }
        return this;
    }

    public ModuleListBuilder include(Iterable<Class<? extends Module>> modules) {
        for (Class<? extends Module> module : modules) {
            include (module);
        }
        return this;
    }

    public ModuleListBuilder include(final Module m) {
        ModuleProvider provider = new ModuleProvider(m);
       
        if (!m.getClass().isAnonymousClass()) {
            // Do nothing if already exists
            if (includes.containsKey(m.getClass())) {
                return this;
            }
           
            includes.put(m.getClass(),  provider);
           
            // Get all @Modules and injected dependencies of this module
            for (Class<? extends Module> dep : provider.getIncludeList()) {
                // Circular dependencies will be caught by includes.containsKey() above
                include(dep, false);
            }
           
            for (Class<? extends Module> dep : provider.getExcludeList()) {
                exclude(dep);
            }
        }

        // Add to list of known modules.  We do this after all dependencies
        // have been added
        providers.add(provider);

        return this;
    }
   
    public ModuleListBuilder include(final Class<? extends Module> m) {
        include(m, true);
        return this;
    }
   
    private void include(final Class<? extends Module> m, boolean addToProviders) {
        ModuleProvider provider = new ModuleProvider(m);
       
        if (!m.getClass().isAnonymousClass()) {
            // Do nothing if already exists
            if (includes.containsKey(m.getClass())) {
                return;
            }
           
            includes.put(m,  provider);
           
            // Get all @Modules and injected dependencies of this module
            for (Class<? extends Module> dep : provider.getIncludeList()) {
                // Circular dependencies will be caught by includes.containsKey() above
                include(dep, false);
            }
           
            for (Class<? extends Module> dep : provider.getExcludeList()) {
                exclude(dep);
            }
        }

        // Add to list of known modules.  We do this after all dependencies
        // have been added
        if (addToProviders) {
            providers.add(provider);
        }
    }
   
    public ModuleListBuilder exclude(Class<? extends Module> m) {
        excludes.add(m);
        return this;
    }
   
    public ModuleListBuilder exclude(Iterable<Class<? extends Module>> modules) {
        for (Class<? extends Module> module : modules) {
            excludes.add(module);
        }
        return this;
    }

    private void registerModule(Module m) {
        if (m.getClass().isAnonymousClass()) {
            resolvedModules.add(m);
        }
        else {
            if (!resolvedModuleLookup.contains(m.getClass())) {
                LOG.info("Adding module '" + m.getClass().getName());
                resolvedModules.add(m);
                resolvedModuleLookup.add(m.getClass());
            }
        }
    }
   
    List<Module> build(Injector injector) throws Exception {
        for (ModuleProvider provider : providers) {
            provider.getInstance(injector);
        }
        return resolvedModules;
    }
}
TOP

Related Classes of com.netflix.governator.guice.ModuleListBuilder$ModuleProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.