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.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      // 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 ) );
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                     // validation error, continue project building and delay failing to help IDEs
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             // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
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                         // we don't canonicalize on unix to avoid interfering with symlinks
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 }