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