1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
81
82 public static Session createSession() {
83 return createSession(null);
84 }
85
86
87
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
325 System.getenv().forEach((k, v) -> properties.put("env." + k, v));
326
327 System.getProperties().forEach((k, v) -> properties.put(k.toString(), v.toString()));
328
329
330
331 properties.put("user.home", "target");
332
333
334
335
336
337
338
339
340
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
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
380
381
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
405
406
407
408
409
410
411
412 }
413
414 static class UnsupportedInStandaloneModeException extends MavenException {}
415
416 record DumbPackaging(String id, Type type, Map<String, PluginContainer> plugins) implements Packaging {}
417 }