1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.impl;
20
21 import java.nio.file.Files;
22 import java.nio.file.Path;
23 import java.nio.file.Paths;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.function.Predicate;
27 import java.util.stream.Collectors;
28
29 import org.apache.maven.api.JavaToolchain;
30 import org.apache.maven.api.Toolchain;
31 import org.apache.maven.api.Version;
32 import org.apache.maven.api.VersionConstraint;
33 import org.apache.maven.api.annotations.Nonnull;
34 import org.apache.maven.api.di.Inject;
35 import org.apache.maven.api.di.Named;
36 import org.apache.maven.api.di.Singleton;
37 import org.apache.maven.api.services.ToolchainFactory;
38 import org.apache.maven.api.services.ToolchainFactoryException;
39 import org.apache.maven.api.services.VersionParser;
40 import org.apache.maven.api.services.VersionParserException;
41 import org.apache.maven.api.toolchain.ToolchainModel;
42 import org.apache.maven.api.xml.XmlNode;
43 import org.apache.maven.impl.util.Os;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 @Named("jdk")
48 @Singleton
49 public class DefaultJavaToolchainFactory implements ToolchainFactory {
50
51 public static final String KEY_JAVAHOME = "jdkHome";
52
53 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultJavaToolchainFactory.class);
54
55 final VersionParser versionParser;
56
57 @Inject
58 public DefaultJavaToolchainFactory(VersionParser versionParser) {
59 this.versionParser = versionParser;
60 }
61
62 @Nonnull
63 @Override
64 public JavaToolchain createToolchain(@Nonnull ToolchainModel model) {
65
66 Map<String, Predicate<String>> matchers = model.getProvides().entrySet().stream()
67 .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, entry -> {
68 String key = entry.getKey();
69 String value = entry.getValue();
70 if (value == null) {
71 throw new ToolchainFactoryException(
72 "Provides token '" + key + "' doesn't have any value configured.");
73 }
74 return "version".equals(key) ? new VersionMatcher(versionParser, value) : new ExactMatcher(value);
75 }));
76
77
78 XmlNode dom = model.getConfiguration();
79 XmlNode javahome = dom != null ? dom.child(KEY_JAVAHOME) : null;
80 if (javahome == null || javahome.value() == null) {
81 throw new ToolchainFactoryException(
82 "Java toolchain without the " + KEY_JAVAHOME + " configuration element.");
83 }
84 Path normal = Paths.get(javahome.value()).normalize();
85 if (!Files.exists(normal)) {
86 throw new ToolchainFactoryException("Non-existing JDK home configuration at " + normal.toAbsolutePath());
87 }
88 String javaHome = normal.toString();
89
90 Version javaVersion = model.getProvides().entrySet().stream()
91 .filter(entry -> "version".equals(entry.getKey()))
92 .map(Map.Entry::getValue)
93 .map(versionParser::parseVersion)
94 .findAny()
95 .orElse(null);
96
97 return new DefaultJavaToolchain(model, javaHome, javaVersion, matchers);
98 }
99
100 @Nonnull
101 @Override
102 public Optional<Toolchain> createDefaultToolchain() {
103 return Optional.empty();
104 }
105
106 static class DefaultJavaToolchain implements JavaToolchain {
107
108 final ToolchainModel model;
109 final String javaHome;
110 final Map<String, Predicate<String>> matchers;
111
112 private Version javaVersion;
113
114 DefaultJavaToolchain(
115 ToolchainModel model, String javaHome, Version javaVersion, Map<String, Predicate<String>> matchers) {
116 this.model = model;
117 this.javaHome = javaHome;
118 this.javaVersion = javaVersion;
119 this.matchers = matchers;
120 }
121
122 @Override
123 public String getJavaHome() {
124 return javaHome;
125 }
126
127 @Override
128 public Version getJavaVersion() {
129 return javaVersion;
130 }
131
132 @Override
133 public String getType() {
134 return "jdk";
135 }
136
137 @Override
138 public ToolchainModel getModel() {
139 return model;
140 }
141
142 @Override
143 public String findTool(String toolName) {
144 Path toRet = findTool(toolName, Paths.get(getJavaHome()).normalize());
145 if (toRet != null) {
146 return toRet.toAbsolutePath().toString();
147 }
148 return null;
149 }
150
151 private static Path findTool(String toolName, Path installDir) {
152 Path bin = installDir.resolve("bin");
153 if (Files.isDirectory(bin)) {
154 if (Os.IS_WINDOWS) {
155 Path tool = bin.resolve(toolName + ".exe");
156 if (Files.exists(tool)) {
157 return tool;
158 }
159 }
160 Path tool = bin.resolve(toolName);
161 if (Files.exists(tool)) {
162 return tool;
163 }
164 }
165 return null;
166 }
167
168 @Override
169 public boolean matchesRequirements(Map<String, String> requirements) {
170 for (Map.Entry<String, String> requirement : requirements.entrySet()) {
171 String key = requirement.getKey();
172
173 Predicate<String> matcher = matchers.get(key);
174
175 if (matcher == null) {
176 LOGGER.debug("Toolchain {} is missing required property: {}", this, key);
177 return false;
178 }
179 if (!matcher.test(requirement.getValue())) {
180 LOGGER.debug("Toolchain {} doesn't match required property: {}", this, key);
181 return false;
182 }
183 }
184 return true;
185 }
186
187 @Override
188 public String toString() {
189 return "JDK[" + getJavaHome() + "]";
190 }
191 }
192
193 static final class ExactMatcher implements Predicate<String> {
194
195 final String provides;
196
197 ExactMatcher(String provides) {
198 this.provides = provides;
199 }
200
201 @Override
202 public boolean test(String requirement) {
203 return provides.equalsIgnoreCase(requirement);
204 }
205
206 @Override
207 public String toString() {
208 return provides;
209 }
210 }
211
212 static final class VersionMatcher implements Predicate<String> {
213
214 final VersionParser versionParser;
215 final Version version;
216
217 VersionMatcher(VersionParser versionParser, String version) {
218 this.versionParser = versionParser;
219 this.version = versionParser.parseVersion(version);
220 }
221
222 @Override
223 public boolean test(String requirement) {
224 try {
225 VersionConstraint constraint = versionParser.parseVersionConstraint(requirement);
226 return constraint.contains(version);
227 } catch (VersionParserException ex) {
228 return false;
229 }
230 }
231
232 @Override
233 public String toString() {
234 return version.toString();
235 }
236 }
237 }