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 ) );
103 }
104
105 public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request )
106 throws ProjectBuildingException
107 {
108 return build( null, modelSource, new InternalConfig( request, 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( new ReactorModelCache() );
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 );
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 InternalConfig config = new InternalConfig( request, modelPool );
338
339 Map<String, MavenProject> projectIndex = new HashMap<String, MavenProject>( 256 );
340
341 boolean noErrors =
342 build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<File>(), true, recursive, config );
343
344 populateReactorModelPool( modelPool, interimResults );
345
346 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
347
348 try
349 {
350 noErrors =
351 build( results, new ArrayList<MavenProject>(), projectIndex, interimResults, request,
352 new HashMap<File, Boolean>() ) && noErrors;
353 }
354 finally
355 {
356 Thread.currentThread().setContextClassLoader( oldContextClassLoader );
357 }
358
359 if ( !noErrors )
360 {
361 throw new ProjectBuildingException( results );
362 }
363
364 return results;
365 }
366
367 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults,
368 Map<String, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles,
369 boolean isRoot, boolean recursive, InternalConfig config )
370 {
371 boolean noErrors = true;
372
373 for ( File pomFile : pomFiles )
374 {
375 aggregatorFiles.add( pomFile );
376
377 if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, isRoot, recursive, config ) )
378 {
379 noErrors = false;
380 }
381
382 aggregatorFiles.remove( pomFile );
383 }
384
385 return noErrors;
386 }
387
388 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults,
389 Map<String, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles,
390 boolean isRoot, boolean recursive, InternalConfig config )
391 {
392 boolean noErrors = true;
393
394 ModelBuildingRequest request = getModelBuildingRequest( config );
395
396 MavenProject project = new MavenProject( repositorySystem, this, config.request, logger );
397
398 request.setPomFile( pomFile );
399 request.setTwoPhaseBuilding( true );
400 request.setLocationTracking( true );
401
402 DefaultModelBuildingListener listener =
403 new DefaultModelBuildingListener( project, projectBuildingHelper, config.request );
404 request.setModelBuildingListener( listener );
405
406 try
407 {
408 ModelBuildingResult result = modelBuilder.build( request );
409
410 Model model = result.getEffectiveModel();
411
412 projectIndex.put( result.getModelIds().get( 0 ), project );
413
414 InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot );
415 interimResults.add( interimResult );
416
417 if ( recursive && !model.getModules().isEmpty() )
418 {
419 File basedir = pomFile.getParentFile();
420
421 List<File> moduleFiles = new ArrayList<File>();
422
423 for ( String module : model.getModules() )
424 {
425 if ( StringUtils.isEmpty( module ) )
426 {
427 continue;
428 }
429
430 module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar );
431
432 File moduleFile = new File( basedir, module );
433
434 if ( moduleFile.isDirectory() )
435 {
436 moduleFile = modelProcessor.locatePom( moduleFile );
437 }
438
439 if ( !moduleFile.isFile() )
440 {
441 ModelProblem problem =
442 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile
443 + " does not exist", ModelProblem.Severity.ERROR, ModelProblem.Version.BASE, model, -1, -1, null );
444 result.getProblems().add( problem );
445
446 noErrors = false;
447
448 continue;
449 }
450
451 if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
452 {
453
454 try
455 {
456 moduleFile = moduleFile.getCanonicalFile();
457 }
458 catch ( IOException e )
459 {
460 moduleFile = moduleFile.getAbsoluteFile();
461 }
462 }
463 else
464 {
465 moduleFile = new File( moduleFile.toURI().normalize() );
466 }
467
468 if ( aggregatorFiles.contains( moduleFile ) )
469 {
470 StringBuilder buffer = new StringBuilder( 256 );
471 for ( File aggregatorFile : aggregatorFiles )
472 {
473 buffer.append( aggregatorFile ).append( " -> " );
474 }
475 buffer.append( moduleFile );
476
477 ModelProblem problem =
478 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile
479 + " forms aggregation cycle " + buffer, ModelProblem.Severity.ERROR, ModelProblem.Version.BASE, model, -1, -1,
480 null );
481 result.getProblems().add( problem );
482
483 noErrors = false;
484
485 continue;
486 }
487
488 moduleFiles.add( moduleFile );
489 }
490
491 interimResult.modules = new ArrayList<InterimResult>();
492
493 if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false,
494 recursive, config ) )
495 {
496 noErrors = false;
497 }
498 }
499 }
500 catch ( ModelBuildingException e )
501 {
502 results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) );
503
504 noErrors = false;
505 }
506
507 return noErrors;
508 }
509
510 static class InterimResult
511 {
512
513 File pomFile;
514
515 ModelBuildingRequest request;
516
517 ModelBuildingResult result;
518
519 DefaultModelBuildingListener listener;
520
521 boolean root;
522
523 List<InterimResult> modules = Collections.emptyList();
524
525 InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result,
526 DefaultModelBuildingListener listener, boolean root )
527 {
528 this.pomFile = pomFile;
529 this.request = request;
530 this.result = result;
531 this.listener = listener;
532 this.root = root;
533 }
534
535 }
536
537 private void populateReactorModelPool( ReactorModelPool reactorModelPool, List<InterimResult> interimResults )
538 {
539 for ( InterimResult interimResult : interimResults )
540 {
541 Model model = interimResult.result.getEffectiveModel();
542 reactorModelPool.put( model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile() );
543
544 populateReactorModelPool( reactorModelPool, interimResult.modules );
545 }
546 }
547
548 private boolean build( List<ProjectBuildingResult> results, List<MavenProject> projects,
549 Map<String, MavenProject> projectIndex, List<InterimResult> interimResults,
550 ProjectBuildingRequest request, Map<File, Boolean> profilesXmls )
551 {
552 boolean noErrors = true;
553
554 for ( InterimResult interimResult : interimResults )
555 {
556 try
557 {
558 ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result );
559
560 MavenProject project = interimResult.listener.getProject();
561 initProject( project, projectIndex, result, profilesXmls );
562
563 List<MavenProject> modules = new ArrayList<MavenProject>();
564 noErrors =
565 build( results, modules, projectIndex, interimResult.modules, request, profilesXmls ) && noErrors;
566
567 projects.addAll( modules );
568 projects.add( project );
569
570 project.setExecutionRoot( interimResult.root );
571 project.setCollectedProjects( modules );
572
573 results.add( new DefaultProjectBuildingResult( project, result.getProblems(), null ) );
574 }
575 catch ( ModelBuildingException e )
576 {
577 results.add( new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ) );
578
579 noErrors = false;
580 }
581 }
582
583 return noErrors;
584 }
585
586 private void initProject( MavenProject project, Map<String, MavenProject> projects, ModelBuildingResult result,
587 Map<File, Boolean> profilesXmls )
588 {
589 Model model = result.getEffectiveModel();
590
591 project.setModel( model );
592 project.setOriginalModel( result.getRawModel() );
593
594 project.setFile( model.getPomFile() );
595
596 File parentPomFile = result.getRawModel( result.getModelIds().get( 1 ) ).getPomFile();
597 project.setParentFile( parentPomFile );
598
599 project.setParent( projects.get( result.getModelIds().get( 1 ) ) );
600
601 Artifact projectArtifact =
602 repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null,
603 project.getPackaging() );
604 project.setArtifact( projectArtifact );
605
606 if ( project.getFile() != null )
607 {
608 Build build = project.getBuild();
609 project.addScriptSourceRoot( build.getScriptSourceDirectory() );
610 project.addCompileSourceRoot( build.getSourceDirectory() );
611 project.addTestCompileSourceRoot( build.getTestSourceDirectory() );
612 }
613
614 List<Profile> activeProfiles = new ArrayList<Profile>();
615 activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) );
616 activeProfiles.addAll( result.getActiveExternalProfiles() );
617 project.setActiveProfiles( activeProfiles );
618
619 project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) );
620 for ( String modelId : result.getModelIds() )
621 {
622 project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) );
623 }
624
625 String modelId = findProfilesXml( result, profilesXmls );
626 if ( modelId != null )
627 {
628 ModelProblem problem =
629 new DefaultModelProblem( "Detected profiles.xml alongside " + modelId
630 + ", this file is no longer supported and was ignored" + ", please use the settings.xml instead",
631 ModelProblem.Severity.WARNING, ModelProblem.Version.V30, model, -1, -1, null );
632 result.getProblems().add( problem );
633 }
634 }
635
636 private String findProfilesXml( ModelBuildingResult result, Map<File, Boolean> profilesXmls )
637 {
638 for ( String modelId : result.getModelIds() )
639 {
640 Model model = result.getRawModel( modelId );
641
642 File basedir = model.getProjectDirectory();
643 if ( basedir == null )
644 {
645 break;
646 }
647
648 Boolean profilesXml = profilesXmls.get( basedir );
649 if ( profilesXml == null )
650 {
651 profilesXml = Boolean.valueOf( new File( basedir, "profiles.xml" ).exists() );
652 profilesXmls.put( basedir, profilesXml );
653 }
654 if ( profilesXml.booleanValue() )
655 {
656 return modelId;
657 }
658 }
659
660 return null;
661 }
662
663 class InternalConfig
664 {
665
666 public final ProjectBuildingRequest request;
667
668 public final RepositorySystemSession session;
669
670 public final List<RemoteRepository> repositories;
671
672 public final ReactorModelPool modelPool;
673
674 InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool )
675 {
676 this.request = request;
677 this.modelPool = modelPool;
678 session =
679 LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(),
680 repoSystem );
681 repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() );
682 }
683
684 }
685
686 }