1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project.collector;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.nio.file.Path;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.function.Predicate;
32
33 import org.apache.maven.execution.MavenExecutionRequest;
34 import org.apache.maven.model.Plugin;
35 import org.apache.maven.model.building.ModelProblem;
36 import org.apache.maven.model.locator.ModelLocator;
37 import org.apache.maven.plugin.PluginManagerException;
38 import org.apache.maven.plugin.PluginResolutionException;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.project.ProjectBuildingException;
41 import org.apache.maven.project.ProjectBuildingResult;
42 import org.eclipse.aether.resolution.ArtifactResolutionException;
43 import org.eclipse.aether.transfer.ArtifactNotFoundException;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47
48
49
50 @Named("MultiModuleCollectionStrategy")
51 @Singleton
52 public class MultiModuleCollectionStrategy implements ProjectCollectionStrategy {
53 private static final Logger LOGGER = LoggerFactory.getLogger(MultiModuleCollectionStrategy.class);
54 private final ModelLocator modelLocator;
55 private final ProjectsSelector projectsSelector;
56
57 @Inject
58 public MultiModuleCollectionStrategy(ModelLocator modelLocator, ProjectsSelector projectsSelector) {
59 this.modelLocator = modelLocator;
60 this.projectsSelector = projectsSelector;
61 }
62
63 @Override
64 public List<MavenProject> collectProjects(MavenExecutionRequest request) throws ProjectBuildingException {
65 File moduleProjectPomFile = getRootProject(request);
66 List<File> files = Collections.singletonList(moduleProjectPomFile.getAbsoluteFile());
67 try {
68 List<MavenProject> projects = projectsSelector.selectProjects(files, request);
69 boolean isRequestedProjectCollected = isRequestedProjectCollected(request, projects);
70 if (isRequestedProjectCollected) {
71 return projects;
72 } else {
73 LOGGER.debug(
74 "Multi module project collection failed:{}"
75 + "Detected a POM file next to a .mvn directory in a parent directory ({}). "
76 + "Maven assumed that POM file to be the parent of the requested project ({}), but it turned "
77 + "out that it was not. Another project collection strategy will be executed as result.",
78 System.lineSeparator(),
79 moduleProjectPomFile.getAbsolutePath(),
80 request.getPom().getAbsolutePath());
81 return Collections.emptyList();
82 }
83 } catch (ProjectBuildingException e) {
84 boolean fallThrough = isModuleOutsideRequestScopeDependingOnPluginModule(request, e);
85
86 if (fallThrough) {
87 LOGGER.debug(
88 "Multi module project collection failed:{}"
89 + "Detected that one of the modules of this multi-module project uses another module as "
90 + "plugin extension which still needed to be built. This is not possible within the same "
91 + "reactor build. Another project collection strategy will be executed as result.",
92 System.lineSeparator());
93 return Collections.emptyList();
94 }
95
96 throw e;
97 }
98 }
99
100 private File getRootProject(MavenExecutionRequest request) {
101 Path rootDirectory = request.getRootDirectory();
102 if (request.getPom().getParentFile().toPath().equals(rootDirectory)) {
103 return request.getPom();
104 } else {
105 Path rootProjectPom = modelLocator.locateExistingPom(rootDirectory);
106 if (rootProjectPom == null) {
107 LOGGER.info(
108 "Maven detected that the requested POM file is part of a multi-module project, "
109 + "but could not find a pom.xml file in the root directory '{}'.",
110 rootDirectory);
111 LOGGER.info(
112 "The reactor is limited to all projects under: {}",
113 request.getPom().getParent());
114 return request.getPom();
115 }
116
117 return rootProjectPom.toFile();
118 }
119 }
120
121
122
123
124
125
126
127
128 private boolean isRequestedProjectCollected(MavenExecutionRequest request, List<MavenProject> projects) {
129 return projects.stream().map(MavenProject::getFile).anyMatch(request.getPom()::equals);
130 }
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 private boolean isModuleOutsideRequestScopeDependingOnPluginModule(
147 MavenExecutionRequest request, ProjectBuildingException exception) {
148 return exception.getResults().stream()
149 .map(ProjectBuildingResult::getProject)
150 .filter(Objects::nonNull)
151 .filter(project -> request.getPom().equals(project.getFile()))
152 .findFirst()
153 .map(requestPomProject -> {
154 List<MavenProject> modules = requestPomProject.getCollectedProjects() != null
155 ? requestPomProject.getCollectedProjects()
156 : Collections.emptyList();
157 List<MavenProject> projectsInRequestScope = new ArrayList<>(modules);
158 projectsInRequestScope.add(requestPomProject);
159
160 Predicate<ProjectBuildingResult> projectsOutsideOfRequestScope =
161 pr -> !projectsInRequestScope.contains(pr.getProject());
162
163 Predicate<Exception> pluginArtifactNotFoundException = exc -> exc instanceof PluginManagerException
164 && exc.getCause() instanceof PluginResolutionException
165 && exc.getCause().getCause() instanceof ArtifactResolutionException
166 && exc.getCause().getCause().getCause() instanceof ArtifactNotFoundException;
167
168 Predicate<Plugin> isPluginPartOfRequestScope = plugin -> projectsInRequestScope.stream()
169 .anyMatch(project -> project.getGroupId().equals(plugin.getGroupId())
170 && project.getArtifactId().equals(plugin.getArtifactId())
171 && project.getVersion().equals(plugin.getVersion()));
172
173 return exception.getResults().stream()
174 .filter(projectsOutsideOfRequestScope)
175 .flatMap(projectBuildingResult -> projectBuildingResult.getProblems().stream())
176 .map(ModelProblem::getException)
177 .filter(pluginArtifactNotFoundException)
178 .map(exc -> ((PluginResolutionException) exc.getCause()).getPlugin())
179 .anyMatch(isPluginPartOfRequestScope);
180 })
181 .orElse(false);
182 }
183 }