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