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