1 package org.apache.maven.project;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
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
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 }