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