View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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"; // NOI18N
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          // populate the provides section
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          // compute and normalize the java home
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"); // NOI18N
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 }