View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht;
21  
22  import com.google.common.base.Preconditions;
23  import com.google.common.base.Throwables;
24  import org.grouplens.grapht.util.LogContext;
25  import org.grouplens.grapht.util.TypedProvider;
26  import org.grouplens.grapht.util.Types;
27  import javax.inject.Provider;
28  import org.slf4j.LoggerFactory;
29  import org.slf4j.Logger;
30  import org.grouplens.grapht.util.LogContext;
31  
32  /**
33   * Utilities and methods for building and working with {@link org.grouplens.grapht.Instantiator}s.
34   *
35   * @author <a href="http://www.grouplens.org">GroupLens Research</a>
36   * @since 0.9
37   */
38  public final class Instantiators {
39      private static final Logger logger = LoggerFactory.getLogger(Instantiators.class);
40  
41      private Instantiators() {}
42  
43      /**
44       * Create an instantiator that returns an instance.
45       * @param inst The instance to return (must be non-null).
46       * @return An instantiator that returns {@code inst}.
47       */
48      public static Instantiator ofInstance(Object inst) {
49          Preconditions.checkNotNull(inst, "instance");
50          return new InstanceInstantiator(inst);
51      }
52  
53      /**
54       * Create an instantiator that returns a null value.
55       * @param type The type of null value to return.
56       * @return An instantiator that returns {@code null}.
57       */
58      public static Instantiator ofNull(Class<?> type) {
59          Preconditions.checkNotNull(type, "instance");
60          return new InstanceInstantiator(null, type);
61      }
62  
63      /**
64       * Convert a providerInstantiator to an instantiator.  Any exception thrown by the provider - including a
65       * runtime exception - is wrapped in an {@link ConstructionException}.
66       * @param provider The providerInstantiator to wrap.
67       * @return An instantiator wrapping {@code providerInstantiator}.
68       */
69      public static Instantiator ofProvider(Provider<?> provider) {
70          Preconditions.checkNotNull(provider, "provider");
71          return new ProviderInstantiator(ofInstance(provider));
72      }
73  
74      /**
75       * Flatten an instnatiator of providers into an instantiator of the provided type.  Any
76       * exception thrown by the provider - including a runtime exception - is wrapped in an
77       * {@link ConstructionException}.
78       * @param pinst The providerInstantiator instantiator to wrap.
79       * @return An instantiator wrapping {@code providerInstantiator}.
80       */
81      public static Instantiator ofProviderInstantiator(Instantiator pinst) {
82          Preconditions.checkNotNull(pinst, "provider instantiator");
83          Preconditions.checkArgument(Provider.class.isAssignableFrom(pinst.getType()),
84                                      "instantiator is not of type Provider");
85  
86          return new ProviderInstantiator(pinst);
87      }
88  
89      /**
90       * Convert an instantiator to a provider.
91       * @param instantiator The instantiator to convert.
92       * @return A provider whose {@link javax.inject.Provider#get()} method invokes the instantiator.
93       */
94      public static Provider<?> toProvider(Instantiator instantiator) {
95          // First try to unpack the instantiator
96          if (instantiator instanceof ProviderInstantiator) {
97              Instantiator itor = ((ProviderInstantiator) instantiator).providerInstantiator;
98              if (itor instanceof InstanceInstantiator) {
99                  return (Provider) ((InstanceInstantiator) itor).instance;
100             }
101         }
102         // Otherwise, wrap it.
103 
104         return new InstantiatorProvider(instantiator);
105     }
106 
107     /**
108      * Memoize an instantiator.
109      * @param instantiator The instantiator to memoize.
110      * @return An instantiator that memoizes {@code instantiator}.
111      */
112     public static Instantiator memoize(Instantiator instantiator) {
113         Preconditions.checkNotNull(instantiator, "instantiator");
114         return new MemoizingInstantiator(instantiator);
115     }
116     private static final class InstanceInstantiator implements Instantiator {
117         private final Object instance;
118         private final Class<?> type;
119 
120         public InstanceInstantiator(Object inst) {
121             this(inst, (Class) inst.getClass());
122         }
123 
124         public InstanceInstantiator(Object inst, Class<?> typ) {
125             instance = inst;
126             type = typ;
127         }
128 
129         @Override
130         public Object instantiate() throws ConstructionException {
131             return instance;
132         }
133 
134         @Override
135         public Class getType() {
136             return type;
137         }
138     }
139 
140     private static class ProviderInstantiator implements Instantiator {
141         private final Instantiator providerInstantiator;
142         public ProviderInstantiator(Instantiator prov) {
143             providerInstantiator = prov;
144         }
145 
146         @Override
147         public Object instantiate() throws ConstructionException {
148             Provider<?> provider = (Provider) providerInstantiator.instantiate();
149             LogContext mdcContextProvider = LogContext.create();
150             logger.trace("invoking provider {}",provider);
151             try {
152                 mdcContextProvider.put("org.grouplens.grapht.currentProvider", provider.toString());
153                 return provider.get();
154             } catch (Throwable th) {
155                 throw new ConstructionException(getType(), "Error invoking provider " + providerInstantiator, th);
156             } finally {
157                 mdcContextProvider.finish();
158             }
159         }
160         @SuppressWarnings("unchecked")
161         @Override
162         public Class<?> getType() {
163             return Types.getProvidedType(providerInstantiator.getType());
164         }
165     }
166 
167 
168     private static class MemoizingInstantiator implements Instantiator {
169         private final Instantiator delegate;
170         private volatile boolean instantiated = false;
171         private Object instance = null;
172         private Throwable error = null;
173 
174         public MemoizingInstantiator(Instantiator inst) {
175             delegate = inst;
176         }
177 
178         @Override
179         public Object instantiate() throws ConstructionException {
180             if (!instantiated) {
181                 synchronized (this) {
182                     if (!instantiated) {
183                         try {
184                             instance = delegate.instantiate();
185                         } catch (Throwable th) {
186                             error = th;
187                         }
188                         instantiated = true;
189                     }
190                 }
191             }
192 
193             if (error != null) {
194                 Throwables.propagateIfPossible(error, ConstructionException.class);
195                 // shouldn't happen, but hey.
196                 throw Throwables.propagate(error);
197             } else {
198                 return instance;
199             }
200         }
201 
202         @Override
203         public Class getType() {
204             return delegate.getType();
205         }
206     }
207 
208     private static class InstantiatorProvider implements TypedProvider {
209         private final Instantiator instantiator;
210 
211         public InstantiatorProvider(Instantiator itor) {
212             instantiator = itor;
213         }
214 
215         @Override
216         public Class<?> getProvidedType() {
217             return instantiator.getType();
218         }
219 
220         @Override
221         public Object get() {
222             try {
223                 logger.trace("invoking instantiator {}", instantiator);
224                 return getProvidedType().cast(instantiator.instantiate());
225             } catch (ConstructionException ex) {
226                 throw new RuntimeException(ex);
227             }
228         }
229     }
230 
231 }