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