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