Package com.github.overengineer.container

Source Code of com.github.overengineer.container.DefaultContainer

package com.github.overengineer.container;

import com.github.overengineer.container.dynamic.DynamicComponentFactory;
import com.github.overengineer.container.key.*;
import com.github.overengineer.container.metadata.MetadataAdapter;
import com.github.overengineer.container.module.InstanceMapping;
import com.github.overengineer.container.module.Mapping;
import com.github.overengineer.container.module.Module;
import com.github.overengineer.container.scope.Scope;
import com.github.overengineer.container.scope.Scopes;
import com.github.overengineer.container.util.Order;
import com.github.overengineer.container.util.ParameterRef;
import com.github.overengineer.container.util.ParameterRefImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.*;

/**
*
* TODO Features:
* TODO scoped containers and proxies, factories, enhance interceptor rules (@OR, @AND, @NOT)
* TODO named containers
* TODO for scoped proxies - new proxy that uses a provider to obtain it's component on each request -
* TODO       new ThreadLocalContainer interface and DelegatingThreadLocalContainer impl
* TODO       new ThreadLocalContainerStrategy, so new DelegatingThreadLocalContainer(threadLocalStrategy);
* TODO       for instance:
*
* <pre>
*
*     class SessionScopedContainerStrategy implements ThreadLocalContainerStrategy {
*
*          SessionScopedContainerStrategy(Module ... sessionModules) {
*
*          }
*
*          public Container getContainer(String name?) {
*              HttpServletRequest request = RequestHolder.getRequest();
*              HttpSession session = request.getSession(true);
*              Container = session.get("session.scoped.container." + name);
*              if (container == null) {
*                  container = Clarence.please.gimmeThatTainer(sessionModules);
*                  session.put(...
*              }
*              return container;
*          }
*
*     }
*
*
* </pre>
*
*
<pre>
*
*     class MyApplication extends WebApplication {
*
*
*          protected void configure() {
*
*              requestScope.loadModule(...
*
*              sessionScope.loadModule(...
*
*              globalScope.loadModule(...
*
*              master.loadModule(...
*
*
*          }
*
*     }
*
*
* </pre>
*
* TODO ScopedProxyHandler(ThreadLocalContainer container)
* TODO
*
*
* TODO Tech debt:
* TODO cleanup interceptor impl, move from extensions to decorations?
* TODO move aspect list to invocation factory, create new "aspect cache" interface for factory to implement?
* TODO then the aop container can just interface with its cache and that of its children?
*
* TODO strategies keys become UnqualifiedDependencyKey
* TODO strategies hold reference to QualifiedImplementationKey
* TODO container takes requests for QualifiedDependencyRequest
* TODO component strategy becomes
*
* @author rees.byars
*/
public class DefaultContainer implements Container {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultContainer.class);

    private final Map<Key<?>, SortedSet<ComponentStrategy<?>>> strategies = new HashMap<Key<?>, SortedSet<ComponentStrategy<?>>>();
    private final List<Container> cascadingContainers = new ArrayList<Container>();
    private final List<Container> children = new ArrayList<Container>();
    private final ComponentStrategyFactory strategyFactory;
    private final DynamicComponentFactory dynamicComponentFactory;
    private final MetadataAdapter metadataAdapter;
    private final List<ComponentInitializationListener> componentInitializationListeners;

    public DefaultContainer(ComponentStrategyFactory strategyFactory, DynamicComponentFactory dynamicComponentFactory, MetadataAdapter metadataAdapter, List<ComponentInitializationListener> componentInitializationListeners) {
        this.strategyFactory = strategyFactory;
        this.dynamicComponentFactory = dynamicComponentFactory;
        this.metadataAdapter = metadataAdapter;
        this.componentInitializationListeners = componentInitializationListeners;
    }

    @Override
    public void verify() throws WiringException {
        LOG.info("Verifying container.");
        try {
            for (Key<?> key : strategies.keySet()) {
                get(key);
            }
        } catch (Exception e) {
            throw new WiringException("An exception occurred while verifying the container", e);
        }
        for (Container child : children) {
            child.verify();
        }
        for (Container cascader : cascadingContainers) {
            cascader.verify();
        }
        LOG.info("Container verified.");
    }

    @Override
    public <M extends Module> Container loadModule(Class<M> moduleClass) {
        Module module = strategyFactory.create(moduleClass, Qualifier.NONE, Scopes.PROTOTYPE).get(this);
        for (Mapping<?> mapping : module.getMappings()) {
            Class<?> implementationType = mapping.getImplementationType();
            Object qualifier = mapping.getQualifier();
            if (qualifier.equals(Qualifier.NONE)) {
                qualifier = metadataAdapter.getQualifier(implementationType, implementationType.getAnnotations());
            }
            if (mapping instanceof InstanceMapping) {
                InstanceMapping<?> instanceMapping = (InstanceMapping) mapping;
                Object instance = instanceMapping.getInstance();
                for (Class<?> target : mapping.getTargetClasses()) {
                    addMapping(Locksmith.makeKey(target, qualifier), instance);
                }
                for (Key<?> targetGeneric : mapping.getTargetKeys()) {
                    addMapping(targetGeneric, instance);
                }
            } else {
                for (Class<?> target : mapping.getTargetClasses()) {
                    addMapping(Locksmith.makeKey(target, qualifier), implementationType, mapping.getScope());
                }
                for (Key<?> targetGeneric : mapping.getTargetKeys()) {
                    addMapping(targetGeneric, implementationType, mapping.getScope());
                }
            }
        }
        for (Map.Entry<Key, Class> entry : module.getNonManagedComponentFactories().entrySet()) {
            registerNonManagedComponentFactory(entry.getKey(), entry.getValue());
        }
        return this;
    }

    @Override
    public synchronized Container addCascadingContainer(Container container) {
        if (this == container.getReal()) {
            throw new CircularReferenceException("Cannot add a container as a cascading container of itself");
        }
        if (isThisCascaderOfTarget(container)) {
            throw new CircularReferenceException("Cannot add a container as a cascader of one of its cascaders");
        }
        if (isTargetChildOfThis(container)) {
            throw new CircularReferenceException("Cannot add a child container as a cascader");
        }
        if (thisHasChildrenInCommonWithTarget(container)) {
            throw new CircularReferenceException("Cannot add a container as a cascader if the containers have children in common");
        }
        cascadingContainers.add(container);
        for (Container child : children) {
            child.addCascadingContainer(container);
        }
        return this;
    }

    @Override
    public synchronized Container addChild(Container child) {
        if (this == child.getReal()) {
            throw new CircularReferenceException("Cannot add a container as a child of itself");
        }
        if (isTargetCascaderOfThis(child)) {
            throw new CircularReferenceException("Cannot add a container as a child if it is already a cascader");
        }
        if (isThisCascaderOfTarget(child)) {
            throw new CircularReferenceException("Cannot add a container as a child of the one of the container's cascaders");
        }
        if (isThisChildOfTarget(child)) {
            throw new CircularReferenceException("Cannot add a container as a child of one of it's children");
        }
        children.add(child);
        for (Container cascadingContainer : cascadingContainers) {
            child.addCascadingContainer(cascadingContainer);
        }
        return this;
    }

    @Override
    public Container newEmptyClone() {
        return strategyFactory.create(this.getClass(), Qualifier.NONE, Scopes.SINGLETON).get(this);
    }

    @Override
    public Container addListener(Class<? extends ComponentInitializationListener> listenerClass) {
        ComponentStrategy strategy = strategyFactory.create(listenerClass, Qualifier.NONE, Scopes.SINGLETON);
        getInitializationListeners().add((ComponentInitializationListener) strategy.get(this));
        return this;
    }

    @Override
    public <T> Container add(Class<T> componentType, Class<? extends T> implementationType) {
        add(Locksmith.makeKey(componentType, metadataAdapter.getQualifier(implementationType, implementationType.getAnnotations())), implementationType);
        return this;
    }

    @Override
    public <T> Container add(Class<T> componentType, Object qualifier, Class<? extends T> implementationType) {
        add(Locksmith.makeKey(componentType, qualifier), implementationType);
        return this;
    }

    @Override
    public <T> Container add(Key<T> key, Class<? extends T> implementationType) {
        addMapping(key, implementationType, Scopes.SINGLETON);
        return this;
    }

    @Override
    public <T, I extends T> Container addInstance(Class<T> componentType, I implementation) {
        addInstance(Locksmith.makeKey(componentType, metadataAdapter.getQualifier(implementation.getClass(), implementation.getClass().getAnnotations())), implementation);
        return this;
    }

    @Override
    public <T, I extends T> Container addInstance(Class<T> componentType, Object qualifier, I implementation) {
        addInstance(Locksmith.makeKey(componentType, qualifier), implementation);
        return this;
    }

    @Override
    public <T, I extends T> Container addInstance(Key<T> key, I implementation) {
        addMapping(key, implementation);
        return this;
    }

    @Override
    public Container addCustomProvider(Class<?> providedType, Class<?> customProviderType) {
        addCustomProvider(Locksmith.makeKey(providedType, metadataAdapter.getQualifier(providedType, providedType.getAnnotations())), customProviderType);
        return this;
    }

    @Override
    public Container addCustomProvider(Key<?> providedTypeKey, Class<?> customProviderType) {
        Object qualifier = providedTypeKey.getQualifier();
        Key<?> providerKey = Locksmith.makeKey(customProviderType, qualifier);
        ComponentStrategy providerStrategy = getStrategy(providerKey);
        if (providerStrategy == null) {
            providerStrategy = strategyFactory.create(customProviderType, qualifier, Scopes.SINGLETON);
        }
        putStrategy(providerKey, providerStrategy);
        putStrategy(providedTypeKey, strategyFactory.createCustomStrategy(providerStrategy, qualifier));
        return this;
    }

    @Override
    public Container addCustomProvider(Class<?> providedType, Object customProvider) {
        addCustomProvider(Locksmith.makeKey(providedType, metadataAdapter.getQualifier(providedType, providedType.getAnnotations())), customProvider);
        return this;
    }

    @Override
    public Container addCustomProvider(Key<?> providedTypeKey, Object customProvider) {
        Object qualifier = providedTypeKey.getQualifier();
        Key<?> providerKey = Locksmith.makeKey(customProvider.getClass(), qualifier);
        ComponentStrategy providerStrategy = getStrategy(providerKey);
        if (providerStrategy == null) {
            providerStrategy = strategyFactory.createInstanceStrategy(customProvider, qualifier);
        }
        putStrategy(providerKey, providerStrategy);
        putStrategy(providedTypeKey, strategyFactory.createCustomStrategy(providerStrategy, qualifier));
        return this;
    }

    @Override
    public Container registerNonManagedComponentFactory(Key<?> factoryKey, Class producedType) {
        addMapping(factoryKey, dynamicComponentFactory.createNonManagedComponentFactory(factoryKey.getTargetClass(), producedType, this));
        return this;
    }

    @Override
    public synchronized Container registerCompositeTarget(Class<?> targetInterface) {
        registerCompositeTarget(Locksmith.makeKey(targetInterface));
        return this;
    }

    @Override
    public Container registerCompositeTarget(Class<?> targetInterface, Object qualifier) {
        registerCompositeTarget(Locksmith.makeKey(targetInterface, qualifier));
        return this;
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized Container registerCompositeTarget(Key targetKey) {
        Object composite = dynamicComponentFactory.createCompositeHandler(targetKey.getTargetClass(), this);
        ComponentStrategy compositeStrategy = new TopLevelStrategy(strategyFactory.createInstanceStrategy(composite, targetKey.getQualifier()));
        putStrategy(targetKey, compositeStrategy);
        return this;
    }

    @Override
    public Container registerDeconstructedApi(Class<?> targetInterface) {
        registerDeconstructedApi(Locksmith.makeKey(targetInterface));
        return this;
    }

    @Override
    public Container registerDeconstructedApi(Class<?> targetInterface, Object qualifier) {
        registerDeconstructedApi(Locksmith.makeKey(targetInterface, qualifier));
        return this;
    }

    @Override
    public Container registerDeconstructedApi(Key<?> targetKey) {
        Object delegatingService = dynamicComponentFactory.createDelegatingService(targetKey.getTargetClass(), this);
        ComponentStrategy strategy = strategyFactory.createInstanceStrategy(delegatingService, targetKey.getQualifier());
        putStrategy(targetKey, strategy);
        return this;
    }

    @Override
    public List<ComponentInitializationListener> getInitializationListeners() {
        return componentInitializationListeners;
    }

    @Override
    public List<Object> getAllComponents() {
        List<Object> components = new LinkedList<Object>();
        components.addAll(getInitializationListeners());
        for (SortedSet<ComponentStrategy<?>> strategySet : strategies.values()) {
            for (ComponentStrategy<?> strategy : strategySet) {
                components.add(strategy.get(this));
            }
        }
        for (Container child : children) {
            components.addAll(child.getAllComponents());
        }
        return components;
    }

    @Override
    public List<Container> getCascadingContainers() {
        List<Container> result = new LinkedList<Container>(cascadingContainers);
        for (Container child : getChildren()) {
            result.addAll(child.getCascadingContainers());
        }
        for (Container cascader : cascadingContainers) {
            result.addAll(cascader.getChildren());
        }
        return result;
    }

    @Override
    public List<Container> getChildren() {
        List<Container> result = new LinkedList<Container>(children);
        for (Container child : children) {
            result.addAll(child.getChildren());
        }
        return result;
    }

    @Override
    public Container getReal() {
        return this;
    }

    @Override
    public Container makeInjectable() {
        addInstance(Container.class, this);
        addInstance(Provider.class, this);
        return this;
    }

    @Override
    public <T> T get(Class<T> clazz, SelectionAdvisor ... advisors) {
        return get(Locksmith.makeKey(clazz), advisors);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Class<T> clazz, Object qualifier, SelectionAdvisor ... advisors) {
        return get(Locksmith.makeKey(clazz, qualifier), advisors);
    }

    @Override
    public <T> T get(final Key<T> key, SelectionAdvisor ... advisors) {

        @SuppressWarnings("unchecked")
        ComponentStrategy<T> strategy = getStrategy(key, advisors);

        if (strategy != null) {
            return strategy.get(this);
        }

        Class<?> targetClass = key.getTargetClass();
        Type targetType = key.getType();

        if (!(targetType instanceof ParameterizedType)) {
            throw new MissingDependencyException(key);
        }

        if (((ParameterizedType) targetType).getActualTypeArguments().length > 1) {
            throw new MissingDependencyException(key);
        }

        //TODO this is slow, refactor to cache the type in the key and to reuse the strategy
        Key parameterizedKey = Locksmith.makeKey(new ParameterRef() {
            @Override
            public Type getType() {
                return ((ParameterizedType) key.getType()).getActualTypeArguments()[0];
            }
        }, key.getQualifier());

        if (metadataAdapter.getProviderClass().isAssignableFrom(targetClass)) {

            T instance = dynamicComponentFactory.createManagedComponentFactory(metadataAdapter.getProviderClass(), parameterizedKey, this);
            strategy = strategyFactory.createInstanceStrategy(instance, Qualifier.NONE);
            putStrategy(key, strategy);

            return strategy.get(this);

        }

        if (!(Collection.class.isAssignableFrom(targetClass))) {
            throw new MissingDependencyException(key);
        }

        //TODO store results in an instance strategy for better perf

        if (List.class.isAssignableFrom(targetClass)) {

            @SuppressWarnings("unchecked")
            T t = (T) getAll(parameterizedKey);
            return t;

        }

        if (Set.class.isAssignableFrom(targetClass)) {

            @SuppressWarnings("unchecked")
            T t = (T) new HashSet(getAll(parameterizedKey));
            return t;

        }

        if (Collection.class == targetClass) {

            @SuppressWarnings("unchecked")
            T t = (T) getAll(parameterizedKey);
            return t;

        }

        throw new MissingDependencyException(key);


    }

    @Override
    public <T> List<T> getAll(Class<T> clazz, SelectionAdvisor... advisors) {
        return getAll(Locksmith.makeKey(clazz), advisors);
    }

    @Override
    public <T> List<T> getAll(Class<T> clazz, Object qualifier, SelectionAdvisor... advisors) {
        return getAll(Locksmith.makeKey(clazz, qualifier), advisors);
    }

    @Override
    public <T> List<T> getAll(Key<T> key, SelectionAdvisor... advisors) {
        List<T> components = new LinkedList<T>();
        List<ComponentStrategy<T>> componentStrategies = getAllStrategies(key, advisors);
        for (ComponentStrategy<T> strategy : componentStrategies) {
            components.add(strategy.get(this));
        }
        return components;
    }

    protected synchronized void addMapping(Key key, final Class<?> implementationType, Scope scope) {

        Key componentKey = Locksmith.makeKey(implementationType, key.getQualifier());

        ComponentStrategy strategy = getStrategy(componentKey, new SelectionAdvisor() {
            @Override
            public boolean validSelection(ComponentStrategy<?> candidateStrategy) {
                return candidateStrategy.getComponentType() == implementationType;
            }
        });

        if (strategy == null) {
            strategy = strategyFactory.create(implementationType, key.getQualifier(), scope);
            putStrategy(componentKey, strategy);
        }

        putStrategy(key, strategy);

    }

    protected synchronized void addMapping(Key key, Object implementation) {

        ComponentStrategy newStrategy = strategyFactory.createInstanceStrategy(implementation, key.getQualifier());
        putStrategy(key, newStrategy);
        putStrategy(Locksmith.makeKey(implementation.getClass()), newStrategy);

    }

     @Override
     public <T> ComponentStrategy<T> getStrategy(Key<T> key, SelectionAdvisor ... advisors) {

        Object qualifier = key.getQualifier();
        boolean qualified = !Qualifier.NONE.equals(qualifier);

        SortedSet<ComponentStrategy<T>> strategySet = getStrategySet(key);
        if (strategySet != null) {
            for (ComponentStrategy<T> strategy : strategySet) {
                boolean valid = true;
                if (qualified && !qualifier.equals(strategy.getQualifier())) {
                    continue;
                }
                for (SelectionAdvisor advisor : advisors) {
                    if (!advisor.validSelection(strategy)) {
                        valid = false;
                        break;
                    }
                }
                if (valid) {
                    return strategy;
                }
            }
        }
        for (Container child : children) {
            ComponentStrategy<T> strategy = child.getStrategy(key);
            if (strategy != null) {
                return strategy;
            }
        }
        for (Container container : cascadingContainers) {
            ComponentStrategy<T> strategy = container.getStrategy(key);
            if (strategy != null) {
                return strategy;
            }
        }
        return null;
    }

    @Override
    public <T> List<ComponentStrategy<T>> getAllStrategies(final Key<T> key, SelectionAdvisor... advisors) {

        List<ComponentStrategy<T>> allStrategies = new LinkedList<ComponentStrategy<T>>();

        Object qualifier = key.getQualifier();
        boolean qualified = !Qualifier.NONE.equals(qualifier);

        SortedSet<ComponentStrategy<T>> strategySet = getStrategySet(key);
        if (strategySet != null) {
            for (ComponentStrategy<T> strategy : strategySet) {
                if (!qualified || qualifier.equals(strategy.getQualifier())) {
                    boolean valid = true;
                    for (SelectionAdvisor advisor : advisors) {
                        if (!advisor.validSelection(strategy)) {
                            valid = false;
                            break;
                        }
                    }
                    if (valid) {
                        allStrategies.add(strategy);
                    }
                }
            }
        }
        for (Container child : children) {
            List<ComponentStrategy<T>> childAllStrategies = child.getAllStrategies(key, advisors);
            allStrategies.addAll(childAllStrategies);
        }
        for (Container container : cascadingContainers) {
            List<ComponentStrategy<T>> containerAllStrategies = container.getAllStrategies(key, advisors);
            allStrategies.addAll(containerAllStrategies);
        }
        return allStrategies;
    }

    @SuppressWarnings("unchecked")
    protected <T> SortedSet<ComponentStrategy<T>> getStrategySet(Key<T> key) {
        SortedSet<ComponentStrategy<?>> strategySet = strategies.get(key);
        return (SortedSet<ComponentStrategy<T>>) (SortedSet) strategySet;
    }

    protected void putStrategy(Key key, ComponentStrategy<?> strategy) {
        SortedSet<ComponentStrategy<?>> strategySet = strategies.get(key);
        if (strategySet == null) {
            strategySet = new TreeSet<ComponentStrategy<?>>(new StrategyComparator() {
                @Override
                public int compare(ComponentStrategy<?> strategy, ComponentStrategy<?> strategy2) {
                    if (strategy.equals(strategy2) ||
                            //TODO need a better way to ensure only one composite/delegating service etc is allowed
                            (strategy.getComponentType().equals(strategy2.getComponentType())
                                    && strategy.getQualifier().equals(strategy2.getQualifier()
                            ) && !Proxy.isProxyClass(strategy.getComponentType()))) {
                        return Order.EXCLUDE;
                    } else if (strategy instanceof TopLevelStrategy) {
                        return Order.PREPEND;
                    } else if (strategy2 instanceof TopLevelStrategy) {
                        return Order.APPEND;
                    } else if (strategy.isDecorator()) {
                        return Order.PREPEND;
                    } else if (strategy2.isDecorator()) {
                        return Order.APPEND;
                    }
                    return Order.PREPEND;
                }
            });
            strategies.put(key, strategySet);
        }
        strategySet.add(strategy);
    }

    protected boolean isTargetCascaderOfThis(Container target) {
        for (Container cascader : getCascadingContainers()) {
            if (cascader.getReal() == target.getReal()) {
                return true;
            }
        }
        return false;
    }

    protected boolean isTargetChildOfThis(Container target) {
        for (Container child : getChildren()) {
            if (child.getReal() == target.getReal()) {
                return true;
            }
        }
        return false;
    }

    protected boolean isThisCascaderOfTarget(Container target) {
        for (Container cascader : target.getCascadingContainers()) {
            if (cascader.getReal() == this) {
                return true;
            }
        }
        return false;
    }

    protected boolean isThisChildOfTarget(Container target) {
        for (Container child : target.getChildren()) {
            if (child.getReal() == this) {
                return true;
            }
        }
        return false;
    }

    protected boolean thisHasChildrenInCommonWithTarget(Container target) {
        for (Container targetChild : target.getChildren()) {
            for (Container child : getChildren()) {
                if (targetChild.getReal() == child.getReal()) {
                    return true;
                }
            }
        }
        return false;
    }

}
TOP

Related Classes of com.github.overengineer.container.DefaultContainer

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.