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