View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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                 //noinspection unchecked
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                 //noinspection unchecked
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             // For non-explicit bindings, also bind all their base classes and interfaces according to the @Type
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         // Bind inner classes
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         // Bind inner providers
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 }