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