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.getChild(KEY_JAVAHOME) : null;
80 if (javahome == null || javahome.getValue() == null) {
81 throw new ToolchainFactoryException(
82 "Java toolchain without the " + KEY_JAVAHOME + " configuration element.");
83 }
84 Path normal = Paths.get(javahome.getValue()).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 return new DefaultJavaToolchain(model, javaHome, matchers);
91 }
92
93 @Nonnull
94 @Override
95 public Optional<Toolchain> createDefaultToolchain() {
96 return Optional.empty();
97 }
98
99 static class DefaultJavaToolchain implements JavaToolchain {
100
101 final ToolchainModel model;
102 final String javaHome;
103 final Map<String, Predicate<String>> matchers;
104
105 DefaultJavaToolchain(ToolchainModel model, String javaHome, Map<String, Predicate<String>> matchers) {
106 this.model = model;
107 this.javaHome = javaHome;
108 this.matchers = matchers;
109 }
110
111 @Override
112 public String getJavaHome() {
113 return javaHome;
114 }
115
116 @Override
117 public String getType() {
118 return "jdk";
119 }
120
121 @Override
122 public ToolchainModel getModel() {
123 return model;
124 }
125
126 @Override
127 public String findTool(String toolName) {
128 Path toRet = findTool(toolName, Paths.get(getJavaHome()).normalize());
129 if (toRet != null) {
130 return toRet.toAbsolutePath().toString();
131 }
132 return null;
133 }
134
135 private static Path findTool(String toolName, Path installDir) {
136 Path bin = installDir.resolve("bin");
137 if (Files.isDirectory(bin)) {
138 if (Os.IS_WINDOWS) {
139 Path tool = bin.resolve(toolName + ".exe");
140 if (Files.exists(tool)) {
141 return tool;
142 }
143 }
144 Path tool = bin.resolve(toolName);
145 if (Files.exists(tool)) {
146 return tool;
147 }
148 }
149 return null;
150 }
151
152 @Override
153 public boolean matchesRequirements(Map<String, String> requirements) {
154 for (Map.Entry<String, String> requirement : requirements.entrySet()) {
155 String key = requirement.getKey();
156
157 Predicate<String> matcher = matchers.get(key);
158
159 if (matcher == null) {
160 LOGGER.debug("Toolchain {} is missing required property: {}", this, key);
161 return false;
162 }
163 if (!matcher.test(requirement.getValue())) {
164 LOGGER.debug("Toolchain {} doesn't match required property: {}", this, key);
165 return false;
166 }
167 }
168 return true;
169 }
170 }
171
172 static final class ExactMatcher implements Predicate<String> {
173
174 final String provides;
175
176 ExactMatcher(String provides) {
177 this.provides = provides;
178 }
179
180 @Override
181 public boolean test(String requirement) {
182 return provides.equalsIgnoreCase(requirement);
183 }
184
185 @Override
186 public String toString() {
187 return provides;
188 }
189 }
190
191 static final class VersionMatcher implements Predicate<String> {
192
193 final VersionParser versionParser;
194 final Version version;
195
196 VersionMatcher(VersionParser versionParser, String version) {
197 this.versionParser = versionParser;
198 this.version = versionParser.parseVersion(version);
199 }
200
201 @Override
202 public boolean test(String requirement) {
203 try {
204 VersionConstraint constraint = versionParser.parseVersionConstraint(requirement);
205 return constraint.contains(version);
206 } catch (VersionParserException ex) {
207 return false;
208 }
209 }
210
211 @Override
212 public String toString() {
213 return version.toString();
214 }
215 }
216 }