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