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