1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.Date;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37
38 import org.apache.maven.artifact.ArtifactUtils;
39 import org.apache.maven.execution.DefaultMavenExecutionResult;
40 import org.apache.maven.execution.ExecutionEvent;
41 import org.apache.maven.execution.MavenExecutionRequest;
42 import org.apache.maven.execution.MavenExecutionResult;
43 import org.apache.maven.execution.MavenSession;
44 import org.apache.maven.execution.ProjectDependencyGraph;
45 import org.apache.maven.graph.GraphBuilder;
46 import org.apache.maven.internal.RepositorySystemSessionFactory;
47 import org.apache.maven.internal.aether.MavenChainedWorkspaceReader;
48 import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
49 import org.apache.maven.lifecycle.internal.LifecycleStarter;
50 import org.apache.maven.model.Prerequisites;
51 import org.apache.maven.model.building.ModelProblem;
52 import org.apache.maven.model.building.Result;
53 import org.apache.maven.plugin.LegacySupport;
54 import org.apache.maven.project.MavenProject;
55 import org.apache.maven.project.ProjectBuilder;
56 import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
57 import org.apache.maven.session.scope.internal.SessionScope;
58 import org.codehaus.plexus.PlexusContainer;
59 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
60 import org.eclipse.aether.DefaultRepositorySystemSession;
61 import org.eclipse.aether.RepositorySystemSession;
62 import org.eclipse.aether.repository.WorkspaceReader;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66
67
68
69 @Singleton
70 @Named
71 public class DefaultMaven implements Maven {
72
73 private final Logger logger = LoggerFactory.getLogger(getClass());
74
75 @Inject
76 protected ProjectBuilder projectBuilder;
77
78 @Inject
79 private LifecycleStarter lifecycleStarter;
80
81 @Inject
82 protected PlexusContainer container;
83
84 @Inject
85 private ExecutionEventCatapult eventCatapult;
86
87 @Inject
88 private LegacySupport legacySupport;
89
90 @Inject
91 private SessionScope sessionScope;
92
93 @Inject
94 private RepositorySystemSessionFactory repositorySessionFactory;
95
96 @Inject
97 @Named(GraphBuilder.HINT)
98 private GraphBuilder graphBuilder;
99
100 @Override
101 public MavenExecutionResult execute(MavenExecutionRequest request) {
102 MavenExecutionResult result;
103
104 try {
105 result = doExecute(request);
106 } catch (OutOfMemoryError e) {
107 result = addExceptionToResult(new DefaultMavenExecutionResult(), e);
108 } catch (RuntimeException e) {
109
110 if (e.getCause() instanceof ProjectCycleException) {
111 result = addExceptionToResult(new DefaultMavenExecutionResult(), e.getCause());
112 } else {
113 result = addExceptionToResult(
114 new DefaultMavenExecutionResult(), new InternalErrorException("Internal error: " + e, e));
115 }
116 } finally {
117 legacySupport.setSession(null);
118 }
119
120 return result;
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 @SuppressWarnings("checkstyle:methodlength")
152 private MavenExecutionResult doExecute(MavenExecutionRequest request) {
153 request.setStartTime(new Date());
154
155 MavenExecutionResult result = new DefaultMavenExecutionResult();
156
157 try {
158 validateLocalRepository(request);
159 } catch (LocalRepositoryNotAccessibleException e) {
160 return addExceptionToResult(result, e);
161 }
162
163
164
165
166
167
168 sessionScope.enter();
169 try (RepositorySystemSession.CloseableSession repoSession =
170 newRepositorySessionBuilder(request).build()) {
171 DefaultRepositorySystemSession mutableSession = new DefaultRepositorySystemSession(repoSession);
172 MavenSession session = new MavenSession(container, mutableSession, request, result);
173
174 sessionScope.seed(RepositorySystemSession.class, mutableSession);
175 sessionScope.seed(MavenSession.class, session);
176
177 legacySupport.setSession(session);
178
179 return doExecute(request, session, result, mutableSession);
180 } finally {
181 sessionScope.exit();
182 }
183 }
184
185 private MavenExecutionResult doExecute(
186 MavenExecutionRequest request,
187 MavenSession session,
188 MavenExecutionResult result,
189 DefaultRepositorySystemSession repoSession) {
190 try {
191
192 for (AbstractMavenLifecycleParticipant listener :
193 getLifecycleParticipants(Collections.<MavenProject>emptyList())) {
194 listener.afterSessionStart(session);
195 }
196
197 } catch (MavenExecutionException e) {
198 return addExceptionToResult(result, e);
199 }
200
201 eventCatapult.fire(ExecutionEvent.Type.ProjectDiscoveryStarted, session, null);
202
203 Result<? extends ProjectDependencyGraph> graphResult = buildGraph(session, result);
204
205 if (graphResult.hasErrors()) {
206 return addExceptionToResult(
207 result, graphResult.getProblems().iterator().next().getException());
208 }
209
210 try {
211 session.setProjectMap(getProjectMap(session.getProjects()));
212 } catch (DuplicateProjectException e) {
213 return addExceptionToResult(result, e);
214 }
215
216 try {
217 setupWorkspaceReader(session, repoSession);
218 } catch (ComponentLookupException e) {
219 return addExceptionToResult(result, e);
220 }
221
222 repoSession.setReadOnly();
223
224 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
225 try {
226 for (AbstractMavenLifecycleParticipant listener : getLifecycleParticipants(session.getProjects())) {
227 Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader());
228
229 listener.afterProjectsRead(session);
230 }
231 } catch (MavenExecutionException e) {
232 return addExceptionToResult(result, e);
233 } finally {
234 Thread.currentThread().setContextClassLoader(originalClassLoader);
235 }
236
237
238
239
240
241
242
243
244
245
246 graphResult = buildGraph(session, result);
247
248 if (graphResult.hasErrors()) {
249 return addExceptionToResult(
250 result, graphResult.getProblems().iterator().next().getException());
251 }
252
253 try {
254 if (result.hasExceptions()) {
255 return result;
256 }
257
258 result.setTopologicallySortedProjects(session.getProjects());
259
260 result.setProject(session.getTopLevelProject());
261
262 validatePrerequisitesForNonMavenPluginProjects(session.getProjects());
263
264 validateActivatedProfiles(
265 session.getProjects(), request.getActiveProfiles(), request.getInactiveProfiles());
266
267 lifecycleStarter.execute(session);
268
269 validateActivatedProfiles(
270 session.getProjects(), request.getActiveProfiles(), request.getInactiveProfiles());
271
272 if (session.getResult().hasExceptions()) {
273 return addExceptionToResult(
274 result, session.getResult().getExceptions().get(0));
275 }
276 } finally {
277 try {
278 afterSessionEnd(session.getProjects(), session);
279 } catch (MavenExecutionException e) {
280 return addExceptionToResult(result, e);
281 }
282 }
283
284 return result;
285 }
286
287 private void setupWorkspaceReader(MavenSession session, DefaultRepositorySystemSession repoSession)
288 throws ComponentLookupException {
289
290 Set<WorkspaceReader> workspaceReaders = new LinkedHashSet<>();
291
292 workspaceReaders.add(container.lookup(WorkspaceReader.class, ReactorReader.HINT));
293
294 WorkspaceReader repoWorkspaceReader = repoSession.getWorkspaceReader();
295 if (repoWorkspaceReader != null) {
296 workspaceReaders.add(repoWorkspaceReader);
297 }
298
299 workspaceReaders.addAll(getProjectScopedExtensionComponents(session.getProjects(), WorkspaceReader.class));
300 repoSession.setWorkspaceReader(MavenChainedWorkspaceReader.of(workspaceReaders));
301 }
302
303 private void afterSessionEnd(Collection<MavenProject> projects, MavenSession session)
304 throws MavenExecutionException {
305 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
306 try {
307 for (AbstractMavenLifecycleParticipant listener : getLifecycleParticipants(projects)) {
308 Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader());
309
310 listener.afterSessionEnd(session);
311 }
312 } finally {
313 Thread.currentThread().setContextClassLoader(originalClassLoader);
314 }
315 }
316
317 @Deprecated
318 public RepositorySystemSession newRepositorySession(MavenExecutionRequest request) {
319 return newRepositorySessionBuilder(request).build();
320 }
321
322 private RepositorySystemSession.SessionBuilder newRepositorySessionBuilder(MavenExecutionRequest request) {
323 return repositorySessionFactory.newRepositorySessionBuilder(request);
324 }
325
326 private void validateLocalRepository(MavenExecutionRequest request) throws LocalRepositoryNotAccessibleException {
327 File localRepoDir = request.getLocalRepositoryPath();
328
329 logger.debug("Using local repository at " + localRepoDir);
330
331 localRepoDir.mkdirs();
332
333 if (!localRepoDir.isDirectory()) {
334 throw new LocalRepositoryNotAccessibleException("Could not create local repository at " + localRepoDir);
335 }
336 }
337
338 private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants(Collection<MavenProject> projects) {
339 Collection<AbstractMavenLifecycleParticipant> lifecycleListeners = new LinkedHashSet<>();
340
341 try {
342 lifecycleListeners.addAll(container.lookupList(AbstractMavenLifecycleParticipant.class));
343 } catch (ComponentLookupException e) {
344
345 logger.warn("Failed to lookup lifecycle participants: " + e.getMessage());
346 }
347
348 lifecycleListeners.addAll(
349 getProjectScopedExtensionComponents(projects, AbstractMavenLifecycleParticipant.class));
350
351 return lifecycleListeners;
352 }
353
354 protected <T> Collection<T> getProjectScopedExtensionComponents(Collection<MavenProject> projects, Class<T> role) {
355
356 Collection<T> foundComponents = new LinkedHashSet<>();
357 Collection<ClassLoader> scannedRealms = new HashSet<>();
358
359 Thread currentThread = Thread.currentThread();
360 ClassLoader originalContextClassLoader = currentThread.getContextClassLoader();
361 try {
362 for (MavenProject project : projects) {
363 ClassLoader projectRealm = project.getClassRealm();
364
365 if (projectRealm != null && scannedRealms.add(projectRealm)) {
366 currentThread.setContextClassLoader(projectRealm);
367
368 try {
369 foundComponents.addAll(container.lookupList(role));
370 } catch (ComponentLookupException e) {
371
372 logger.warn("Failed to lookup " + role + ": " + e.getMessage());
373 }
374 }
375 }
376 return foundComponents;
377 } finally {
378 currentThread.setContextClassLoader(originalContextClassLoader);
379 }
380 }
381
382 private MavenExecutionResult addExceptionToResult(MavenExecutionResult result, Throwable e) {
383 if (!result.getExceptions().contains(e)) {
384 result.addException(e);
385 }
386
387 return result;
388 }
389
390 private void validatePrerequisitesForNonMavenPluginProjects(List<MavenProject> projects) {
391 for (MavenProject mavenProject : projects) {
392 if (!"maven-plugin".equals(mavenProject.getPackaging())) {
393 Prerequisites prerequisites = mavenProject.getPrerequisites();
394 if (prerequisites != null && prerequisites.getMaven() != null) {
395 logger.warn("The project " + mavenProject.getId() + " uses prerequisites"
396 + " which is only intended for maven-plugin projects "
397 + "but not for non maven-plugin projects. "
398 + "For such purposes you should use the maven-enforcer-plugin. "
399 + "See https://maven.apache.org/enforcer/enforcer-rules/requireMavenVersion.html");
400 }
401 }
402 }
403 }
404
405 private void validateActivatedProfiles(
406 List<MavenProject> projects, List<String> activeProfileIds, List<String> inactiveProfileIds) {
407 Collection<String> notActivatedProfileIds = new LinkedHashSet<>(activeProfileIds);
408
409 for (MavenProject project : projects) {
410 for (List<String> profileIds : project.getInjectedProfileIds().values()) {
411 notActivatedProfileIds.removeAll(profileIds);
412 }
413 }
414
415 notActivatedProfileIds.removeAll(inactiveProfileIds);
416
417 for (String notActivatedProfileId : notActivatedProfileIds) {
418 logger.warn("The requested profile \"" + notActivatedProfileId
419 + "\" could not be activated because it does not exist.");
420 }
421 }
422
423 private Map<String, MavenProject> getProjectMap(Collection<MavenProject> projects)
424 throws DuplicateProjectException {
425 Map<String, MavenProject> index = new LinkedHashMap<>();
426 Map<String, List<File>> collisions = new LinkedHashMap<>();
427
428 for (MavenProject project : projects) {
429 String projectId = ArtifactUtils.key(project.getGroupId(), project.getArtifactId(), project.getVersion());
430
431 MavenProject collision = index.get(projectId);
432
433 if (collision == null) {
434 index.put(projectId, project);
435 } else {
436 List<File> pomFiles = collisions.get(projectId);
437
438 if (pomFiles == null) {
439 pomFiles = new ArrayList<>(Arrays.asList(collision.getFile(), project.getFile()));
440 collisions.put(projectId, pomFiles);
441 } else {
442 pomFiles.add(project.getFile());
443 }
444 }
445 }
446
447 if (!collisions.isEmpty()) {
448 throw new DuplicateProjectException(
449 "Two or more projects in the reactor"
450 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
451 + " is unique for each project: " + collisions,
452 collisions);
453 }
454
455 return index;
456 }
457
458 private Result<? extends ProjectDependencyGraph> buildGraph(MavenSession session, MavenExecutionResult result) {
459 Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build(session);
460 for (ModelProblem problem : graphResult.getProblems()) {
461 if (problem.getSeverity() == ModelProblem.Severity.WARNING) {
462 logger.warn(problem.toString());
463 } else {
464 logger.error(problem.toString());
465 }
466 }
467
468 if (!graphResult.hasErrors()) {
469 ProjectDependencyGraph projectDependencyGraph = graphResult.get();
470 session.setProjects(projectDependencyGraph.getSortedProjects());
471 session.setAllProjects(projectDependencyGraph.getAllProjects());
472 session.setProjectDependencyGraph(projectDependencyGraph);
473 }
474
475 return graphResult;
476 }
477
478 @Deprecated
479
480 protected Logger getLogger() {
481 return logger;
482 }
483 }