1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.buildcache;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23
24 import java.io.File;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Optional;
31 import java.util.Properties;
32 import java.util.Set;
33 import java.util.TreeSet;
34 import java.util.function.Function;
35 import java.util.stream.Collectors;
36
37 import org.apache.maven.SessionScoped;
38 import org.apache.maven.buildcache.checksum.KeyUtils;
39 import org.apache.maven.buildcache.xml.CacheConfig;
40 import org.apache.maven.buildcache.xml.config.Discovery;
41 import org.apache.maven.buildcache.xml.config.MultiModule;
42 import org.apache.maven.execution.MavenSession;
43 import org.apache.maven.project.DefaultProjectBuildingRequest;
44 import org.apache.maven.project.MavenProject;
45 import org.apache.maven.project.ProjectBuilder;
46 import org.apache.maven.project.ProjectBuildingException;
47 import org.apache.maven.project.ProjectBuildingRequest;
48 import org.apache.maven.project.ProjectBuildingResult;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 @SessionScoped
53 @Named
54 public class DefaultMultiModuleSupport implements MultiModuleSupport {
55
56 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMultiModuleSupport.class);
57
58 private final ProjectBuilder projectBuilder;
59 private final CacheConfig cacheConfig;
60 private final MavenSession session;
61
62 private volatile boolean built;
63 private volatile Map<String, MavenProject> projectMap;
64 private volatile Map<String, MavenProject> sessionProjectMap;
65
66 @Inject
67 public DefaultMultiModuleSupport(ProjectBuilder projectBuilder, CacheConfig cacheConfig, MavenSession session) {
68 this.projectBuilder = projectBuilder;
69 this.cacheConfig = cacheConfig;
70 this.session = session;
71 }
72
73 @Override
74 public boolean isPartOfSession(String groupId, String artifactId, String version) {
75 return getProjectMap(session).containsKey(KeyUtils.getProjectKey(groupId, artifactId, version));
76 }
77
78 @Override
79 public Optional<MavenProject> tryToResolveProject(String groupId, String artifactId, String version) {
80 return Optional.ofNullable(
81 getMultiModuleProjectsMap().get(KeyUtils.getProjectKey(groupId, artifactId, version)));
82 }
83
84 @Override
85 public boolean isPartOfMultiModule(String groupId, String artifactId, String version) {
86 String projectKey = KeyUtils.getProjectKey(groupId, artifactId, version);
87 return getProjectMap(session).containsKey(projectKey)
88 || getMultiModuleProjectsMap().containsKey(projectKey);
89 }
90
91 private Map<String, MavenProject> getProjectMap(MavenSession session) {
92 if (sessionProjectMap != null) {
93 return sessionProjectMap;
94 }
95 sessionProjectMap =
96 session.getProjects().stream().collect(Collectors.toMap(KeyUtils::getProjectKey, Function.identity()));
97 return sessionProjectMap;
98 }
99
100 private Map<String, MavenProject> getMultiModuleProjectsMap() {
101 if (projectMap != null) {
102 return projectMap;
103 }
104 return getMultiModuleProjectsMapInner(session);
105 }
106
107 private synchronized Map<String, MavenProject> getMultiModuleProjectsMapInner(MavenSession session) {
108 if (projectMap != null) {
109 return projectMap;
110 }
111 buildModel(session);
112 return projectMap;
113 }
114
115 private synchronized void buildModel(MavenSession session) {
116 if (built) {
117 return;
118 }
119
120 Optional<Discovery> multiModuleDiscovery =
121 Optional.ofNullable(cacheConfig.getMultiModule()).map(MultiModule::getDiscovery);
122
123
124 if (!multiModuleDiscovery.isPresent()) {
125 projectMap = buildProjectMap(session.getProjects());
126 return;
127 }
128
129 Set<String> scanProfiles = new TreeSet<>(
130 multiModuleDiscovery.map(Discovery::getScanProfiles).orElse(Collections.emptyList()));
131 MavenProject currentProject = session.getCurrentProject();
132 File multiModulePomFile = getMultiModulePomFile(session);
133
134 ProjectBuildingRequest projectBuildingRequest = currentProject.getProjectBuildingRequest();
135 boolean profilesMatched = projectBuildingRequest.getActiveProfileIds().containsAll(scanProfiles);
136
137
138 if (currentProject.getFile().getAbsolutePath().equals(multiModulePomFile.getAbsolutePath())
139 && profilesMatched) {
140 projectMap = buildProjectMap(session.getProjects());
141 return;
142 }
143
144 long t0 = System.currentTimeMillis();
145
146 ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(projectBuildingRequest);
147
148
149
150
151
152
153 buildingRequest.setProfiles(buildingRequest.getProfiles().stream()
154 .peek(it -> it.setProperties(new Properties()))
155 .collect(Collectors.toList()));
156 if (!profilesMatched) {
157 Set<String> profiles = new LinkedHashSet<>(buildingRequest.getActiveProfileIds());
158
159 profiles.addAll(scanProfiles);
160 buildingRequest.setActiveProfileIds(new ArrayList<>(profiles));
161 }
162 try {
163 List<ProjectBuildingResult> buildingResults =
164 projectBuilder.build(Collections.singletonList(multiModulePomFile), true, buildingRequest);
165 LOGGER.info(
166 "Multi module project model calculated [activeProfiles={}, time={} ms ",
167 buildingRequest.getActiveProfileIds(),
168 System.currentTimeMillis() - t0);
169
170 List<MavenProject> projectList = buildingResults.stream()
171 .map(ProjectBuildingResult::getProject)
172 .collect(Collectors.toList());
173 projectMap = buildProjectMap(projectList);
174
175 } catch (ProjectBuildingException e) {
176 LOGGER.error("Unable to build model", e);
177 } finally {
178 built = true;
179 }
180 }
181
182 private Map<String, MavenProject> buildProjectMap(List<MavenProject> projectList) {
183 return projectList.stream().collect(Collectors.toMap(KeyUtils::getProjectKey, Function.identity()));
184 }
185
186 private static File getMultiModulePomFile(MavenSession session) {
187 return CacheUtils.getMultimoduleRoot(session).resolve("pom.xml").toFile();
188 }
189 }