1 package org.apache.maven.project;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import org.apache.maven.RepositoryUtils;
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
32 import org.apache.maven.model.Build;
33 import org.apache.maven.model.Model;
34 import org.apache.maven.model.Profile;
35 import org.apache.maven.model.building.DefaultModelBuildingRequest;
36 import org.apache.maven.model.building.DefaultModelProblem;
37 import org.apache.maven.model.building.FileModelSource;
38 import org.apache.maven.model.building.ModelBuilder;
39 import org.apache.maven.model.building.ModelBuildingException;
40 import org.apache.maven.model.building.ModelBuildingRequest;
41 import org.apache.maven.model.building.ModelBuildingResult;
42 import org.apache.maven.model.building.ModelProblem;
43 import org.apache.maven.model.building.ModelProcessor;
44 import org.apache.maven.model.building.ModelSource;
45 import org.apache.maven.model.building.StringModelSource;
46 import org.apache.maven.model.resolution.ModelResolver;
47 import org.apache.maven.repository.RepositorySystem;
48 import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
49 import org.codehaus.plexus.component.annotations.Component;
50 import org.codehaus.plexus.component.annotations.Requirement;
51 import org.codehaus.plexus.logging.Logger;
52 import org.codehaus.plexus.util.Os;
53 import org.codehaus.plexus.util.StringUtils;
54 import org.sonatype.aether.RepositorySystemSession;
55 import org.sonatype.aether.RequestTrace;
56 import org.sonatype.aether.impl.RemoteRepositoryManager;
57 import org.sonatype.aether.repository.LocalRepositoryManager;
58 import org.sonatype.aether.repository.RemoteRepository;
59 import org.sonatype.aether.repository.WorkspaceRepository;
60 import org.sonatype.aether.resolution.ArtifactRequest;
61 import org.sonatype.aether.resolution.ArtifactResult;
62 import org.sonatype.aether.util.DefaultRequestTrace;
63
64
65
66 @Component( role = ProjectBuilder.class )
67 public class DefaultProjectBuilder
68 implements ProjectBuilder
69 {
70
71 @Requirement
72 private Logger logger;
73
74 @Requirement
75 private ModelBuilder modelBuilder;
76
77 @Requirement
78 private ModelProcessor modelProcessor;
79
80 @Requirement
81 private ProjectBuildingHelper projectBuildingHelper;
82
83 @Requirement
84 private RepositorySystem repositorySystem;
85
86 @Requirement
87 private org.sonatype.aether.RepositorySystem repoSystem;
88
89 @Requirement
90 private RemoteRepositoryManager repositoryManager;
91
92 @Requirement
93 private ProjectDependenciesResolver dependencyResolver;
94
95
96
97
98
99 public ProjectBuildingResult build( File pomFile, ProjectBuildingRequest request )
100 throws ProjectBuildingException
101 {
102 return build( pomFile, new FileModelSource( pomFile ), new InternalConfig( request, null, null ) );
103 }
104
105 public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request )
106 throws ProjectBuildingException
107 {
108 return build( null, modelSource, new InternalConfig( request, null, null ) );
109 }
110
111 private ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConfig config )
112 throws ProjectBuildingException
113 {
114 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
115
116 try
117 {
118 ProjectBuildingRequest configuration = config.request;
119
120 MavenProject project = configuration.getProject();
121
122 List<ModelProblem> modelProblems = null;
123 Throwable error = null;
124
125 if ( project == null )
126 {
127 ModelBuildingRequest request = getModelBuildingRequest( config );
128
129 project = new MavenProject( repositorySystem, this, configuration, logger );
130
131 DefaultModelBuildingListener listener =
132 new DefaultModelBuildingListener( project, projectBuildingHelper, configuration );
133 request.setModelBuildingListener( listener );
134
135 request.setPomFile( pomFile );
136 request.setModelSource( modelSource );
137 request.setLocationTracking( true );
138
139 ModelBuildingResult result;
140 try
141 {
142 result = modelBuilder.build( request );
143 }
144 catch ( ModelBuildingException e )
145 {
146 result = e.getResult();
147 if ( result == null || result.getEffectiveModel() == null )
148 {
149 throw new ProjectBuildingException( e.getModelId(), e.getMessage(), pomFile, e );
150 }
151
152 error = e;
153 }
154
155 modelProblems = result.getProblems();
156
157 initProject( project, Collections.<String, MavenProject> emptyMap(), result,
158 new HashMap<File, Boolean>() );
159 }
160 else if ( configuration.isResolveDependencies() )
161 {
162 projectBuildingHelper.selectProjectRealm( project );
163 }
164
165 DependencyResolutionResult resolutionResult = null;
166
167 if ( configuration.isResolveDependencies() )
168 {
169 resolutionResult = resolveDependencies( project, config.session );
170 }
171
172 ProjectBuildingResult result = new DefaultProjectBuildingResult( project, modelProblems, resolutionResult );
173
174 if ( error != null )
175 {
176 ProjectBuildingException e = new ProjectBuildingException( Arrays.asList( result ) );
177 e.initCause( error );
178 throw e;
179 }
180
181 return result;
182 }
183 finally
184 {
185 Thread.currentThread().setContextClassLoader( oldContextClassLoader );
186 }
187 }
188
189 private DependencyResolutionResult resolveDependencies( MavenProject project, RepositorySystemSession session )
190 {
191 DependencyResolutionResult resolutionResult = null;
192
193 try
194 {
195 DefaultDependencyResolutionRequest resolution = new DefaultDependencyResolutionRequest( project, session );
196 resolutionResult = dependencyResolver.resolve( resolution );
197 }
198 catch ( DependencyResolutionException e )
199 {
200 resolutionResult = e.getResult();
201 }
202
203 Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
204 if ( resolutionResult.getDependencyGraph() != null )
205 {
206 RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(),
207 Collections.singletonList( project.getArtifact().getId() ), null );
208
209
210 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
211 for ( Artifact artifact : artifacts )
212 {
213 if ( !artifact.isResolved() )
214 {
215 String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) );
216 artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) );
217 }
218 }
219 }
220 project.setResolvedArtifacts( artifacts );
221 project.setArtifacts( artifacts );
222
223 return resolutionResult;
224 }
225
226 private List<String> getProfileIds( List<Profile> profiles )
227 {
228 List<String> ids = new ArrayList<String>( profiles.size() );
229
230 for ( Profile profile : profiles )
231 {
232 ids.add( profile.getId() );
233 }
234
235 return ids;
236 }
237
238 private ModelBuildingRequest getModelBuildingRequest( InternalConfig config )
239 {
240 ProjectBuildingRequest configuration = config.request;
241
242 ModelBuildingRequest request = new DefaultModelBuildingRequest();
243
244 RequestTrace trace = DefaultRequestTrace.newChild( null, configuration ).newChild( request );
245
246 ModelResolver resolver =
247 new ProjectModelResolver( config.session, trace, repoSystem, repositoryManager, config.repositories,
248 configuration.getRepositoryMerging(), config.modelPool );
249
250 request.setValidationLevel( configuration.getValidationLevel() );
251 request.setProcessPlugins( configuration.isProcessPlugins() );
252 request.setProfiles( configuration.getProfiles() );
253 request.setActiveProfileIds( configuration.getActiveProfileIds() );
254 request.setInactiveProfileIds( configuration.getInactiveProfileIds() );
255 request.setSystemProperties( configuration.getSystemProperties() );
256 request.setUserProperties( configuration.getUserProperties() );
257 request.setBuildStartTime( configuration.getBuildStartTime() );
258 request.setModelResolver( resolver );
259 request.setModelCache( config.modelCache );
260
261 return request;
262 }
263
264 public ProjectBuildingResult build( Artifact artifact, ProjectBuildingRequest request )
265 throws ProjectBuildingException
266 {
267 return build( artifact, false, request );
268 }
269
270 public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request )
271 throws ProjectBuildingException
272 {
273 org.sonatype.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact );
274 pomArtifact = ArtifactDescriptorUtils.toPomArtifact( pomArtifact );
275
276 InternalConfig config = new InternalConfig( request, null, null );
277
278 boolean localProject;
279
280 try
281 {
282 ArtifactRequest pomRequest = new ArtifactRequest();
283 pomRequest.setArtifact( pomArtifact );
284 pomRequest.setRepositories( config.repositories );
285 ArtifactResult pomResult = repoSystem.resolveArtifact( config.session, pomRequest );
286
287 pomArtifact = pomResult.getArtifact();
288 localProject = pomResult.getRepository() instanceof WorkspaceRepository;
289 }
290 catch ( org.sonatype.aether.resolution.ArtifactResolutionException e )
291 {
292 if ( e.getResults().get( 0 ).isMissing() && allowStubModel )
293 {
294 return build( null, createStubModelSource( artifact ), config );
295 }
296 throw new ProjectBuildingException( artifact.getId(),
297 "Error resolving project artifact: " + e.getMessage(), e );
298 }
299
300 File pomFile = pomArtifact.getFile();
301
302 if ( "pom".equals( artifact.getType() ) )
303 {
304 artifact.selectVersion( pomArtifact.getVersion() );
305 artifact.setFile( pomFile );
306 artifact.setResolved( true );
307 }
308
309 return build( localProject ? pomFile : null, new FileModelSource( pomFile ), config );
310 }
311
312 private ModelSource createStubModelSource( Artifact artifact )
313 {
314 StringBuilder buffer = new StringBuilder( 1024 );
315
316 buffer.append( "<?xml version='1.0'?>" );
317 buffer.append( "<project>" );
318 buffer.append( "<modelVersion>4.0.0</modelVersion>" );
319 buffer.append( "<groupId>" ).append( artifact.getGroupId() ).append( "</groupId>" );
320 buffer.append( "<artifactId>" ).append( artifact.getArtifactId() ).append( "</artifactId>" );
321 buffer.append( "<version>" ).append( artifact.getBaseVersion() ).append( "</version>" );
322 buffer.append( "<packaging>" ).append( artifact.getType() ).append( "</packaging>" );
323 buffer.append( "</project>" );
324
325 return new StringModelSource( buffer, artifact.getId() );
326 }
327
328 public List<ProjectBuildingResult> build( List<File> pomFiles, boolean recursive, ProjectBuildingRequest request )
329 throws ProjectBuildingException
330 {
331 List<ProjectBuildingResult> results = new ArrayList<ProjectBuildingResult>();
332
333 List<InterimResult> interimResults = new ArrayList<InterimResult>();
334
335 ReactorModelPool modelPool = new ReactorModelPool();
336
337 ReactorModelCache modelCache = new ReactorModelCache();
338
339 InternalConfig config = new InternalConfig( request, modelPool, modelCache );
340
341 Map<String, MavenProject> projectIndex = new HashMap<String, MavenProject>( 256 );
342
343 boolean noErrors =
344 build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<File>(), true, recursive, config );
345
346 populateReactorModelPool( modelPool, interimResults );
347
348 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
349
350 try
351 {
352 noErrors =
353 build( results, new ArrayList<MavenProject>(), projectIndex, interimResults, request,
354 new HashMap<File, Boolean>() ) && noErrors;
355 }
356 finally
357 {
358 Thread.currentThread().setContextClassLoader( oldContextClassLoader );
359 }
360
361 if ( !noErrors )
362 {
363 throw new ProjectBuildingException( results );
364 }
365
366 return results;
367 }
368
369 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults,
370 Map<String, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles,
371 boolean isRoot, boolean recursive, InternalConfig config )
372 {
373 boolean noErrors = true;
374
375 for ( File pomFile : pomFiles )
376 {
377 aggregatorFiles.add( pomFile );
378
379 if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, isRoot, recursive, config ) )
380 {
381 noErrors = false;
382 }
383
384 aggregatorFiles.remove( pomFile );
385 }
386
387 return noErrors;
388 }
389
390 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults,
391 Map<String, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles,
392 boolean isRoot, boolean recursive, InternalConfig config )
393 {
394 boolean noErrors = true;
395
396 ModelBuildingRequest request = getModelBuildingRequest( config );
397
398 MavenProject project = new MavenProject( repositorySystem, this, config.request, logger );
399
400 request.setPomFile( pomFile );
401 request.setTwoPhaseBuilding( true );
402 request.setLocationTracking( true );
403
404 DefaultModelBuildingListener listener =
405 new DefaultModelBuildingListener( project, projectBuildingHelper, config.request );
406 request.setModelBuildingListener( listener );
407
408 try
409 {
410 ModelBuildingResult result = modelBuilder.build( request );
411
412 Model model = result.getEffectiveModel();
413
414 projectIndex.put( result.getModelIds().get( 0 ), project );
415
416 InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot );
417 interimResults.add( interimResult );
418
419 if ( recursive && !model.getModules().isEmpty() )
420 {
421 File basedir = pomFile.getParentFile();
422
423 List<File> moduleFiles = new ArrayList<File>();
424
425 for ( String module : model.getModules() )
426 {
427 if ( StringUtils.isEmpty( module ) )
428 {
429 continue;
430 }
431
432 module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar );
433
434 File moduleFile = new File( basedir, module );
435
436 if ( moduleFile.isDirectory() )
437 {
438 moduleFile = modelProcessor.locatePom( moduleFile );
439 }
440
441 if ( !moduleFile.isFile() )
442 {
443 ModelProblem problem =
444 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile
445 + " does not exist", ModelProblem.Severity.ERROR, model, -1, -1, null );
446 result.getProblems().add( problem );
447
448 noErrors = false;
449
450 continue;
451 }
452
453 if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
454 {
455
456 try
457 {
458 moduleFile = moduleFile.getCanonicalFile();
459 }
460 catch ( IOException e )
461 {
462 moduleFile = moduleFile.getAbsoluteFile();
463 }
464 }
465 else
466 {
467 moduleFile = new File( moduleFile.toURI().normalize() );
468 }
469
470 if ( aggregatorFiles.contains( moduleFile ) )
471 {
472 StringBuilder buffer = new StringBuilder( 256 );
473 for ( File aggregatorFile : aggregatorFiles )
474 {
475 buffer.append( aggregatorFile ).append( " -> " );
476 }
477 buffer.append( moduleFile );
478
479 ModelProblem problem =
480 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile
481 + " forms aggregation cycle " + buffer, ModelProblem.Severity.ERROR, model, -1, -1,
482 null );
483 result.getProblems().add( problem );
484
485 noErrors = false;
486
487 continue;
488 }
489
490 moduleFiles.add( moduleFile );
491 }
492
493 interimResult.modules = new ArrayList<InterimResult>();
494
495 if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false,
496 recursive, config ) )
497 {
498 noErrors = false;
499 }
500 }
501 }
502 catch ( ModelBuildingException e )
503 {
504 results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) );
505
506 noErrors = false;
507 }
508
509 return noErrors;
510 }
511
512 static class InterimResult
513 {
514
515 File pomFile;
516
517 ModelBuildingRequest request;
518
519 ModelBuildingResult result;
520
521 DefaultModelBuildingListener listener;
522
523 boolean root;
524
525 List<InterimResult> modules = Collections.emptyList();
526
527 InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result,
528 DefaultModelBuildingListener listener, boolean root )
529 {
530 this.pomFile = pomFile;
531 this.request = request;
532 this.result = result;
533 this.listener = listener;
534 this.root = root;
535 }
536
537 }
538
539 private void populateReactorModelPool( ReactorModelPool reactorModelPool, List<InterimResult> interimResults )
540 {
541 for ( InterimResult interimResult : interimResults )
542 {
543 Model model = interimResult.result.getEffectiveModel();
544 reactorModelPool.put( model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile() );
545
546 populateReactorModelPool( reactorModelPool, interimResult.modules );
547 }
548 }
549
550 private boolean build( List<ProjectBuildingResult> results, List<MavenProject> projects,
551 Map<String, MavenProject> projectIndex, List<InterimResult> interimResults,
552 ProjectBuildingRequest request, Map<File, Boolean> profilesXmls )
553 {
554 boolean noErrors = true;
555
556 for ( InterimResult interimResult : interimResults )
557 {
558 try
559 {
560 ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result );
561
562 MavenProject project = interimResult.listener.getProject();
563 initProject( project, projectIndex, result, profilesXmls );
564
565 List<MavenProject> modules = new ArrayList<MavenProject>();
566 noErrors =
567 build( results, modules, projectIndex, interimResult.modules, request, profilesXmls ) && noErrors;
568
569 projects.addAll( modules );
570 projects.add( project );
571
572 project.setExecutionRoot( interimResult.root );
573 project.setCollectedProjects( modules );
574
575 results.add( new DefaultProjectBuildingResult( project, result.getProblems(), null ) );
576 }
577 catch ( ModelBuildingException e )
578 {
579 results.add( new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ) );
580
581 noErrors = false;
582 }
583 }
584
585 return noErrors;
586 }
587
588 private void initProject( MavenProject project, Map<String, MavenProject> projects, ModelBuildingResult result,
589 Map<File, Boolean> profilesXmls )
590 {
591 Model model = result.getEffectiveModel();
592
593 project.setModel( model );
594 project.setOriginalModel( result.getRawModel() );
595
596 project.setFile( model.getPomFile() );
597
598 File parentPomFile = result.getRawModel( result.getModelIds().get( 1 ) ).getPomFile();
599 project.setParentFile( parentPomFile );
600
601 project.setParent( projects.get( result.getModelIds().get( 1 ) ) );
602
603 Artifact projectArtifact =
604 repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null,
605 project.getPackaging() );
606 project.setArtifact( projectArtifact );
607
608 if ( project.getFile() != null )
609 {
610 Build build = project.getBuild();
611 project.addScriptSourceRoot( build.getScriptSourceDirectory() );
612 project.addCompileSourceRoot( build.getSourceDirectory() );
613 project.addTestCompileSourceRoot( build.getTestSourceDirectory() );
614 }
615
616 List<Profile> activeProfiles = new ArrayList<Profile>();
617 activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) );
618 activeProfiles.addAll( result.getActiveExternalProfiles() );
619 project.setActiveProfiles( activeProfiles );
620
621 project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) );
622 for ( String modelId : result.getModelIds() )
623 {
624 project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) );
625 }
626
627 String modelId = findProfilesXml( result, profilesXmls );
628 if ( modelId != null )
629 {
630 ModelProblem problem =
631 new DefaultModelProblem( "Detected profiles.xml alongside " + modelId
632 + ", this file is no longer supported and was ignored" + ", please use the settings.xml instead",
633 ModelProblem.Severity.WARNING, model, -1, -1, null );
634 result.getProblems().add( problem );
635 }
636 }
637
638 private String findProfilesXml( ModelBuildingResult result, Map<File, Boolean> profilesXmls )
639 {
640 for ( String modelId : result.getModelIds() )
641 {
642 Model model = result.getRawModel( modelId );
643
644 File basedir = model.getProjectDirectory();
645 if ( basedir == null )
646 {
647 break;
648 }
649
650 Boolean profilesXml = profilesXmls.get( basedir );
651 if ( profilesXml == null )
652 {
653 profilesXml = Boolean.valueOf( new File( basedir, "profiles.xml" ).exists() );
654 profilesXmls.put( basedir, profilesXml );
655 }
656 if ( profilesXml.booleanValue() )
657 {
658 return modelId;
659 }
660 }
661
662 return null;
663 }
664
665 class InternalConfig
666 {
667
668 public final ProjectBuildingRequest request;
669
670 public final RepositorySystemSession session;
671
672 public final List<RemoteRepository> repositories;
673
674 public final ReactorModelPool modelPool;
675
676 public final ReactorModelCache modelCache;
677
678 public InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, ReactorModelCache modelCache )
679 {
680 this.request = request;
681 this.modelPool = modelPool;
682 this.modelCache = modelCache;
683 session =
684 LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(),
685 repoSystem );
686 repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() );
687 }
688
689 }
690
691 }