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.internal.impl;
20  
21  import javax.inject.Named;
22  import javax.inject.Provider;
23  
24  import java.io.BufferedReader;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.InputStreamReader;
28  import java.lang.annotation.Annotation;
29  import java.net.URL;
30  import java.nio.charset.StandardCharsets;
31  import java.util.ArrayList;
32  import java.util.Comparator;
33  import java.util.HashSet;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.function.Supplier;
39  import java.util.stream.Collectors;
40  
41  import com.google.inject.AbstractModule;
42  import com.google.inject.binder.AnnotatedBindingBuilder;
43  import com.google.inject.name.Names;
44  import org.apache.maven.api.services.MavenException;
45  import org.apache.maven.di.Injector;
46  import org.apache.maven.di.Key;
47  import org.apache.maven.di.impl.Binding;
48  import org.apache.maven.di.impl.DIException;
49  import org.apache.maven.di.impl.InjectorImpl;
50  import org.codehaus.plexus.PlexusContainer;
51  
52  @Named
53  class SisuDiBridgeModule extends AbstractModule {
54  
55      @Override
56      protected void configure() {
57          Provider<PlexusContainer> containerProvider = getProvider(PlexusContainer.class);
58  
59          InjectorImpl injector = new InjectorImpl() {
60              @Override
61              public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
62                  Set<Binding<Q>> res = getBindings(key);
63                  if (res != null && !res.isEmpty()) {
64                      List<Binding<Q>> bindingList = new ArrayList<>(res);
65                      Comparator<Binding<Q>> comparing = Comparator.comparing(Binding::getPriority);
66                      bindingList.sort(comparing.reversed());
67                      Binding<Q> binding = bindingList.get(0);
68                      return compile(binding);
69                  }
70                  if (key.getRawType() == List.class) {
71                      Set<Binding<Object>> res2 = getBindings(key.getTypeParameter(0));
72                      Set<Binding<Object>> res3 = res2 != null ? new HashSet<>(res2) : new HashSet<>();
73                      try {
74                          List<Object> l = containerProvider
75                                  .get()
76                                  .lookupList(key.getTypeParameter(0).getRawType());
77                          l.forEach(o -> res3.add(new Binding.BindingToInstance<>(o)));
78                      } catch (Throwable e) {
79                          // ignore
80                          e.printStackTrace();
81                      }
82                      List<Supplier<Object>> list =
83                              res3.stream().map(this::compile).collect(Collectors.toList());
84                      //noinspection unchecked
85                      return () -> (Q) list(list);
86                  }
87                  if (key.getRawType() == Map.class) {
88                      Key<?> k = key.getTypeParameter(0);
89                      Key<Object> v = key.getTypeParameter(1);
90                      if (k.getRawType() == String.class) {
91                          Set<Binding<Object>> res2 = getBindings(v);
92                          Set<Binding<Object>> res3 = res2 != null ? new HashSet<>(res2) : new HashSet<>();
93                          Map<String, Supplier<Object>> map = res3.stream()
94                                  .filter(b -> b.getOriginalKey() == null
95                                          || b.getOriginalKey().getQualifier() == null
96                                          || b.getOriginalKey().getQualifier() instanceof String)
97                                  .collect(Collectors.toMap(
98                                          b -> (String)
99                                                  (b.getOriginalKey() != null
100                                                         ? b.getOriginalKey().getQualifier()
101                                                         : null),
102                                         this::compile));
103                         //noinspection unchecked
104                         return () -> (Q) map(map);
105                     }
106                 }
107                 try {
108                     Q t = containerProvider.get().lookup(key.getRawType());
109                     return compile(new Binding.BindingToInstance<>(t));
110                 } catch (Throwable e) {
111                     // ignore
112                     e.printStackTrace();
113                 }
114                 throw new DIException("No binding to construct an instance for key "
115                         + key.getDisplayString() + ".  Existing bindings:\n"
116                         + getBoundKeys().stream()
117                                 .map(Key::toString)
118                                 .map(String::trim)
119                                 .sorted()
120                                 .distinct()
121                                 .collect(Collectors.joining("\n - ", " - ", "")));
122             }
123         };
124         injector.bindInstance(Injector.class, injector);
125         bind(Injector.class).toInstance(injector);
126         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
127         if (classLoader == null) {
128             classLoader = getClass().getClassLoader();
129         }
130         try {
131             for (Iterator<URL> it = classLoader
132                             .getResources("META-INF/maven/org.apache.maven.api.di.Inject")
133                             .asIterator();
134                     it.hasNext(); ) {
135                 URL url = it.next();
136                 List<String> lines;
137                 try (InputStream is = url.openStream();
138                         BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
139                     lines = reader.lines()
140                             .map(String::trim)
141                             .filter(s -> !s.isEmpty() && !s.startsWith("#"))
142                             .toList();
143                 }
144                 for (String className : lines) {
145                     try {
146                         Class<?> clazz = classLoader.loadClass(className);
147                         injector.bindImplicit(clazz);
148                     } catch (ClassNotFoundException e) {
149                         // ignore
150                         e.printStackTrace();
151                     }
152                 }
153             }
154 
155         } catch (IOException e) {
156             throw new MavenException(e);
157         }
158         injector.getBindings().keySet().stream()
159                 .filter(k -> k.getQualifier() != null)
160                 .sorted(Comparator.comparing(k -> k.getRawType().getName()))
161                 .distinct()
162                 .forEach(key -> {
163                     Class<?> clazz = key.getRawType();
164                     Class<Object> itf = (clazz.isInterface()
165                             ? null
166                             : (Class<Object>) (clazz.getInterfaces().length > 0 ? clazz.getInterfaces()[0] : clazz));
167                     if (itf != null) {
168                         AnnotatedBindingBuilder<Object> binder = bind(itf);
169                         if (key.getQualifier() instanceof String s) {
170                             binder.annotatedWith(Names.named(s));
171                         } else if (key.getQualifier() instanceof Annotation a) {
172                             binder.annotatedWith(a);
173                         }
174                         binder.toProvider(() -> injector.getInstance(clazz));
175                     }
176                 });
177     }
178 }