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