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