1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
77
78 public static Session createSession() {
79 return createSession(null);
80 }
81
82
83
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
306 System.getenv().forEach((k, v) -> properties.put("env." + k, v));
307
308 System.getProperties().forEach((k, v) -> properties.put(k.toString(), v.toString()));
309
310
311
312 properties.put("user.home", "target");
313
314
315
316
317
318
319
320
321
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
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
359
360
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
384
385
386
387
388
389
390
391 }
392
393 static class UnsupportedInStandaloneModeException extends MavenException {}
394
395 record DumbPackaging(String id, Type type, Map<String, PluginContainer> plugins) implements Packaging {}
396 }