Java tutorial
/* * 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; } } }