/* * 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.Preconditions; import com.google.common.base.Throwables; import org.grouplens.grapht.util.LogContext; import org.grouplens.grapht.util.TypedProvider; import org.grouplens.grapht.util.Types; import javax.inject.Provider; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import org.grouplens.grapht.util.LogContext; /** * Utilities and methods for building and working with {@link org.grouplens.grapht.Instantiator}s. * * @author <a href="http://www.grouplens.org">GroupLens Research</a> * @since 0.9 */ public final class Instantiators { private static final Logger logger = LoggerFactory.getLogger(Instantiators.class); private Instantiators() {} /** * Create an instantiator that returns an instance. * @param inst The instance to return (must be non-null). * @return An instantiator that returns {@code inst}. */ public static Instantiator ofInstance(Object inst) { Preconditions.checkNotNull(inst, "instance"); return new InstanceInstantiator(inst); } /** * Create an instantiator that returns a null value. * @param type The type of null value to return. * @return An instantiator that returns {@code null}. */ public static Instantiator ofNull(Class<?> type) { Preconditions.checkNotNull(type, "instance"); return new InstanceInstantiator(null, type); } /** * Convert a providerInstantiator to an instantiator. Any exception thrown by the provider - including a * runtime exception - is wrapped in an {@link ConstructionException}. * @param provider The providerInstantiator to wrap. * @return An instantiator wrapping {@code providerInstantiator}. */ public static Instantiator ofProvider(Provider<?> provider) { Preconditions.checkNotNull(provider, "provider"); return new ProviderInstantiator(ofInstance(provider)); } /** * Flatten an instnatiator of providers into an instantiator of the provided type. Any * exception thrown by the provider - including a runtime exception - is wrapped in an * {@link ConstructionException}. * @param pinst The providerInstantiator instantiator to wrap. * @return An instantiator wrapping {@code providerInstantiator}. */ public static Instantiator ofProviderInstantiator(Instantiator pinst) { Preconditions.checkNotNull(pinst, "provider instantiator"); Preconditions.checkArgument(Provider.class.isAssignableFrom(pinst.getType()), "instantiator is not of type Provider"); return new ProviderInstantiator(pinst); } /** * Convert an instantiator to a provider. * @param instantiator The instantiator to convert. * @return A provider whose {@link javax.inject.Provider#get()} method invokes the instantiator. */ public static Provider<?> toProvider(Instantiator instantiator) { // First try to unpack the instantiator if (instantiator instanceof ProviderInstantiator) { Instantiator itor = ((ProviderInstantiator) instantiator).providerInstantiator; if (itor instanceof InstanceInstantiator) { return (Provider) ((InstanceInstantiator) itor).instance; } } // Otherwise, wrap it. return new InstantiatorProvider(instantiator); } /** * Memoize an instantiator. * @param instantiator The instantiator to memoize. * @return An instantiator that memoizes {@code instantiator}. */ public static Instantiator memoize(Instantiator instantiator) { Preconditions.checkNotNull(instantiator, "instantiator"); return new MemoizingInstantiator(instantiator); } private static final class InstanceInstantiator implements Instantiator { private final Object instance; private final Class<?> type; public InstanceInstantiator(Object inst) { this(inst, (Class) inst.getClass()); } public InstanceInstantiator(Object inst, Class<?> typ) { instance = inst; type = typ; } @Override public Object instantiate() throws ConstructionException { return instance; } @Override public Class getType() { return type; } } private static class ProviderInstantiator implements Instantiator { private final Instantiator providerInstantiator; public ProviderInstantiator(Instantiator prov) { providerInstantiator = prov; } @Override public Object instantiate() throws ConstructionException { Provider<?> provider = (Provider) providerInstantiator.instantiate(); LogContext mdcContextProvider = LogContext.create(); logger.trace("invoking provider {}",provider); try { mdcContextProvider.put("org.grouplens.grapht.currentProvider", provider.toString()); return provider.get(); } catch (Throwable th) { throw new ConstructionException(getType(), "Error invoking provider " + providerInstantiator, th); } finally { mdcContextProvider.finish(); } } @SuppressWarnings("unchecked") @Override public Class<?> getType() { return Types.getProvidedType(providerInstantiator.getType()); } } private static class MemoizingInstantiator implements Instantiator { private final Instantiator delegate; private volatile boolean instantiated = false; private Object instance = null; private Throwable error = null; public MemoizingInstantiator(Instantiator inst) { delegate = inst; } @Override public Object instantiate() throws ConstructionException { if (!instantiated) { synchronized (this) { if (!instantiated) { try { instance = delegate.instantiate(); } catch (Throwable th) { error = th; } instantiated = true; } } } if (error != null) { Throwables.propagateIfPossible(error, ConstructionException.class); // shouldn't happen, but hey. throw Throwables.propagate(error); } else { return instance; } } @Override public Class getType() { return delegate.getType(); } } private static class InstantiatorProvider implements TypedProvider { private final Instantiator instantiator; public InstantiatorProvider(Instantiator itor) { instantiator = itor; } @Override public Class<?> getProvidedType() { return instantiator.getType(); } @Override public Object get() { try { logger.trace("invoking instantiator {}", instantiator); return getProvidedType().cast(instantiator.instantiate()); } catch (ConstructionException ex) { throw new RuntimeException(ex); } } } }