1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.solver;
21
22 import org.grouplens.grapht.CachePolicy;
23 import org.grouplens.grapht.ResolutionException;
24 import org.grouplens.grapht.annotation.*;
25 import org.grouplens.grapht.reflect.Desire;
26 import org.grouplens.grapht.reflect.Qualifiers;
27 import org.grouplens.grapht.reflect.Satisfaction;
28 import org.grouplens.grapht.reflect.Satisfactions;
29 import org.grouplens.grapht.util.Preconditions;
30 import org.grouplens.grapht.util.Types;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import javax.inject.Provider;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.lang.annotation.Annotation;
38 import java.net.URL;
39 import java.util.HashMap;
40 import java.util.Map;
41 import java.util.Properties;
42
43
44
45
46
47
48
49
50
51 public class DefaultDesireBindingFunction implements BindingFunction {
52 private static final String META_INF_DEFAULTS = "META-INF/grapht/defaults/";
53 private final Logger logger = LoggerFactory.getLogger(DefaultDesireBindingFunction.class);
54 private final ClassLoader classLoader;
55
56 private final Map<Class<?>, BindingResult> metaInfCache =
57 new HashMap<Class<?>, BindingResult>();
58
59 DefaultDesireBindingFunction(ClassLoader loader) {
60 Preconditions.notNull("spi", loader);
61 classLoader = loader;
62 }
63
64 public static DefaultDesireBindingFunction create(ClassLoader loader) {
65 if (loader == null) {
66 loader = Thread.currentThread().getContextClassLoader();
67 }
68 if (loader == null) {
69 loader = DefaultDesireBindingFunction.class.getClassLoader();
70 }
71 return new DefaultDesireBindingFunction(loader);
72 }
73
74 public static DefaultDesireBindingFunction create() {
75 return create(null);
76 }
77
78 @Override
79 public BindingResult bind(InjectionContext context, DesireChain dchain) throws ResolutionException {
80 Desire desire = dchain.getCurrentDesire();
81 BindingResult result = null;
82
83 Annotation qualifier = desire.getInjectionPoint().getQualifier();
84
85
86
87
88 if (dchain.getPreviousDesires().isEmpty() && qualifier != null) {
89 Class<? extends Annotation> annotType = qualifier.annotationType();
90 annotType = Qualifiers.resolveAliases(annotType);
91
92 result = getDefaultValue(desire, annotType);
93 if (result == null) {
94 result = getAnnotatedDefault(desire, annotType);
95 }
96
97
98 if (!annotType.isAnnotationPresent(AllowUnqualifiedMatch.class)) {
99 return result;
100 }
101 }
102
103
104
105 if (result == null) {
106 result = getAnnotatedDefault(desire, desire.getDesiredType());
107 }
108
109
110 if (result == null) {
111 result = getMetaInfDefault(desire, desire.getDesiredType());
112 }
113
114
115
116
117 return result;
118 }
119
120
121
122
123
124
125
126 private BindingResult getDefaultValue(Desire desire, Class<?> type) {
127
128 BindingResult.Builder bld = null;
129 DefaultDouble dfltDouble = type.getAnnotation(DefaultDouble.class);
130 if (dfltDouble != null) {
131 bld = BindingResult.newBuilder()
132 .setDesire(desire.restrict(Satisfactions.instance(dfltDouble.value())));
133 }
134 DefaultInteger dfltInt = type.getAnnotation(DefaultInteger.class);
135 if (dfltInt != null) {
136 bld = BindingResult.newBuilder().setDesire(desire.restrict(Satisfactions.instance(dfltInt.value())));
137 }
138 DefaultBoolean dfltBool = type.getAnnotation(DefaultBoolean.class);
139 if (dfltBool != null) {
140 bld = BindingResult.newBuilder().setDesire(desire.restrict(Satisfactions.instance(dfltBool.value())));
141 }
142 DefaultString dfltStr = type.getAnnotation(DefaultString.class);
143 if (dfltStr != null) {
144 bld = BindingResult.newBuilder().setDesire(desire.restrict(Satisfactions.instance(dfltStr.value())));
145 }
146 if (bld != null) {
147 return bld.setCachePolicy(CachePolicy.NO_PREFERENCE)
148 .addFlag(BindingFlag.TERMINAL)
149 .build();
150 } else {
151 return null;
152 }
153 }
154
155
156
157
158
159
160
161 private BindingResult getAnnotatedDefault(Desire desire, Class<?> type) {
162 DefaultProvider provider = type.getAnnotation(DefaultProvider.class);
163 BindingResult.Builder brb = null;
164 if (provider != null) {
165 brb = BindingResult.newBuilder()
166 .setDesire(desire.restrict(Satisfactions.providerType(provider.value())))
167 .setCachePolicy(provider.cachePolicy())
168 .addFlag(BindingFlag.TERMINAL);
169 if (provider.skipIfUnusable()) {
170 brb.addFlag(BindingFlag.SKIPPABLE);
171 }
172 }
173
174 DefaultImplementation impl = type.getAnnotation(DefaultImplementation.class);
175 if (impl != null) {
176 brb = BindingResult.newBuilder()
177 .setCachePolicy(impl.cachePolicy());
178 if (Types.isInstantiable(impl.value())) {
179 brb.setDesire(desire.restrict(Satisfactions.type(impl.value())));
180 } else {
181 brb.setDesire(desire.restrict(impl.value()));
182 }
183 if (impl.skipIfUnusable()) {
184 brb.addFlag(BindingFlag.SKIPPABLE);
185 }
186 }
187
188 DefaultNull dnull = type.getAnnotation(DefaultNull.class);
189 if (dnull != null) {
190 brb = BindingResult.newBuilder()
191 .setDesire(desire.restrict(Satisfactions.nullOfType(desire.getDesiredType())))
192 .setCachePolicy(CachePolicy.NO_PREFERENCE)
193 .addFlag(BindingFlag.TERMINAL);
194 }
195
196 return brb != null ? brb.build() : null;
197 }
198
199 @SuppressWarnings("unchecked")
200 private BindingResult getMetaInfDefault(Desire desire, Class<?> type) throws ResolutionException {
201 synchronized (metaInfCache) {
202 if (metaInfCache.containsKey(type)) {
203 return metaInfCache.get(type);
204 }
205 }
206
207 BindingResult.Builder builder = BindingResult.newBuilder();
208 boolean found = false;
209 String resourceName = META_INF_DEFAULTS + type.getCanonicalName() + ".properties";
210 logger.debug("searching for defaults in {}", resourceName);
211 URL url = classLoader.getResource(resourceName);
212
213 if (url != null) {
214 Properties props;
215 InputStream istr = null;
216 try {
217 istr = url.openStream();
218 props = new Properties();
219 props.load(istr);
220 } catch (IOException e) {
221 throw new ResolutionException("error reading " + resourceName, e);
222 } finally {
223 try {
224 if (istr != null) {
225 istr.close();
226 }
227 } catch (IOException e) {
228 logger.error("error closing {}: {}", resourceName, e);
229 }
230 }
231
232 String providerName = props.getProperty("provider");
233 if (providerName != null) {
234 try {
235 logger.debug("found provider {} for {}", providerName, type);
236 Class<?> clazz = classLoader.loadClass(providerName);
237 Satisfaction sat = Satisfactions.providerType((Class<Provider<?>>) clazz.asSubclass(Provider.class));
238 if (!type.isAssignableFrom(sat.getErasedType())) {
239 throw new ResolutionException(providerName + " does not provide " + type);
240 }
241 builder.setDesire(desire.restrict(sat))
242 .addFlag(BindingFlag.TERMINAL);
243 found = true;
244 } catch (ClassNotFoundException e) {
245 throw new ResolutionException("cannot find default provider for " + type, e);
246 }
247 }
248
249 String implName = props.getProperty("implementation");
250 if (implName != null) {
251 try {
252 logger.debug("found implementation {} for {}", implName, type);
253 Class<?> clazz = classLoader.loadClass(implName);
254 Satisfaction sat = Satisfactions.type(clazz);
255 if (!type.isAssignableFrom(sat.getErasedType())) {
256 throw new ResolutionException(providerName + " not compatible with " + type);
257 }
258 builder.setDesire(desire.restrict(sat));
259 found = true;
260 } catch (ClassNotFoundException e) {
261 throw new ResolutionException("cannot find default implementation for " + type, e);
262 }
263 }
264
265 String skip = props.getProperty("skipIfUnusable");
266 if (skip != null && skip.trim().toLowerCase().equals("true")) {
267 builder.addFlag(BindingFlag.SKIPPABLE);
268 }
269
270 if (found) {
271 String policy = props.getProperty("cachePolicy", "NO_PREFERENCE");
272 builder.setCachePolicy(CachePolicy.valueOf(policy));
273 }
274 }
275
276 BindingResult result = found ? builder.build() : null;
277 synchronized (metaInfCache) {
278 metaInfCache.put(type, result);
279 }
280 return result;
281 }
282 }