View Javadoc

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