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