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