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.model.Build;
31  import org.apache.maven.model.Model;
32  import org.apache.maven.model.Profile;
33  import org.apache.maven.model.building.DefaultModelBuildingRequest;
34  import org.apache.maven.model.building.DefaultModelProblem;
35  import org.apache.maven.model.building.FileModelSource;
36  import org.apache.maven.model.building.ModelBuilder;
37  import org.apache.maven.model.building.ModelBuildingException;
38  import org.apache.maven.model.building.ModelBuildingRequest;
39  import org.apache.maven.model.building.ModelBuildingResult;
40  import org.apache.maven.model.building.ModelProblem;
41  import org.apache.maven.model.building.ModelProcessor;
42  import org.apache.maven.model.building.ModelSource;
43  import org.apache.maven.model.building.StringModelSource;
44  import org.apache.maven.model.resolution.ModelResolver;
45  import org.apache.maven.repository.RepositorySystem;
46  import org.codehaus.plexus.component.annotations.Component;
47  import org.codehaus.plexus.component.annotations.Requirement;
48  import org.codehaus.plexus.logging.Logger;
49  import org.codehaus.plexus.util.Os;
50  import org.codehaus.plexus.util.StringUtils;
51  import org.sonatype.aether.impl.ArtifactResolver;
52  import org.sonatype.aether.impl.RemoteRepositoryManager;
53  import org.sonatype.aether.repository.LocalRepositoryManager;
54  import org.sonatype.aether.repository.WorkspaceRepository;
55  import org.sonatype.aether.resolution.ArtifactRequest;
56  import org.sonatype.aether.resolution.ArtifactResult;
57  import org.sonatype.aether.util.artifact.SubArtifact;
58  
59  /**
60   * @version $Id: DefaultProjectBuilder.java 1002852 2010-09-29 21:31:34Z bentmann $
61   */
62  @Component(role = ProjectBuilder.class)
63  public class DefaultProjectBuilder
64      implements ProjectBuilder
65  {
66  
67      @Requirement
68      private Logger logger;
69  
70      @Requirement
71      private ModelBuilder modelBuilder;
72  
73      @Requirement
74      private ModelProcessor modelProcessor;
75  
76      @Requirement
77      private ProjectBuildingHelper projectBuildingHelper;
78  
79      @Requirement
80      private RepositorySystem repositorySystem;
81  
82      @Requirement
83      private ArtifactResolver artifactResolver;
84  
85      @Requirement
86      private RemoteRepositoryManager repositoryManager;
87  
88      @Requirement
89      private ProjectDependenciesResolver dependencyResolver;
90  
91      // ----------------------------------------------------------------------
92      // MavenProjectBuilder Implementation
93      // ----------------------------------------------------------------------
94  
95      public ProjectBuildingResult build( File pomFile, ProjectBuildingRequest configuration )
96          throws ProjectBuildingException
97      {
98          return build( pomFile, new FileModelSource( pomFile ), configuration );
99      }
100 
101     public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest configuration )
102         throws ProjectBuildingException
103     {
104         return build( null, modelSource, configuration );
105     }
106 
107     private ProjectBuildingResult build( File pomFile, ModelSource modelSource, ProjectBuildingRequest configuration )
108         throws ProjectBuildingException
109     {
110         ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
111 
112         try
113         {
114             MavenProject project = configuration.getProject();
115 
116             List<ModelProblem> modelProblems = null;
117 
118             if ( project == null )
119             {
120                 ModelBuildingRequest request = getModelBuildingRequest( configuration, null );
121 
122                 project = new MavenProject( repositorySystem, this, configuration, logger );
123 
124                 DefaultModelBuildingListener listener =
125                     new DefaultModelBuildingListener( project, projectBuildingHelper, configuration );
126                 request.setModelBuildingListener( listener );
127     
128                 request.setPomFile( pomFile );
129                 request.setModelSource( modelSource );
130                 request.setLocationTracking( pomFile != null );
131     
132                 ModelBuildingResult result;
133                 try
134                 {
135                     result = modelBuilder.build( request );
136                 }
137                 catch ( ModelBuildingException e )
138                 {
139                     throw new ProjectBuildingException( e.getModelId(), e.getMessage(), pomFile, e );
140                 }
141 
142                 modelProblems = result.getProblems();
143 
144                 initProject( project, result, new HashMap<File, Boolean>() );
145             }
146             else if ( configuration.isResolveDependencies() )
147             {
148                 projectBuildingHelper.selectProjectRealm( project );
149             }
150 
151             DependencyResolutionResult resolutionResult = null;
152 
153             if ( configuration.isResolveDependencies() )
154             {
155                 try
156                 {
157                     DefaultDependencyResolutionRequest resolution =
158                         new DefaultDependencyResolutionRequest( project, configuration.getRepositorySession() );
159                     resolutionResult = dependencyResolver.resolve( resolution );
160                 }
161                 catch ( DependencyResolutionException e )
162                 {
163                     resolutionResult = e.getResult();
164                 }
165 
166                 Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
167                 if ( resolutionResult.getDependencyGraph() != null )
168                 {
169                     RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(),
170                                                  Collections.singletonList( project.getArtifact().getId() ), null );
171 
172                     // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
173                     LocalRepositoryManager lrm = configuration.getRepositorySession().getLocalRepositoryManager();
174                     for ( Artifact artifact : artifacts )
175                     {
176                         if ( !artifact.isResolved() )
177                         {
178                             String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) );
179                             artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) );
180                         }
181                     }
182                 }
183                 project.setResolvedArtifacts( artifacts );
184                 project.setArtifacts( artifacts );
185             }
186 
187             return new DefaultProjectBuildingResult( project, modelProblems, resolutionResult );
188         }
189         finally
190         {
191             Thread.currentThread().setContextClassLoader( oldContextClassLoader );
192         }
193     }
194 
195     private List<String> getProfileIds( List<Profile> profiles )
196     {
197         List<String> ids = new ArrayList<String>( profiles.size() );
198 
199         for ( Profile profile : profiles )
200         {
201             ids.add( profile.getId() );
202         }
203 
204         return ids;
205     }
206 
207     private ModelBuildingRequest getModelBuildingRequest( ProjectBuildingRequest configuration,
208                                                           ReactorModelPool modelPool )
209     {
210         ModelResolver resolver =
211             new ProjectModelResolver( configuration.getRepositorySession(), artifactResolver, repositoryManager,
212                                       RepositoryUtils.toRepos( configuration.getRemoteRepositories() ),
213                                       configuration.getRepositoryMerging(), modelPool );
214 
215         ModelBuildingRequest request = new DefaultModelBuildingRequest();
216 
217         request.setValidationLevel( configuration.getValidationLevel() );
218         request.setProcessPlugins( configuration.isProcessPlugins() );
219         request.setProfiles( configuration.getProfiles() );
220         request.setActiveProfileIds( configuration.getActiveProfileIds() );
221         request.setInactiveProfileIds( configuration.getInactiveProfileIds() );
222         request.setSystemProperties( configuration.getSystemProperties() );
223         request.setUserProperties( configuration.getUserProperties() );
224         request.setBuildStartTime( configuration.getBuildStartTime() );
225         request.setModelResolver( resolver );
226 
227         return request;
228     }
229 
230     public ProjectBuildingResult build( Artifact artifact, ProjectBuildingRequest configuration )
231         throws ProjectBuildingException
232     {
233         return build( artifact, false, configuration );
234     }
235 
236     public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest configuration )
237         throws ProjectBuildingException
238     {
239         org.sonatype.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact );
240         if ( !pomArtifact.getExtension().equals( "pom" ) )
241         {
242             pomArtifact = new SubArtifact( pomArtifact, "", "pom" );
243         }
244 
245         ArtifactResult result;
246         try
247         {
248             ArtifactRequest request = new ArtifactRequest();
249             request.setArtifact( pomArtifact );
250             request.setRepositories( RepositoryUtils.toRepos( configuration.getRemoteRepositories() ) );
251             result = artifactResolver.resolveArtifact( configuration.getRepositorySession(), request );
252             pomArtifact = result.getArtifact();
253         }
254         catch ( org.sonatype.aether.resolution.ArtifactResolutionException e )
255         {
256             if ( e.getResults().get( 0 ).isMissing() && allowStubModel )
257             {
258                 return build( null, createStubModelSource( artifact ), configuration );
259             }
260             throw new ProjectBuildingException( artifact.getId(),
261                                                 "Error resolving project artifact: " + e.getMessage(), e );
262         }
263 
264         boolean localProject = result.getRepository() instanceof WorkspaceRepository;
265 
266         File pomFile = pomArtifact.getFile();
267 
268         if ( "pom".equals( artifact.getType() ) )
269         {
270             artifact.selectVersion( pomArtifact.getVersion() );
271             artifact.setFile( pomFile );
272             artifact.setResolved( true );
273         }
274 
275         return build( localProject ? pomFile : null, new FileModelSource( pomFile ), configuration );
276     }
277 
278     private ModelSource createStubModelSource( Artifact artifact )
279     {
280         StringBuilder buffer = new StringBuilder( 1024 );
281 
282         buffer.append( "<?xml version='1.0'?>" );
283         buffer.append( "<project>" );
284         buffer.append( "<modelVersion>4.0.0</modelVersion>" );
285         buffer.append( "<groupId>" ).append( artifact.getGroupId() ).append( "</groupId>" );
286         buffer.append( "<artifactId>" ).append( artifact.getArtifactId() ).append( "</artifactId>" );
287         buffer.append( "<version>" ).append( artifact.getBaseVersion() ).append( "</version>" );
288         buffer.append( "<packaging>" ).append( artifact.getType() ).append( "</packaging>" );
289         buffer.append( "</project>" );
290 
291         return new StringModelSource( buffer, artifact.getId() );
292     }
293 
294     public List<ProjectBuildingResult> build( List<File> pomFiles, boolean recursive, ProjectBuildingRequest config )
295         throws ProjectBuildingException
296     {
297         List<ProjectBuildingResult> results = new ArrayList<ProjectBuildingResult>();
298 
299         List<InterimResult> interimResults = new ArrayList<InterimResult>();
300 
301         ReactorModelPool modelPool = new ReactorModelPool();
302 
303         ReactorModelCache modelCache = new ReactorModelCache();
304 
305         boolean noErrors =
306             build( results, interimResults, pomFiles, true, recursive, config, modelPool, modelCache );
307 
308         populateReactorModelPool( modelPool, interimResults );
309 
310         ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
311 
312         try
313         {
314             noErrors =
315                 build( results, new ArrayList<MavenProject>(), interimResults, config, new HashMap<File, Boolean>() )
316                     && noErrors;
317         }
318         finally
319         {
320             Thread.currentThread().setContextClassLoader( oldContextClassLoader );
321         }
322 
323         if ( !noErrors )
324         {
325             throw new ProjectBuildingException( results );
326         }
327 
328         return results;
329     }
330 
331     private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults,
332                            List<File> pomFiles, boolean isRoot, boolean recursive, ProjectBuildingRequest config,
333                            ReactorModelPool reactorModelPool, ReactorModelCache modelCache )
334     {
335         boolean noErrors = true;
336 
337         for ( File pomFile : pomFiles )
338         {
339             ModelBuildingRequest request = getModelBuildingRequest( config, reactorModelPool );
340 
341             MavenProject project = new MavenProject( repositorySystem, this, config, logger );
342 
343             request.setPomFile( pomFile );
344             request.setTwoPhaseBuilding( true );
345             request.setLocationTracking( true );
346             request.setModelCache( modelCache );
347 
348             DefaultModelBuildingListener listener =
349                 new DefaultModelBuildingListener( project, projectBuildingHelper, config );
350             request.setModelBuildingListener( listener );
351 
352             try
353             {
354                 ModelBuildingResult result = modelBuilder.build( request );
355 
356                 Model model = result.getEffectiveModel();
357 
358                 InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot );
359                 interimResults.add( interimResult );
360 
361                 if ( recursive && !model.getModules().isEmpty() )
362                 {
363                     File basedir = pomFile.getParentFile();
364 
365                     List<File> moduleFiles = new ArrayList<File>();
366 
367                     for ( String module : model.getModules() )
368                     {
369                         if ( StringUtils.isEmpty( module ) )
370                         {
371                             continue;
372                         }
373 
374                         module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar );
375 
376                         File moduleFile = new File( basedir, module );
377 
378                         if ( moduleFile.isDirectory() )
379                         {
380                             moduleFile = modelProcessor.locatePom( moduleFile );
381                         }
382 
383                         if ( !moduleFile.isFile() )
384                         {
385                             ModelProblem problem =
386                                 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile
387                                     + " does not exist", ModelProblem.Severity.ERROR, model, -1, -1, null );
388                             result.getProblems().add( problem );
389 
390                             noErrors = false;
391 
392                             continue;
393                         }
394 
395                         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
396                         {
397                             // we don't canonicalize on unix to avoid interfering with symlinks
398                             try
399                             {
400                                 moduleFile = moduleFile.getCanonicalFile();
401                             }
402                             catch ( IOException e )
403                             {
404                                 moduleFile = moduleFile.getAbsoluteFile();
405                             }
406                         }
407                         else
408                         {
409                             moduleFile = new File( moduleFile.toURI().normalize() );
410                         }
411 
412                         moduleFiles.add( moduleFile );
413                     }
414 
415                     interimResult.modules = new ArrayList<InterimResult>();
416 
417                     if ( !build( results, interimResult.modules, moduleFiles, false, recursive, config,
418                                 reactorModelPool, modelCache ) )
419                     {
420                         noErrors = false;
421                     }
422                 }
423             }
424             catch ( ModelBuildingException e )
425             {
426                 results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) );
427 
428                 noErrors = false;
429             }
430         }
431 
432         return noErrors;
433     }
434 
435     static class InterimResult
436     {
437 
438         File pomFile;
439 
440         ModelBuildingRequest request;
441 
442         ModelBuildingResult result;
443 
444         DefaultModelBuildingListener listener;
445 
446         boolean root;
447 
448         List<InterimResult> modules = Collections.emptyList();
449 
450         InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result,
451                        DefaultModelBuildingListener listener, boolean root )
452         {
453             this.pomFile = pomFile;
454             this.request = request;
455             this.result = result;
456             this.listener = listener;
457             this.root = root;
458         }
459 
460     }
461 
462     private void populateReactorModelPool( ReactorModelPool reactorModelPool, List<InterimResult> interimResults )
463     {
464         for ( InterimResult interimResult : interimResults )
465         {
466             Model model = interimResult.result.getEffectiveModel();
467             reactorModelPool.put( model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile() );
468 
469             populateReactorModelPool( reactorModelPool, interimResult.modules );
470         }
471     }
472 
473     private boolean build( List<ProjectBuildingResult> results, List<MavenProject> projects,
474                            List<InterimResult> interimResults, ProjectBuildingRequest config, Map<File, Boolean> profilesXmls )
475     {
476         boolean noErrors = true;
477 
478         for ( InterimResult interimResult : interimResults )
479         {
480             try
481             {
482                 ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result );
483 
484                 MavenProject project = interimResult.listener.getProject();
485                 initProject( project, result, profilesXmls );
486 
487                 List<MavenProject> modules = new ArrayList<MavenProject>();
488                 noErrors = build( results, modules, interimResult.modules, config, profilesXmls ) && noErrors;
489 
490                 projects.addAll( modules );
491                 projects.add( project );
492 
493                 project.setExecutionRoot( interimResult.root );
494                 project.setCollectedProjects( modules );
495 
496                 results.add( new DefaultProjectBuildingResult( project, result.getProblems(), null ) );
497             }
498             catch ( ModelBuildingException e )
499             {
500                 results.add( new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ) );
501 
502                 noErrors = false;
503             }
504         }
505 
506         return noErrors;
507     }
508 
509     private void initProject( MavenProject project, ModelBuildingResult result, Map<File, Boolean> profilesXmls )
510     {
511         Model model = result.getEffectiveModel();
512 
513         project.setModel( model );
514         project.setOriginalModel( result.getRawModel() );
515 
516         project.setFile( model.getPomFile() );
517 
518         File parentPomFile = result.getRawModel( result.getModelIds().get( 1 ) ).getPomFile();
519         project.setParentFile( parentPomFile );
520 
521         Artifact projectArtifact =
522             repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null,
523                                              project.getPackaging() );
524         project.setArtifact( projectArtifact );
525 
526         if ( project.getFile() != null )
527         {
528             Build build = project.getBuild();
529             project.addScriptSourceRoot( build.getScriptSourceDirectory() );
530             project.addCompileSourceRoot( build.getSourceDirectory() );
531             project.addTestCompileSourceRoot( build.getTestSourceDirectory() );
532         }
533 
534         List<Profile> activeProfiles = new ArrayList<Profile>();
535         activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) );
536         activeProfiles.addAll( result.getActiveExternalProfiles() );
537         project.setActiveProfiles( activeProfiles );
538 
539         project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) );
540         for ( String modelId : result.getModelIds() )
541         {
542             project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) );
543         }
544 
545         String modelId = findProfilesXml( result, profilesXmls );
546         if ( modelId != null )
547         {
548             ModelProblem problem =
549                 new DefaultModelProblem( "Detected profiles.xml alongside " + modelId
550                     + ", this file is no longer supported and was ignored" + ", please use the settings.xml instead",
551                                          ModelProblem.Severity.WARNING, model, -1, -1, null );
552             result.getProblems().add( problem );
553         }
554     }
555 
556     private String findProfilesXml( ModelBuildingResult result, Map<File, Boolean> profilesXmls )
557     {
558         for ( String modelId : result.getModelIds() )
559         {
560             Model model = result.getRawModel( modelId );
561 
562             File basedir = model.getProjectDirectory();
563             if ( basedir == null )
564             {
565                 break;
566             }
567 
568             Boolean profilesXml = profilesXmls.get( basedir );
569             if ( profilesXml == null )
570             {
571                 profilesXml = Boolean.valueOf( new File( basedir, "profiles.xml" ).exists() );
572                 profilesXmls.put( basedir, profilesXml );
573             }
574             if ( profilesXml.booleanValue() )
575             {
576                 return modelId;
577             }
578         }
579 
580         return null;
581     }
582 
583 }