1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cling.invoker;
20
21 import javax.xml.stream.XMLStreamException;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.PrintWriter;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Properties;
36 import java.util.function.Function;
37 import java.util.stream.Collectors;
38
39 import org.apache.maven.api.Constants;
40 import org.apache.maven.api.annotations.Nullable;
41 import org.apache.maven.api.cli.InvokerRequest;
42 import org.apache.maven.api.cli.Options;
43 import org.apache.maven.api.cli.Parser;
44 import org.apache.maven.api.cli.ParserException;
45 import org.apache.maven.api.cli.ParserRequest;
46 import org.apache.maven.api.cli.extensions.CoreExtension;
47 import org.apache.maven.cling.internal.extension.io.CoreExtensionsStaxReader;
48 import org.apache.maven.cling.props.MavenPropertiesLoader;
49 import org.apache.maven.cling.utils.CLIReportingUtils;
50 import org.apache.maven.properties.internal.EnvironmentUtils;
51 import org.apache.maven.properties.internal.SystemProperties;
52
53 import static java.util.Objects.requireNonNull;
54 import static org.apache.maven.cling.invoker.Utils.getCanonicalPath;
55 import static org.apache.maven.cling.invoker.Utils.or;
56 import static org.apache.maven.cling.invoker.Utils.prefix;
57 import static org.apache.maven.cling.invoker.Utils.stripLeadingAndTrailingQuotes;
58 import static org.apache.maven.cling.invoker.Utils.toMap;
59
60 public abstract class BaseParser implements Parser {
61
62 @SuppressWarnings("VisibilityModifier")
63 public static class LocalContext {
64 public final ParserRequest parserRequest;
65 public final Map<String, String> systemPropertiesOverrides;
66
67 public LocalContext(ParserRequest parserRequest) {
68 this.parserRequest = parserRequest;
69 this.systemPropertiesOverrides = new HashMap<>();
70 }
71
72 public Path cwd;
73 public Path installationDirectory;
74 public Path userHomeDirectory;
75 public Map<String, String> systemProperties;
76 public Map<String, String> userProperties;
77 public Path topDirectory;
78
79 @Nullable
80 public Path rootDirectory;
81
82 public List<CoreExtension> extensions;
83 public Options options;
84
85 public Map<String, String> extraInterpolationSource() {
86 Map<String, String> extra = new HashMap<>();
87 extra.put("session.topDirectory", topDirectory.toString());
88 if (rootDirectory != null) {
89 extra.put("session.rootDirectory", rootDirectory.toString());
90 }
91 return extra;
92 }
93 }
94
95 @Override
96 public InvokerRequest parseInvocation(ParserRequest parserRequest) throws ParserException, IOException {
97 requireNonNull(parserRequest);
98
99 LocalContext context = new LocalContext(parserRequest);
100
101
102 context.cwd = requireNonNull(getCwd(context));
103 context.installationDirectory = requireNonNull(getInstallationDirectory(context));
104 context.userHomeDirectory = requireNonNull(getUserHomeDirectory(context));
105
106
107 context.topDirectory = requireNonNull(getTopDirectory(context));
108 context.rootDirectory = getRootDirectory(context);
109
110
111 List<Options> parsedOptions = parseCliOptions(context);
112
113
114 PrintWriter printWriter = new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true);
115 parsedOptions.forEach(o -> o.warnAboutDeprecatedOptions(parserRequest, printWriter::println));
116
117
118 context.options = assembleOptions(parsedOptions);
119
120
121 context.systemProperties = populateSystemProperties(context);
122 context.userProperties = populateUserProperties(context);
123
124
125 context.options = context.options.interpolate(
126 Arrays.asList(context.extraInterpolationSource(), context.userProperties, context.systemProperties));
127
128
129 context.extensions = readCoreExtensionsDescriptor(context);
130
131 return getInvokerRequest(context);
132 }
133
134 protected abstract InvokerRequest getInvokerRequest(LocalContext context);
135
136 protected Path getCwd(LocalContext context) throws ParserException {
137 if (context.parserRequest.cwd() != null) {
138 Path result = getCanonicalPath(context.parserRequest.cwd());
139 context.systemPropertiesOverrides.put("user.dir", result.toString());
140 return result;
141 } else {
142 Path result = getCanonicalPath(Paths.get(System.getProperty("user.dir")));
143 mayOverrideDirectorySystemProperty(context, "user.dir", result);
144 return result;
145 }
146 }
147
148 protected Path getInstallationDirectory(LocalContext context) throws ParserException {
149 if (context.parserRequest.mavenHome() != null) {
150 Path result = getCanonicalPath(context.parserRequest.mavenHome());
151 context.systemPropertiesOverrides.put(Constants.MAVEN_HOME, result.toString());
152 return result;
153 } else {
154 String mavenHome = System.getProperty(Constants.MAVEN_HOME);
155 if (mavenHome == null) {
156 throw new ParserException("local mode requires " + Constants.MAVEN_HOME + " Java System Property set");
157 }
158 Path result = getCanonicalPath(Paths.get(mavenHome));
159 mayOverrideDirectorySystemProperty(context, Constants.MAVEN_HOME, result);
160 return result;
161 }
162 }
163
164 protected Path getUserHomeDirectory(LocalContext context) throws ParserException {
165 if (context.parserRequest.userHome() != null) {
166 Path result = getCanonicalPath(context.parserRequest.userHome());
167 context.systemPropertiesOverrides.put("user.home", result.toString());
168 return result;
169 } else {
170 Path result = getCanonicalPath(Paths.get(System.getProperty("user.home")));
171 mayOverrideDirectorySystemProperty(context, "user.home", result);
172 return result;
173 }
174 }
175
176
177
178
179
180 protected void mayOverrideDirectorySystemProperty(LocalContext context, String javaSystemPropertyKey, Path value) {
181 String valueString = value.toString();
182 if (!Objects.equals(System.getProperty(javaSystemPropertyKey), valueString)) {
183 context.systemPropertiesOverrides.put(javaSystemPropertyKey, valueString);
184 }
185 }
186
187 protected Path getTopDirectory(LocalContext context) throws ParserException {
188
189
190 Path topDirectory = requireNonNull(context.cwd);
191 boolean isAltFile = false;
192 for (String arg : context.parserRequest.args()) {
193 if (isAltFile) {
194
195 Path path = topDirectory.resolve(stripLeadingAndTrailingQuotes(arg));
196 if (Files.isDirectory(path)) {
197 topDirectory = path;
198 } else if (Files.isRegularFile(path)) {
199 topDirectory = path.getParent();
200 if (!Files.isDirectory(topDirectory)) {
201 throw new ParserException("Directory " + topDirectory
202 + " extracted from the -f/--file command-line argument " + arg + " does not exist");
203 }
204 } else {
205 throw new ParserException(
206 "POM file " + arg + " specified with the -f/--file command line argument does not exist");
207 }
208 break;
209 } else {
210
211 isAltFile = arg.equals("-f") || arg.equals("--file");
212 }
213 }
214 return getCanonicalPath(topDirectory);
215 }
216
217 @Nullable
218 protected Path getRootDirectory(LocalContext context) throws ParserException {
219 return Utils.findRoot(context.topDirectory);
220 }
221
222 protected Map<String, String> populateSystemProperties(LocalContext context) throws ParserException {
223 Properties systemProperties = new Properties();
224
225
226
227
228
229 EnvironmentUtils.addEnvVars(systemProperties);
230 SystemProperties.addSystemProperties(systemProperties);
231
232
233
234
235
236
237 Properties buildProperties = CLIReportingUtils.getBuildProperties();
238
239 String mavenVersion = buildProperties.getProperty(CLIReportingUtils.BUILD_VERSION_PROPERTY);
240 systemProperties.setProperty("maven.version", mavenVersion);
241
242 String mavenBuildVersion = CLIReportingUtils.createMavenVersionString(buildProperties);
243 systemProperties.setProperty("maven.build.version", mavenBuildVersion);
244
245 Map<String, String> result = toMap(systemProperties);
246 result.putAll(context.systemPropertiesOverrides);
247 return result;
248 }
249
250 protected Map<String, String> populateUserProperties(LocalContext context) throws ParserException, IOException {
251 Properties userProperties = new Properties();
252
253
254
255
256
257
258
259 Map<String, String> userSpecifiedProperties =
260 context.options.userProperties().orElse(new HashMap<>());
261
262
263
264
265 Map<String, String> paths = context.extraInterpolationSource();
266 Function<String, String> callback =
267 or(paths::get, prefix("cli.", userSpecifiedProperties::get), context.systemProperties::get);
268
269 Path mavenConf;
270 if (context.systemProperties.get(Constants.MAVEN_INSTALLATION_CONF) != null) {
271 mavenConf = context.installationDirectory.resolve(
272 context.systemProperties.get(Constants.MAVEN_INSTALLATION_CONF));
273 } else if (context.systemProperties.get("maven.conf") != null) {
274 mavenConf = context.installationDirectory.resolve(context.systemProperties.get("maven.conf"));
275 } else if (context.systemProperties.get(Constants.MAVEN_HOME) != null) {
276 mavenConf = context.installationDirectory
277 .resolve(context.systemProperties.get(Constants.MAVEN_HOME))
278 .resolve("conf");
279 } else {
280 mavenConf = context.installationDirectory.resolve("");
281 }
282 Path propertiesFile = mavenConf.resolve("maven.properties");
283 MavenPropertiesLoader.loadProperties(userProperties, propertiesFile, callback, false);
284
285
286 userProperties.putAll(userSpecifiedProperties);
287
288 return toMap(userProperties);
289 }
290
291 protected abstract List<Options> parseCliOptions(LocalContext context) throws ParserException, IOException;
292
293 protected abstract Options assembleOptions(List<Options> parsedOptions);
294
295 protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext context)
296 throws ParserException, IOException {
297 ArrayList<CoreExtension> extensions = new ArrayList<>();
298 String installationExtensionsFile = context.userProperties.get(Constants.MAVEN_INSTALLATION_EXTENSIONS);
299 extensions.addAll(readCoreExtensionsDescriptorFromFile(
300 context.installationDirectory.resolve(installationExtensionsFile)));
301
302 String projectExtensionsFile = context.userProperties.get(Constants.MAVEN_PROJECT_EXTENSIONS);
303 extensions.addAll(readCoreExtensionsDescriptorFromFile(context.cwd.resolve(projectExtensionsFile)));
304
305 String userExtensionsFile = context.userProperties.get(Constants.MAVEN_USER_EXTENSIONS);
306 extensions.addAll(readCoreExtensionsDescriptorFromFile(context.userHomeDirectory.resolve(userExtensionsFile)));
307
308 return extensions;
309 }
310
311 protected List<CoreExtension> readCoreExtensionsDescriptorFromFile(Path extensionsFile)
312 throws ParserException, IOException {
313 try {
314 if (extensionsFile != null && Files.exists(extensionsFile)) {
315 try (InputStream is = Files.newInputStream(extensionsFile)) {
316 return new CoreExtensionsStaxReader().read(is, true).getExtensions();
317 }
318 }
319 return List.of();
320 } catch (XMLStreamException e) {
321 throw new ParserException("Failed to parse extensions file: " + extensionsFile, e);
322 }
323 }
324
325 protected List<String> getJvmArguments(Path rootDirectory) throws ParserException {
326 if (rootDirectory != null) {
327 Path jvmConfig = rootDirectory.resolve(".mvn/jvm.config");
328 if (Files.exists(jvmConfig)) {
329 try {
330 return Files.readAllLines(jvmConfig).stream()
331 .filter(l -> !l.isBlank() && !l.startsWith("#"))
332 .flatMap(l -> Arrays.stream(l.split(" ")))
333 .collect(Collectors.toList());
334 } catch (IOException e) {
335 throw new ParserException("Failed to read JVM configuration file: " + jvmConfig, e);
336 }
337 }
338 }
339 return null;
340 }
341 }