View Javadoc

1   package org.apache.maven.project;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
5    * agreements. See the NOTICE file distributed with this work for additional information regarding
6    * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance with the License. You may obtain a
8    * copy of the License at
9    * 
10   * http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software distributed under the License
13   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14   * or implied. See the License for the specific language governing permissions and limitations under
15   * the License.
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   * @version $Id: DefaultProjectBuilder.java 1074288 2011-02-24 20:43:52Z bentmann $
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      // MavenProjectBuilder Implementation
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                     // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
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                         // we don't canonicalize on unix to avoid interfering with symlinks
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 }