1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.toolchain.jdk;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.stream.Stream;
29
30 import org.apache.maven.execution.MavenSession;
31 import org.apache.maven.plugin.AbstractMojo;
32 import org.apache.maven.plugin.MojoFailureException;
33 import org.apache.maven.plugins.annotations.LifecyclePhase;
34 import org.apache.maven.plugins.annotations.Mojo;
35 import org.apache.maven.plugins.annotations.Parameter;
36 import org.apache.maven.toolchain.MisconfiguredToolchainException;
37 import org.apache.maven.toolchain.RequirementMatcherFactory;
38 import org.apache.maven.toolchain.ToolchainFactory;
39 import org.apache.maven.toolchain.ToolchainManagerPrivate;
40 import org.apache.maven.toolchain.ToolchainPrivate;
41 import org.apache.maven.toolchain.model.PersistedToolchains;
42 import org.apache.maven.toolchain.model.ToolchainModel;
43 import org.codehaus.plexus.util.xml.Xpp3Dom;
44
45 import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.ENV;
46 import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_NAME;
47 import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.RUNTIME_VERSION;
48 import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VENDOR;
49 import static org.apache.maven.plugins.toolchain.jdk.ToolchainDiscoverer.VERSION;
50
51
52
53
54
55
56 @Mojo(name = "select-jdk-toolchain", defaultPhase = LifecyclePhase.VALIDATE)
57 public class SelectJdkToolchainMojo extends AbstractMojo {
58
59 public static final String TOOLCHAIN_TYPE_JDK = "jdk";
60
61
62 public enum JdkMode {
63
64 Never,
65
66 IfSame,
67
68 IfMatch
69 }
70
71
72
73
74 @Parameter(property = "toolchain.jdk.version")
75 private String version;
76
77
78
79
80 @Parameter(property = "toolchain.jdk.runtime.name")
81 private String runtimeName;
82
83
84
85
86 @Parameter(property = "toolchain.jdk.runtime.version")
87 private String runtimeVersion;
88
89
90
91
92 @Parameter(property = "toolchain.jdk.vendor")
93 private String vendor;
94
95
96
97
98
99
100
101 @Parameter(property = "toolchain.jdk.env")
102 private String env;
103
104
105
106
107
108
109
110
111
112 @Parameter(property = "toolchain.jdk.mode", defaultValue = "IfMatch")
113 private JdkMode useJdk = JdkMode.IfMatch;
114
115
116
117
118
119 @Parameter(property = "toolchain.jdk.discover", defaultValue = "true")
120 private boolean discoverToolchains = true;
121
122
123
124
125
126
127
128
129
130
131
132
133 @Parameter(property = "toolchain.jdk.comparator", defaultValue = "lts,current,env,version,vendor")
134 private String comparator;
135
136
137
138
139 @Inject
140 private ToolchainManagerPrivate toolchainManager;
141
142
143
144
145 @Inject
146 @Named(TOOLCHAIN_TYPE_JDK)
147 ToolchainFactory factory;
148
149
150
151
152 @Inject
153 private MavenSession session;
154
155
156
157
158 @Inject
159 ToolchainDiscoverer discoverer;
160
161 @Override
162 public void execute() throws MojoFailureException {
163 try {
164 doExecute();
165 } catch (MisconfiguredToolchainException e) {
166 throw new MojoFailureException("Unable to select toolchain: " + e, e);
167 }
168 }
169
170 private void doExecute() throws MisconfiguredToolchainException, MojoFailureException {
171 if (version == null && runtimeName == null && runtimeVersion == null && vendor == null && env == null) {
172 return;
173 }
174
175 Map<String, String> requirements = new HashMap<>();
176 Optional.ofNullable(version).ifPresent(v -> requirements.put(VERSION, v));
177 Optional.ofNullable(runtimeName).ifPresent(v -> requirements.put(RUNTIME_NAME, v));
178 Optional.ofNullable(runtimeVersion).ifPresent(v -> requirements.put(RUNTIME_VERSION, v));
179 Optional.ofNullable(vendor).ifPresent(v -> requirements.put(VENDOR, v));
180 Optional.ofNullable(env).ifPresent(v -> requirements.put(ENV, v));
181
182 ToolchainModel currentJdkToolchainModel =
183 discoverer.getCurrentJdkToolchain().orElse(null);
184 ToolchainPrivate currentJdkToolchain =
185 currentJdkToolchainModel != null ? factory.createToolchain(currentJdkToolchainModel) : null;
186
187 if (useJdk == JdkMode.IfMatch && currentJdkToolchain != null && matches(currentJdkToolchain, requirements)) {
188 getLog().info("Not using an external toolchain as the current JDK matches the requirements.");
189 return;
190 }
191
192 ToolchainPrivate toolchain = Stream.of(toolchainManager.getToolchainsForType(TOOLCHAIN_TYPE_JDK, session))
193 .filter(tc -> matches(tc, requirements))
194 .findFirst()
195 .orElse(null);
196 if (toolchain != null) {
197 getLog().info("Found matching JDK toolchain: " + toolchain);
198 }
199
200 if (toolchain == null && discoverToolchains) {
201 getLog().debug("No matching toolchains configured, trying to discover JDK toolchains");
202 PersistedToolchains persistedToolchains = discoverer.discoverToolchains(comparator);
203 getLog().debug("Discovered " + persistedToolchains.getToolchains().size() + " JDK toolchains");
204
205 for (ToolchainModel tcm : persistedToolchains.getToolchains()) {
206 ToolchainPrivate tc = factory.createToolchain(tcm);
207 if (tc != null && matches(tc, requirements)) {
208 toolchain = tc;
209 getLog().debug("Discovered matching JDK toolchain: " + toolchain);
210 break;
211 }
212 }
213 }
214
215 if (toolchain == null) {
216 throw new MojoFailureException(
217 "Cannot find matching toolchain definitions for the following toolchain types:" + requirements
218 + System.lineSeparator()
219 + "Define the required toolchains in your ~/.m2/toolchains.xml file.");
220 }
221
222 if (useJdk == JdkMode.IfSame
223 && currentJdkToolchain != null
224 && Objects.equals(getJdkHome(currentJdkToolchain), getJdkHome(toolchain))) {
225 getLog().debug("Not using an external toolchain as the current JDK has been selected.");
226 return;
227 }
228
229 toolchainManager.storeToolchainToBuildContext(toolchain, session);
230 getLog().debug("Found matching JDK toolchain: " + toolchain);
231 }
232
233 private boolean matches(ToolchainPrivate tc, Map<String, String> requirements) {
234 ToolchainModel model = tc.getModel();
235 for (Map.Entry<String, String> req : requirements.entrySet()) {
236 String key = req.getKey();
237 String reqVal = req.getValue();
238 String tcVal = model.getProvides().getProperty(key);
239 if (tcVal == null) {
240 getLog().debug("Toolchain " + tc + " is missing required property: " + key);
241 return false;
242 }
243 if (!matches(key, reqVal, tcVal)) {
244 getLog().debug("Toolchain " + tc + " doesn't match required property: " + key);
245 return false;
246 }
247 }
248 return true;
249 }
250
251 private boolean matches(String key, String reqVal, String tcVal) {
252 switch (key) {
253 case VERSION:
254 return RequirementMatcherFactory.createVersionMatcher(tcVal).matches(reqVal);
255 case ENV:
256 return reqVal.matches("(.*,|^)\\Q" + tcVal + "\\E(,.*|$)");
257 default:
258 return RequirementMatcherFactory.createExactMatcher(tcVal).matches(reqVal);
259 }
260 }
261
262 private String getJdkHome(ToolchainPrivate toolchain) {
263 return ((Xpp3Dom) toolchain.getModel().getConfiguration())
264 .getChild("jdkHome")
265 .getValue();
266 }
267 }