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