org.grouplens.grapht.InjectionContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.grouplens.grapht.InjectionContainer.java

Source

/*
 * Grapht, an open source dependency injector.
 * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
 * Copyright 2010-2014 Regents of the University of Minnesota
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.grapht;

import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.grouplens.grapht.graph.DAGEdge;
import org.grouplens.grapht.graph.DAGNode;
import org.grouplens.grapht.reflect.Desire;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.annotation.PreDestroy;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Container for dependency-injected components.  A container is the scope of memoization, so
 * components with a cache policy of {@link CachePolicy#MEMOIZE} will share an instance so long
 * as they are instantiated by the same instantiator.
 *
 * @since 0.9
 * @author <a href="http://www.grouplens.org">GroupLens Research</a>
 */
public class InjectionContainer {
    private static final Logger logger = LoggerFactory.getLogger(InjectionContainer.class);

    private final CachePolicy defaultCachePolicy;
    private final Map<DAGNode<Component, Dependency>, Instantiator> providerCache;
    private final LifecycleManager manager;

    /**
     * Create a new instantiator with a default policy of {@code MEMOIZE}.
     * @return The instantiator.
     */
    public static InjectionContainer create() {
        return create(CachePolicy.MEMOIZE);
    }

    /**
     * Create a new instantiator without a lifecycle manager.
     * @param dft The default cache policy.
     * @return The instantiator.
     */
    public static InjectionContainer create(CachePolicy dft) {
        return new InjectionContainer(dft, null);
    }

    /**
     * Create a new instantiator.
     * @param dft The default cache policy.
     * @param mgr The lifecycle manager.
     * @return The instantiator.
     */
    public static InjectionContainer create(CachePolicy dft, LifecycleManager mgr) {
        return new InjectionContainer(dft, mgr);
    }

    private InjectionContainer(CachePolicy dft, LifecycleManager mgr) {
        defaultCachePolicy = dft;
        providerCache = new WeakHashMap<DAGNode<Component, Dependency>, Instantiator>();
        manager = mgr;
    }

    /**
     * Get a provider that, when invoked, will return an instance of the component represented
     * by a graph.
     *
     *
     * @param node The graph.
     * @return A provider to instantiate {@code graph}.
     * @see #makeInstantiator(DAGNode, SetMultimap)
     */
    public Instantiator makeInstantiator(DAGNode<Component, Dependency> node) {
        return makeInstantiator(node,
                ImmutableSetMultimap.<DAGNode<Component, Dependency>, DAGEdge<Component, Dependency>>of());
    }

    /**
     * Get a provider that, when invoked, will return an instance of the component represented
     * by a graph with back edges.  The provider will implement the cache policy, so cached nodes
     * will return a memoized provider.
     *
     * @param node The graph.
     * @param backEdges A multimap of back edges for cyclic dependencies.
     * @return A provider to instantiate {@code graph}.
     */
    public Instantiator makeInstantiator(DAGNode<Component, Dependency> node,
            SetMultimap<DAGNode<Component, Dependency>, DAGEdge<Component, Dependency>> backEdges) {
        Instantiator cached;
        synchronized (providerCache) {
            cached = providerCache.get(node);
        }
        if (cached == null) {
            logger.debug("Node has not been memoized, instantiating: {}", node.getLabel());

            Map<Desire, Instantiator> depMap = makeDependencyMap(node, backEdges);

            Instantiator raw = node.getLabel().getSatisfaction().makeInstantiator(depMap, manager);

            CachePolicy policy = node.getLabel().getCachePolicy();
            if (policy.equals(CachePolicy.NO_PREFERENCE)) {
                policy = defaultCachePolicy;
            }
            if (policy.equals(CachePolicy.MEMOIZE)) {
                // enforce memoization on providers for MEMOIZE policy
                cached = Instantiators.memoize(raw);
            } else {
                // Satisfaction.makeInstantiator() returns providers that are expected
                // to create new instances with each invocation
                assert policy.equals(CachePolicy.NEW_INSTANCE);
                cached = raw;
            }
            synchronized (providerCache) {
                if (!providerCache.containsKey(node)) {
                    providerCache.put(node, cached);
                } else {
                    logger.debug("two threads built instantiator for {}, discarding 2nd build", node);
                    cached = providerCache.get(node);
                }
            }
        }
        return cached;
    }

    private Map<Desire, Instantiator> makeDependencyMap(DAGNode<Component, Dependency> node,
            SetMultimap<DAGNode<Component, Dependency>, DAGEdge<Component, Dependency>> backEdges) {
        Set<DAGEdge<Component, Dependency>> edges = node.getOutgoingEdges();
        if (backEdges.containsKey(node)) {
            ImmutableSet.Builder<DAGEdge<Component, Dependency>> bld = ImmutableSet.builder();
            edges = bld.addAll(edges).addAll(backEdges.get(node)).build();
        }

        ImmutableSet.Builder<Desire> desires = ImmutableSet.builder();
        for (DAGEdge<Component, Dependency> edge : edges) {
            desires.add(edge.getLabel().getInitialDesire());
        }
        return Maps.asMap(desires.build(), new DepLookup(edges, backEdges));
    }

    /**
     * Get the lifecycle manager for this container.
     * @return The lifecycle manager for the container.
     */
    @Nullable
    public LifecycleManager getLifecycleManager() {
        return manager;
    }

    /**
     * Function to look up a desire in a set of dependency edges.
     */
    private class DepLookup implements Function<Desire, Instantiator> {
        private final Set<DAGEdge<Component, Dependency>> edges;
        private final SetMultimap<DAGNode<Component, Dependency>, DAGEdge<Component, Dependency>> backEdges;

        /**
         * Construct a depenency lookup funciton.
         * @param edges The set of edges to consult.
         * @param backEdges The back edge map (to pass to {@link #makeInstantiator(DAGNode,SetMultimap)}).
         */
        public DepLookup(Set<DAGEdge<Component, Dependency>> edges,
                SetMultimap<DAGNode<Component, Dependency>, DAGEdge<Component, Dependency>> backEdges) {
            this.edges = edges;
            this.backEdges = backEdges;
        }

        @Nullable
        @Override
        public Instantiator apply(@Nullable Desire input) {
            for (DAGEdge<Component, Dependency> edge : edges) {
                if (edge.getLabel().getInitialDesire().equals(input)) {
                    return makeInstantiator(edge.getTail(), backEdges);
                }
            }
            return null;
        }
    }
}