1 package org.apache.maven.graph;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Map;
30
31 import org.apache.maven.DefaultMaven;
32 import org.apache.maven.MavenExecutionException;
33 import org.apache.maven.ProjectCycleException;
34 import org.apache.maven.artifact.ArtifactUtils;
35 import org.apache.maven.execution.MavenExecutionRequest;
36 import org.apache.maven.execution.MavenExecutionResult;
37 import org.apache.maven.execution.MavenSession;
38 import org.apache.maven.execution.ProjectDependencyGraph;
39 import org.apache.maven.model.Plugin;
40 import org.apache.maven.model.building.DefaultModelProblem;
41 import org.apache.maven.model.building.ModelProblem;
42 import org.apache.maven.model.building.ModelProblemUtils;
43 import org.apache.maven.model.building.ModelSource;
44 import org.apache.maven.model.building.Result;
45 import org.apache.maven.model.building.UrlModelSource;
46 import org.apache.maven.project.MavenProject;
47 import org.apache.maven.project.ProjectBuilder;
48 import org.apache.maven.project.ProjectBuildingException;
49 import org.apache.maven.project.ProjectBuildingRequest;
50 import org.apache.maven.project.ProjectBuildingResult;
51 import org.codehaus.plexus.component.annotations.Component;
52 import org.codehaus.plexus.component.annotations.Requirement;
53 import org.codehaus.plexus.logging.Logger;
54 import org.codehaus.plexus.util.StringUtils;
55 import org.codehaus.plexus.util.dag.CycleDetectedException;
56
57 import com.google.common.collect.Lists;
58
59 @Component( role = GraphBuilder.class, hint = GraphBuilder.HINT )
60 public class DefaultGraphBuilder
61 implements GraphBuilder
62 {
63 @Requirement
64 private Logger logger;
65
66 @Requirement
67 protected ProjectBuilder projectBuilder;
68
69 @Override
70 public Result<ProjectDependencyGraph> build( MavenSession session )
71 {
72 if ( session.getProjectDependencyGraph() != null )
73 {
74 return dependencyGraph( session, session.getProjects(), false );
75 }
76
77 List<MavenProject> projects = session.getProjects();
78
79 if ( projects == null )
80 {
81 try
82 {
83 projects = getProjectsForMavenReactor( session );
84 }
85 catch ( ProjectBuildingException e )
86 {
87 return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, e ) ) );
88 }
89
90 validateProjects( projects );
91
92 return dependencyGraph( session, projects, true );
93 }
94 else
95 {
96 return dependencyGraph( session, projects, false );
97 }
98 }
99
100 private Result<ProjectDependencyGraph> dependencyGraph( MavenSession session, List<MavenProject> projects,
101 boolean applyMakeBehaviour )
102 {
103 MavenExecutionRequest request = session.getRequest();
104
105 ProjectDependencyGraph projectDependencyGraph = null;
106
107 try
108 {
109 projectDependencyGraph = new DefaultProjectDependencyGraph( projects );
110
111 if ( applyMakeBehaviour )
112 {
113 List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects();
114
115 activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request );
116 activeProjects = trimExcludedProjects( activeProjects, request );
117 activeProjects = trimResumedProjects( activeProjects, request );
118
119 if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() )
120 {
121 projectDependencyGraph =
122 new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects );
123 }
124 }
125 }
126 catch ( CycleDetectedException e )
127 {
128 String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
129 ProjectCycleException error = new ProjectCycleException( message, e );
130 return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, error ) ) );
131 }
132 catch ( org.apache.maven.project.DuplicateProjectException e )
133 {
134 return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, e ) ) );
135 }
136 catch ( MavenExecutionException e )
137 {
138 return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, e ) ) );
139 }
140
141 session.setProjects( projectDependencyGraph.getSortedProjects() );
142 session.setProjectDependencyGraph( projectDependencyGraph );
143
144 return Result.success( projectDependencyGraph );
145 }
146
147 private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
148 MavenExecutionRequest request )
149 throws MavenExecutionException
150 {
151 List<MavenProject> result = projects;
152
153 if ( !request.getSelectedProjects().isEmpty() )
154 {
155 File reactorDirectory = null;
156 if ( request.getBaseDirectory() != null )
157 {
158 reactorDirectory = new File( request.getBaseDirectory() );
159 }
160
161 Collection<MavenProject> selectedProjects = new LinkedHashSet<>( projects.size() );
162
163 for ( String selector : request.getSelectedProjects() )
164 {
165 MavenProject selectedProject = null;
166
167 for ( MavenProject project : projects )
168 {
169 if ( isMatchingProject( project, selector, reactorDirectory ) )
170 {
171 selectedProject = project;
172 break;
173 }
174 }
175
176 if ( selectedProject != null )
177 {
178 selectedProjects.add( selectedProject );
179 }
180 else
181 {
182 throw new MavenExecutionException( "Could not find the selected project in the reactor: "
183 + selector, request.getPom() );
184 }
185 }
186
187 boolean makeUpstream = false;
188 boolean makeDownstream = false;
189
190 if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
191 {
192 makeUpstream = true;
193 }
194 else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
195 {
196 makeDownstream = true;
197 }
198 else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
199 {
200 makeUpstream = true;
201 makeDownstream = true;
202 }
203 else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
204 {
205 throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
206 request.getPom() );
207 }
208
209 if ( makeUpstream || makeDownstream )
210 {
211 for ( MavenProject selectedProject : new ArrayList<>( selectedProjects ) )
212 {
213 if ( makeUpstream )
214 {
215 selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
216 }
217 if ( makeDownstream )
218 {
219 selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
220 }
221 }
222 }
223
224 result = new ArrayList<>( selectedProjects.size() );
225
226 for ( MavenProject project : projects )
227 {
228 if ( selectedProjects.contains( project ) )
229 {
230 result.add( project );
231 }
232 }
233 }
234
235 return result;
236 }
237
238 private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request )
239 throws MavenExecutionException
240 {
241 List<MavenProject> result = projects;
242
243 if ( !request.getExcludedProjects().isEmpty() )
244 {
245 File reactorDirectory = null;
246
247 if ( request.getBaseDirectory() != null )
248 {
249 reactorDirectory = new File( request.getBaseDirectory() );
250 }
251
252 Collection<MavenProject> excludedProjects = new LinkedHashSet<>( projects.size() );
253
254 for ( String selector : request.getExcludedProjects() )
255 {
256 MavenProject excludedProject = null;
257
258 for ( MavenProject project : projects )
259 {
260 if ( isMatchingProject( project, selector, reactorDirectory ) )
261 {
262 excludedProject = project;
263 break;
264 }
265 }
266
267 if ( excludedProject != null )
268 {
269 excludedProjects.add( excludedProject );
270 }
271 else
272 {
273 throw new MavenExecutionException( "Could not find the selected project in the reactor: "
274 + selector, request.getPom() );
275 }
276 }
277
278 result = new ArrayList<>( projects.size() );
279 for ( MavenProject project : projects )
280 {
281 if ( !excludedProjects.contains( project ) )
282 {
283 result.add( project );
284 }
285 }
286 }
287
288 return result;
289 }
290
291 private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
292 throws MavenExecutionException
293 {
294 List<MavenProject> result = projects;
295
296 if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
297 {
298 File reactorDirectory = null;
299 if ( request.getBaseDirectory() != null )
300 {
301 reactorDirectory = new File( request.getBaseDirectory() );
302 }
303
304 String selector = request.getResumeFrom();
305
306 result = new ArrayList<>( projects.size() );
307
308 boolean resumed = false;
309
310 for ( MavenProject project : projects )
311 {
312 if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
313 {
314 resumed = true;
315 }
316
317 if ( resumed )
318 {
319 result.add( project );
320 }
321 }
322
323 if ( !resumed )
324 {
325 throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
326 + " vs " + projects, request.getPom() );
327 }
328 }
329
330 return result;
331 }
332
333 private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
334 {
335
336 if ( selector.indexOf( ':' ) >= 0 )
337 {
338 String id = ':' + project.getArtifactId();
339
340 if ( id.equals( selector ) )
341 {
342 return true;
343 }
344
345 id = project.getGroupId() + id;
346
347 if ( id.equals( selector ) )
348 {
349 return true;
350 }
351 }
352
353
354 else if ( reactorDirectory != null )
355 {
356 File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
357
358 if ( selectedProject.isFile() )
359 {
360 return selectedProject.equals( project.getFile() );
361 }
362 else if ( selectedProject.isDirectory() )
363 {
364 return selectedProject.equals( project.getBasedir() );
365 }
366 }
367
368 return false;
369 }
370
371 private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
372 {
373 if ( !result.getExceptions().contains( e ) )
374 {
375 result.addException( e );
376 }
377
378 return result;
379 }
380
381
382
383
384
385
386
387 private List<MavenProject> getProjectsForMavenReactor( MavenSession session )
388 throws ProjectBuildingException
389 {
390 MavenExecutionRequest request = session.getRequest();
391
392 request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
393
394 List<MavenProject> projects = new ArrayList<>();
395
396
397
398 if ( request.getPom() == null )
399 {
400 ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) );
401 MavenProject project = projectBuilder.build( modelSource, request.getProjectBuildingRequest() )
402 .getProject();
403 project.setExecutionRoot( true );
404 projects.add( project );
405 request.setProjectPresent( false );
406 return projects;
407 }
408
409 List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() );
410 collectProjects( projects, files, request );
411 return projects;
412 }
413
414 private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
415 throws ProjectBuildingException
416 {
417 ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
418
419 List<ProjectBuildingResult> results = projectBuilder.build( files, request.isRecursive(),
420 projectBuildingRequest );
421
422 boolean problems = false;
423
424 for ( ProjectBuildingResult result : results )
425 {
426 projects.add( result.getProject() );
427
428 if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
429 {
430 logger.warn( "" );
431 logger.warn( "Some problems were encountered while building the effective model for "
432 + result.getProject().getId() );
433
434 for ( ModelProblem problem : result.getProblems() )
435 {
436 String loc = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
437 logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( loc ) ? " @ " + loc : "" ) );
438 }
439
440 problems = true;
441 }
442 }
443
444 if ( problems )
445 {
446 logger.warn( "" );
447 logger.warn( "It is highly recommended to fix these problems"
448 + " because they threaten the stability of your build." );
449 logger.warn( "" );
450 logger.warn( "For this reason, future Maven versions might no"
451 + " longer support building such malformed projects." );
452 logger.warn( "" );
453 }
454 }
455
456 private void validateProjects( List<MavenProject> projects )
457 {
458 Map<String, MavenProject> projectsMap = new HashMap<>();
459
460 for ( MavenProject p : projects )
461 {
462 String projectKey = ArtifactUtils.key( p.getGroupId(), p.getArtifactId(), p.getVersion() );
463
464 projectsMap.put( projectKey, p );
465 }
466
467 for ( MavenProject project : projects )
468 {
469
470 for ( Plugin plugin : project.getBuildPlugins() )
471 {
472 if ( plugin.isExtensions() )
473 {
474 String pluginKey = ArtifactUtils.key( plugin.getGroupId(), plugin.getArtifactId(),
475 plugin.getVersion() );
476
477 if ( projectsMap.containsKey( pluginKey ) )
478 {
479 logger.warn( project.getName() + " uses " + plugin.getKey()
480 + " as extensions, which is not possible within the same reactor build. "
481 + "This plugin was pulled from the local repository!" );
482 }
483 }
484 }
485 }
486 }
487
488 }