1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.di.impl;
20
21 import java.io.BufferedReader;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.lang.annotation.Annotation;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Modifier;
27 import java.lang.reflect.Type;
28 import java.net.URL;
29 import java.util.*;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.function.Function;
32 import java.util.function.Supplier;
33 import java.util.stream.Collectors;
34 import java.util.stream.Stream;
35
36 import org.apache.maven.api.di.Provides;
37 import org.apache.maven.api.di.Qualifier;
38 import org.apache.maven.api.di.Singleton;
39 import org.apache.maven.api.di.Typed;
40 import org.apache.maven.di.Injector;
41 import org.apache.maven.di.Key;
42 import org.apache.maven.di.Scope;
43
44 public class InjectorImpl implements Injector {
45
46 private final Map<Key<?>, Set<Binding<?>>> bindings = new HashMap<>();
47 private final Map<Class<? extends Annotation>, Scope> scopes = new HashMap<>();
48
49 public InjectorImpl() {
50 bindScope(Singleton.class, new SingletonScope());
51 }
52
53 public <T> T getInstance(Class<T> key) {
54 return getInstance(Key.of(key));
55 }
56
57 public <T> T getInstance(Key<T> key) {
58 return getCompiledBinding(key).get();
59 }
60
61 @SuppressWarnings("unchecked")
62 @Override
63 public <T> void injectInstance(T instance) {
64 ReflectionUtils.generateInjectingInitializer(Key.of((Class<T>) instance.getClass()))
65 .compile(this::getCompiledBinding)
66 .accept(instance);
67 }
68
69 @Override
70 public Injector discover(ClassLoader classLoader) {
71 try {
72 Enumeration<URL> enumeration = classLoader.getResources("META-INF/maven/org.apache.maven.api.di.Inject");
73 while (enumeration.hasMoreElements()) {
74 try (InputStream is = enumeration.nextElement().openStream();
75 BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
76 for (String line :
77 reader.lines().filter(l -> !l.startsWith("#")).collect(Collectors.toList())) {
78 Class<?> clazz = classLoader.loadClass(line);
79 bindImplicit(clazz);
80 }
81 }
82 }
83 } catch (Exception e) {
84 throw new DIException("Error while discovering DI classes from classLoader", e);
85 }
86 return this;
87 }
88
89 public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
90 if (scopes.put(scopeAnnotation, scope) != null) {
91 throw new DIException(
92 "Cannot rebind scope annotation class to a different implementation: " + scopeAnnotation);
93 }
94 return this;
95 }
96
97 public <U> Injector bindInstance(Class<U> clazz, U instance) {
98 Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
99 Binding<U> binding = Binding.toInstance(instance);
100 return doBind(key, binding);
101 }
102
103 @Override
104 public Injector bindImplicit(Class<?> clazz) {
105 Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
106 if (clazz.isInterface()) {
107 bindings.computeIfAbsent(key, $ -> new HashSet<>());
108 } else if (!Modifier.isAbstract(clazz.getModifiers())) {
109 Binding<?> binding = ReflectionUtils.generateImplicitBinding(key);
110 doBind(key, binding);
111 }
112 return this;
113 }
114
115 private LinkedHashSet<Key<?>> current = new LinkedHashSet<>();
116
117 private Injector doBind(Key<?> key, Binding<?> binding) {
118 if (!current.add(key)) {
119 current.add(key);
120 throw new DIException("Circular references: " + current);
121 }
122 doBindImplicit(key, binding);
123 Class<?> cls = key.getRawType().getSuperclass();
124 while (cls != Object.class && cls != null) {
125 key = Key.of(cls, key.getQualifier());
126 doBindImplicit(key, binding);
127 cls = cls.getSuperclass();
128 }
129 current.remove(key);
130 return this;
131 }
132
133 protected <U> Injector bind(Key<U> key, Binding<U> b) {
134 Set<Binding<?>> bindingSet = bindings.computeIfAbsent(key, $ -> new HashSet<>());
135 bindingSet.add(b);
136 return this;
137 }
138
139 @SuppressWarnings({"unchecked", "rawtypes"})
140 protected <T> Set<Binding<T>> getBindings(Key<T> key) {
141 return (Set) bindings.get(key);
142 }
143
144 protected Set<Key<?>> getBoundKeys() {
145 return bindings.keySet();
146 }
147
148 public Map<Key<?>, Set<Binding<?>>> getBindings() {
149 return bindings;
150 }
151
152 public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
153 Set<Binding<Q>> res = getBindings(key);
154 if (res != null && !res.isEmpty()) {
155 List<Binding<Q>> bindingList = new ArrayList<>(res);
156 Comparator<Binding<Q>> comparing = Comparator.comparing(Binding::getPriority);
157 bindingList.sort(comparing.reversed());
158 Binding<Q> binding = bindingList.get(0);
159 return compile(binding);
160 }
161 if (key.getRawType() == List.class) {
162 Set<Binding<Object>> res2 = getBindings(key.getTypeParameter(0));
163 if (res2 != null) {
164 List<Supplier<Object>> list = res2.stream().map(this::compile).collect(Collectors.toList());
165
166 return () -> (Q) list(list);
167 }
168 }
169 if (key.getRawType() == Map.class) {
170 Key<?> k = key.getTypeParameter(0);
171 Key<Object> v = key.getTypeParameter(1);
172 Set<Binding<Object>> res2 = getBindings(v);
173 if (k.getRawType() == String.class && res2 != null) {
174 Map<String, Supplier<Object>> map = res2.stream()
175 .filter(b -> b.getOriginalKey() == null
176 || b.getOriginalKey().getQualifier() == null
177 || b.getOriginalKey().getQualifier() instanceof String)
178 .collect(Collectors.toMap(
179 b -> (String)
180 (b.getOriginalKey() != null
181 ? b.getOriginalKey().getQualifier()
182 : null),
183 this::compile));
184
185 return () -> (Q) map(map);
186 }
187 }
188 throw new DIException("No binding to construct an instance for key "
189 + key.getDisplayString() + ". Existing bindings:\n"
190 + getBoundKeys().stream()
191 .map(Key::toString)
192 .map(String::trim)
193 .sorted()
194 .distinct()
195 .collect(Collectors.joining("\n - ", " - ", "")));
196 }
197
198 @SuppressWarnings("unchecked")
199 protected <Q> Supplier<Q> compile(Binding<Q> binding) {
200 Supplier<Q> compiled = binding.compile(this::getCompiledBinding);
201 if (binding.getScope() != null) {
202 Scope scope = scopes.entrySet().stream()
203 .filter(e -> e.getKey().isInstance(binding.getScope()))
204 .map(Map.Entry::getValue)
205 .findFirst()
206 .orElseThrow(() -> new DIException("Scope not bound for annotation "
207 + binding.getScope().getClass()));
208 compiled = scope.scope((Key<Q>) binding.getOriginalKey(), binding.getScope(), compiled);
209 }
210 return compiled;
211 }
212
213 protected void doBindImplicit(Key<?> key, Binding<?> binding) {
214 if (binding != null) {
215
216 Object qualifier = key.getQualifier();
217 Class<?> type = key.getRawType();
218 Set<Class<?>> types = getBoundTypes(type.getAnnotation(Typed.class), type);
219 for (Type t : Types.getAllSuperTypes(type)) {
220 if (types == null || types.contains(Types.getRawType(t))) {
221 bind(Key.ofType(t, qualifier), binding);
222 if (qualifier != null) {
223 bind(Key.ofType(t), binding);
224 }
225 }
226 }
227 }
228
229 for (Class<?> inner : key.getRawType().getDeclaredClasses()) {
230 boolean hasQualifier = Stream.of(inner.getAnnotations())
231 .anyMatch(ann -> ann.annotationType().isAnnotationPresent(Qualifier.class));
232 if (hasQualifier) {
233 bindImplicit(inner);
234 }
235 }
236
237 for (Method method : key.getRawType().getDeclaredMethods()) {
238 if (method.isAnnotationPresent(Provides.class)) {
239 if (method.getTypeParameters().length != 0) {
240 throw new DIException("Parameterized method are not supported " + method);
241 }
242 Object qualifier = ReflectionUtils.qualifierOf(method);
243 Annotation scope = ReflectionUtils.scopeOf(method);
244 Type returnType = method.getGenericReturnType();
245 Set<Class<?>> types = getBoundTypes(method.getAnnotation(Typed.class), Types.getRawType(returnType));
246 Binding<Object> bind = ReflectionUtils.bindingFromMethod(method).scope(scope);
247 for (Type t : Types.getAllSuperTypes(returnType)) {
248 if (types == null || types.contains(Types.getRawType(t))) {
249 bind(Key.ofType(t, qualifier), bind);
250 if (qualifier != null) {
251 bind(Key.ofType(t), bind);
252 }
253 }
254 }
255 }
256 }
257 }
258
259 private static Set<Class<?>> getBoundTypes(Typed typed, Class<?> clazz) {
260 if (typed != null) {
261 Class<?>[] typesArray = typed.value();
262 if (typesArray == null || typesArray.length == 0) {
263 Set<Class<?>> types = new HashSet<>(Arrays.asList(clazz.getInterfaces()));
264 types.add(Object.class);
265 return types;
266 } else {
267 return new HashSet<>(Arrays.asList(typesArray));
268 }
269 } else {
270 return null;
271 }
272 }
273
274 protected <K, V> Map<K, V> map(Map<K, Supplier<V>> map) {
275 return new WrappingMap<>(map, Supplier::get);
276 }
277
278 private static class WrappingMap<K, V, T> extends AbstractMap<K, V> {
279
280 private final Map<K, T> delegate;
281 private final Function<T, V> mapper;
282
283 WrappingMap(Map<K, T> delegate, Function<T, V> mapper) {
284 this.delegate = delegate;
285 this.mapper = mapper;
286 }
287
288 @SuppressWarnings("NullableProblems")
289 @Override
290 public Set<Entry<K, V>> entrySet() {
291 return new AbstractSet<Entry<K, V>>() {
292 @Override
293 public Iterator<Entry<K, V>> iterator() {
294 Iterator<Entry<K, T>> it = delegate.entrySet().iterator();
295 return new Iterator<Entry<K, V>>() {
296 @Override
297 public boolean hasNext() {
298 return it.hasNext();
299 }
300
301 @Override
302 public Entry<K, V> next() {
303 Entry<K, T> n = it.next();
304 return new SimpleImmutableEntry<>(n.getKey(), mapper.apply(n.getValue()));
305 }
306 };
307 }
308
309 @Override
310 public int size() {
311 return delegate.size();
312 }
313 };
314 }
315 }
316
317 protected <T> List<T> list(List<Supplier<T>> bindingList) {
318 return new WrappingList<>(bindingList, Supplier::get);
319 }
320
321 private static class WrappingList<Q, T> extends AbstractList<Q> {
322
323 private final List<T> delegate;
324 private final Function<T, Q> mapper;
325
326 WrappingList(List<T> delegate, Function<T, Q> mapper) {
327 this.delegate = delegate;
328 this.mapper = mapper;
329 }
330
331 @Override
332 public Q get(int index) {
333 return mapper.apply(delegate.get(index));
334 }
335
336 @Override
337 public int size() {
338 return delegate.size();
339 }
340 }
341
342 private static class SingletonScope implements Scope {
343 Map<Key<?>, java.util.function.Supplier<?>> cache = new ConcurrentHashMap<>();
344
345 @SuppressWarnings("unchecked")
346 @Override
347 public <T> java.util.function.Supplier<T> scope(
348 Key<T> key, Annotation scope, java.util.function.Supplier<T> unscoped) {
349 return (java.util.function.Supplier<T>)
350 cache.computeIfAbsent(key, k -> new java.util.function.Supplier<T>() {
351 volatile T instance;
352
353 @Override
354 public T get() {
355 if (instance == null) {
356 synchronized (this) {
357 if (instance == null) {
358 instance = unscoped.get();
359 }
360 }
361 }
362 return instance;
363 }
364 });
365 }
366 }
367 }