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.standalone;
20  
21  import java.nio.file.Path;
22  import java.nio.file.Paths;
23  import java.time.Instant;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Objects;
30  import java.util.Optional;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.function.Consumer;
33  import java.util.stream.Collectors;
34  
35  import org.apache.maven.api.Artifact;
36  import org.apache.maven.api.Lifecycle;
37  import org.apache.maven.api.MonotonicClock;
38  import org.apache.maven.api.Packaging;
39  import org.apache.maven.api.ProducedArtifact;
40  import org.apache.maven.api.Project;
41  import org.apache.maven.api.RemoteRepository;
42  import org.apache.maven.api.Session;
43  import org.apache.maven.api.Type;
44  import org.apache.maven.api.Version;
45  import org.apache.maven.api.di.Provides;
46  import org.apache.maven.api.di.SessionScoped;
47  import org.apache.maven.api.model.PluginContainer;
48  import org.apache.maven.api.model.Profile;
49  import org.apache.maven.api.services.ArtifactManager;
50  import org.apache.maven.api.services.LifecycleRegistry;
51  import org.apache.maven.api.services.Lookup;
52  import org.apache.maven.api.services.MavenException;
53  import org.apache.maven.api.services.PackagingRegistry;
54  import org.apache.maven.api.services.RepositoryFactory;
55  import org.apache.maven.api.services.SettingsBuilder;
56  import org.apache.maven.api.services.TypeRegistry;
57  import org.apache.maven.api.settings.Settings;
58  import org.apache.maven.api.spi.TypeProvider;
59  import org.apache.maven.di.Injector;
60  import org.apache.maven.di.Key;
61  import org.apache.maven.di.impl.DIException;
62  import org.apache.maven.internal.impl.AbstractSession;
63  import org.apache.maven.internal.impl.InternalSession;
64  import org.apache.maven.internal.impl.di.SessionScope;
65  import org.apache.maven.internal.impl.resolver.scopes.Maven4ScopeManagerConfiguration;
66  import org.eclipse.aether.DefaultRepositorySystemSession;
67  import org.eclipse.aether.RepositorySystem;
68  import org.eclipse.aether.RepositorySystemSession;
69  import org.eclipse.aether.internal.impl.scope.ScopeManagerImpl;
70  import org.eclipse.aether.repository.LocalRepository;
71  import org.eclipse.aether.repository.LocalRepositoryManager;
72  
73  public class ApiRunner {
74  
75      /**
76       * Create a new session.
77       */
78      public static Session createSession() {
79          return createSession(null);
80      }
81  
82      /**
83       * Create a new session.
84       */
85      public static Session createSession(Consumer<Injector> injectorConsumer) {
86          Injector injector = Injector.create();
87          injector.bindInstance(Injector.class, injector);
88          injector.bindImplicit(ApiRunner.class);
89          injector.bindImplicit(RepositorySystemSupplier.class);
90          injector.discover(ApiRunner.class.getClassLoader());
91          if (injectorConsumer != null) {
92              injectorConsumer.accept(injector);
93          }
94          Session session = injector.getInstance(Session.class);
95          SessionScope scope = new SessionScope();
96          scope.enter();
97          scope.seed(Session.class, session);
98          injector.bindScope(SessionScoped.class, scope);
99          return session;
100     }
101 
102     static class DefaultSession extends AbstractSession {
103 
104         private final Map<String, String> systemProperties;
105         private final Instant startTime = MonotonicClock.now();
106 
107         DefaultSession(RepositorySystemSession session, RepositorySystem repositorySystem, Lookup lookup) {
108             this(session, repositorySystem, Collections.emptyList(), null, lookup);
109         }
110 
111         protected DefaultSession(
112                 RepositorySystemSession session,
113                 RepositorySystem repositorySystem,
114                 List<RemoteRepository> repositories,
115                 List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
116                 Lookup lookup) {
117             super(session, repositorySystem, repositories, resolverRepositories, lookup);
118             systemProperties = System.getenv().entrySet().stream()
119                     .collect(Collectors.toMap(e -> "env." + e.getKey(), Map.Entry::getValue));
120             System.getProperties().forEach((k, v) -> systemProperties.put(k.toString(), v.toString()));
121         }
122 
123         @Override
124         protected Session newSession(RepositorySystemSession session, List<RemoteRepository> repositories) {
125             return new DefaultSession(session, repositorySystem, repositories, null, lookup);
126         }
127 
128         @Override
129         public Settings getSettings() {
130             return Settings.newInstance();
131         }
132 
133         @Override
134         public Map<String, String> getUserProperties() {
135             return Map.of();
136         }
137 
138         @Override
139         public Map<String, String> getSystemProperties() {
140             return systemProperties;
141         }
142 
143         @Override
144         public Map<String, String> getEffectiveProperties(Project project) {
145             HashMap<String, String> result = new HashMap<>(getSystemProperties());
146             if (project != null) {
147                 result.putAll(project.getModel().getProperties());
148             }
149             result.putAll(getUserProperties());
150             return result;
151         }
152 
153         @Override
154         public Version getMavenVersion() {
155             return null;
156         }
157 
158         @Override
159         public int getDegreeOfConcurrency() {
160             return 0;
161         }
162 
163         @Override
164         public Instant getStartTime() {
165             return startTime;
166         }
167 
168         @Override
169         public Path getTopDirectory() {
170             return null;
171         }
172 
173         @Override
174         public Path getRootDirectory() {
175             throw new IllegalStateException();
176         }
177 
178         @Override
179         public List<Project> getProjects() {
180             return List.of();
181         }
182 
183         @Override
184         public Map<String, Object> getPluginContext(Project project) {
185             throw new UnsupportedInStandaloneModeException();
186         }
187     }
188 
189     @Provides
190     @SuppressWarnings("unused")
191     static Lookup newLookup(Injector injector) {
192         return new Lookup() {
193             @Override
194             public <T> T lookup(Class<T> type) {
195                 try {
196                     return injector.getInstance(type);
197                 } catch (DIException e) {
198                     throw new MavenException("Unable to locate instance of type " + type, e);
199                 }
200             }
201 
202             @Override
203             public <T> T lookup(Class<T> type, String name) {
204                 try {
205                     return injector.getInstance(Key.of(type, name));
206                 } catch (DIException e) {
207                     throw new MavenException("Unable to locate instance of type " + type, e);
208                 }
209             }
210 
211             @Override
212             public <T> Optional<T> lookupOptional(Class<T> type) {
213                 try {
214                     return Optional.of(injector.getInstance(type));
215                 } catch (DIException e) {
216                     return Optional.empty();
217                 }
218             }
219 
220             @Override
221             public <T> Optional<T> lookupOptional(Class<T> type, String name) {
222                 try {
223                     return Optional.of(injector.getInstance(Key.of(type, name)));
224                 } catch (DIException e) {
225                     return Optional.empty();
226                 }
227             }
228 
229             @Override
230             public <T> List<T> lookupList(Class<T> type) {
231                 return injector.getInstance(new Key<List<T>>() {});
232             }
233 
234             @Override
235             public <T> Map<String, T> lookupMap(Class<T> type) {
236                 return injector.getInstance(new Key<Map<String, T>>() {});
237             }
238         };
239     }
240 
241     @Provides
242     @SuppressWarnings("unused")
243     static ArtifactManager newArtifactManager() {
244         return new ArtifactManager() {
245             private final Map<Artifact, Path> paths = new ConcurrentHashMap<>();
246 
247             @Override
248             public Optional<Path> getPath(Artifact artifact) {
249                 return Optional.ofNullable(paths.get(artifact));
250             }
251 
252             @Override
253             public void setPath(ProducedArtifact artifact, Path path) {
254                 paths.put(artifact, path);
255             }
256         };
257     }
258 
259     @Provides
260     @SuppressWarnings("unused")
261     static PackagingRegistry newPackagingRegistry(TypeRegistry typeRegistry) {
262         return id -> Optional.of(new DumbPackaging(id, typeRegistry.require(id), Map.of()));
263     }
264 
265     @Provides
266     @SuppressWarnings("unused")
267     static TypeRegistry newTypeRegistry(List<TypeProvider> providers) {
268         return new TypeRegistry() {
269             @Override
270             public Optional<Type> lookup(String id) {
271                 return providers.stream()
272                         .flatMap(p -> p.provides().stream())
273                         .filter(t -> Objects.equals(id, t.id()))
274                         .findAny();
275             }
276         };
277     }
278 
279     @Provides
280     @SuppressWarnings("unused")
281     static LifecycleRegistry newLifecycleRegistry() {
282         return new LifecycleRegistry() {
283 
284             @Override
285             public Iterator<Lifecycle> iterator() {
286                 return Collections.emptyIterator();
287             }
288 
289             @Override
290             public Optional<Lifecycle> lookup(String id) {
291                 return Optional.empty();
292             }
293 
294             @Override
295             public List<String> computePhases(Lifecycle lifecycle) {
296                 return List.of();
297             }
298         };
299     }
300 
301     @Provides
302     @SuppressWarnings("unused")
303     static Session newSession(RepositorySystem system, Lookup lookup) {
304         Map<String, String> properties = new HashMap<>();
305         // Env variables prefixed with "env."
306         System.getenv().forEach((k, v) -> properties.put("env." + k, v));
307         // Java System properties
308         System.getProperties().forEach((k, v) -> properties.put(k.toString(), v.toString()));
309 
310         // Do not allow user settings to interfere with our unit tests
311         // TODO: remove that when this go more public
312         properties.put("user.home", "target");
313 
314         // SettingsDecrypter settingsDecrypter =
315         // (SettingsDecrypter)Objects.requireNonNull(this.createSettingsDecrypter(preBoot));
316         //        new DefaultProfileSelector(List.of(
317         //                new JdkVersionProfileActivator(),
318         //                new PropertyProfileActivator(),
319         //                new OperatingSystemProfileActivator(),
320         //                new FileProfileActivator(new ProfileActivationFilePathInterpolator(
321         //                        new DefaultPathTranslator(), new DefaultRootLocator()))));
322 
323         Path userHome = Paths.get(properties.get("user.home"));
324         Path mavenUserHome = userHome.resolve(".m2");
325         Path mavenSystemHome = properties.containsKey("maven.home")
326                 ? Paths.get(properties.get("maven.home"))
327                 : properties.containsKey("env.MAVEN_HOME") ? Paths.get(properties.get("env.MAVEN_HOME")) : null;
328 
329         DefaultRepositorySystemSession rsession = new DefaultRepositorySystemSession(h -> false);
330         rsession.setScopeManager(new ScopeManagerImpl(Maven4ScopeManagerConfiguration.INSTANCE));
331         rsession.setSystemProperties(properties);
332         rsession.setConfigProperties(properties);
333 
334         DefaultSession session = new DefaultSession(
335                 rsession,
336                 system,
337                 List.of(lookup.lookup(RepositoryFactory.class)
338                         .createRemote("central", "https://repo.maven.apache.org/maven2")),
339                 null,
340                 lookup);
341 
342         Settings settings = session.getService(SettingsBuilder.class)
343                 .build(
344                         session,
345                         mavenSystemHome != null ? mavenSystemHome.resolve("settings.xml") : null,
346                         mavenUserHome.resolve("settings.xml"))
347                 .getEffectiveSettings();
348 
349         settings.getProfiles();
350 
351         // local repository
352         String localRepository = settings.getLocalRepository() != null
353                         && !settings.getLocalRepository().isEmpty()
354                 ? settings.getLocalRepository()
355                 : mavenUserHome.resolve("repository").toString();
356         LocalRepositoryManager llm = system.newLocalRepositoryManager(rsession, new LocalRepository(localRepository));
357         rsession.setLocalRepositoryManager(llm);
358         // active proxies
359         // TODO
360         // active profiles
361 
362         DefaultSession defaultSession = new DefaultSession(
363                 rsession,
364                 system,
365                 List.of(lookup.lookup(RepositoryFactory.class)
366                         .createRemote("central", "https://repo.maven.apache.org/maven2")),
367                 null,
368                 lookup);
369 
370         Profile profile = session.getService(SettingsBuilder.class)
371                 .convert(org.apache.maven.api.settings.Profile.newBuilder()
372                         .repositories(settings.getRepositories())
373                         .pluginRepositories(settings.getPluginRepositories())
374                         .build());
375         RepositoryFactory repositoryFactory = session.getService(RepositoryFactory.class);
376         List<RemoteRepository> repositories = profile.getRepositories().stream()
377                 .map(repositoryFactory::createRemote)
378                 .toList();
379         InternalSession s = (InternalSession) session.withRemoteRepositories(repositories);
380         InternalSession.associate(rsession, s);
381         return s;
382 
383         // List<RemoteRepository> repositories = repositoryFactory.createRemote();
384 
385         //        session.getService(SettingsBuilder.class).convert()
386 
387         //        settings.getDelegate().getRepositories().stream()
388         //                        .map(r -> SettingsUtilsV4.)
389         //        defaultSession.getService(RepositoryFactory.class).createRemote()
390         //        return defaultSession;
391     }
392 
393     static class UnsupportedInStandaloneModeException extends MavenException {}
394 
395     record DumbPackaging(String id, Type type, Map<String, PluginContainer> plugins) implements Packaging {}
396 }