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
37 import org.apache.maven.api.Constants;
38 import org.apache.maven.api.annotations.Nullable;
39 import org.apache.maven.api.cli.InvokerRequest;
40 import org.apache.maven.api.cli.Options;
41 import org.apache.maven.api.cli.Parser;
42 import org.apache.maven.api.cli.ParserException;
43 import org.apache.maven.api.cli.ParserRequest;
44 import org.apache.maven.api.cli.extensions.CoreExtension;
45 import org.apache.maven.cli.CLIReportingUtils;
46 import org.apache.maven.cli.internal.extension.io.CoreExtensionsStaxReader;
47 import org.apache.maven.cli.props.MavenPropertiesLoader;
48 import org.apache.maven.properties.internal.EnvironmentUtils;
49 import org.apache.maven.properties.internal.SystemProperties;
50
51 import static java.util.Objects.requireNonNull;
52 import static org.apache.maven.api.Constants.MAVEN_HOME;
53 import static org.apache.maven.api.Constants.MAVEN_INSTALLATION_CONF;
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<O extends Options, R extends InvokerRequest<O>> implements Parser<R> {
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 R parse(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<O> parsedOptions = parseCliOptions(context);
112
113
114 parsedOptions.forEach(o -> o.warnAboutDeprecatedOptions(
115 parserRequest, new PrintWriter(parserRequest.out() != null ? parserRequest.out() : System.out, true)));
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 R 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 return getCanonicalPath(Paths.get(System.getProperty("user.dir")));
143 }
144 }
145
146 protected Path getInstallationDirectory(LocalContext context) throws ParserException {
147 Path result;
148 if (context.parserRequest.mavenHome() != null) {
149 result = getCanonicalPath(context.parserRequest.mavenHome());
150 context.systemPropertiesOverrides.put(MAVEN_HOME, result.toString());
151 } else {
152 String mavenHome = System.getProperty(Constants.MAVEN_HOME);
153 if (mavenHome == null) {
154 throw new ParserException("local mode requires " + Constants.MAVEN_HOME + " Java System Property set");
155 }
156 result = getCanonicalPath(Paths.get(mavenHome));
157 }
158 return result;
159 }
160
161 protected Path getUserHomeDirectory(LocalContext context) throws ParserException {
162 if (context.parserRequest.userHome() != null) {
163 Path result = getCanonicalPath(context.parserRequest.userHome());
164 context.systemPropertiesOverrides.put("user.home", result.toString());
165 return result;
166 } else {
167 return getCanonicalPath(Paths.get(System.getProperty("user.home")));
168 }
169 }
170
171 protected Path getTopDirectory(LocalContext context) throws ParserException {
172
173
174 Path topDirectory = requireNonNull(context.cwd);
175 boolean isAltFile = false;
176 for (String arg : context.parserRequest.args()) {
177 if (isAltFile) {
178
179 Path path = topDirectory.resolve(stripLeadingAndTrailingQuotes(arg));
180 if (Files.isDirectory(path)) {
181 topDirectory = path;
182 } else if (Files.isRegularFile(path)) {
183 topDirectory = path.getParent();
184 if (!Files.isDirectory(topDirectory)) {
185 throw new ParserException("Directory " + topDirectory
186 + " extracted from the -f/--file command-line argument " + arg + " does not exist");
187 }
188 } else {
189 throw new ParserException(
190 "POM file " + arg + " specified with the -f/--file command line argument does not exist");
191 }
192 break;
193 } else {
194
195 isAltFile = arg.equals("-f") || arg.equals("--file");
196 }
197 }
198 return getCanonicalPath(topDirectory);
199 }
200
201 @Nullable
202 protected Path getRootDirectory(LocalContext context) throws ParserException {
203 return Utils.findRoot(context.topDirectory);
204 }
205
206 protected Map<String, String> populateSystemProperties(LocalContext context) throws ParserException {
207 Properties systemProperties = new Properties();
208
209
210
211
212
213 EnvironmentUtils.addEnvVars(systemProperties);
214 SystemProperties.addSystemProperties(systemProperties);
215
216
217
218
219
220
221 Properties buildProperties = CLIReportingUtils.getBuildProperties();
222
223 String mavenVersion = buildProperties.getProperty(CLIReportingUtils.BUILD_VERSION_PROPERTY);
224 systemProperties.setProperty("maven.version", mavenVersion);
225
226 String mavenBuildVersion = CLIReportingUtils.createMavenVersionString(buildProperties);
227 systemProperties.setProperty("maven.build.version", mavenBuildVersion);
228
229 Map<String, String> result = toMap(systemProperties);
230 result.putAll(context.systemPropertiesOverrides);
231 return result;
232 }
233
234 protected Map<String, String> populateUserProperties(LocalContext context) throws ParserException, IOException {
235 Properties userProperties = new Properties();
236
237
238
239
240
241
242
243 Map<String, String> userSpecifiedProperties =
244 context.options.userProperties().orElse(new HashMap<>());
245
246
247
248
249 Map<String, String> paths = context.extraInterpolationSource();
250 Function<String, String> callback =
251 or(paths::get, prefix("cli.", userSpecifiedProperties::get), context.systemProperties::get);
252
253 Path mavenConf;
254 if (context.systemProperties.get(MAVEN_INSTALLATION_CONF) != null) {
255 mavenConf = context.installationDirectory.resolve(context.systemProperties.get(MAVEN_INSTALLATION_CONF));
256 } else if (context.systemProperties.get("maven.conf") != null) {
257 mavenConf = context.installationDirectory.resolve(context.systemProperties.get("maven.conf"));
258 } else if (context.systemProperties.get(MAVEN_HOME) != null) {
259 mavenConf = context.installationDirectory
260 .resolve(context.systemProperties.get(MAVEN_HOME))
261 .resolve("conf");
262 } else {
263 mavenConf = context.installationDirectory.resolve("");
264 }
265 Path propertiesFile = mavenConf.resolve("maven.properties");
266 MavenPropertiesLoader.loadProperties(userProperties, propertiesFile, callback, false);
267
268
269 userProperties.putAll(userSpecifiedProperties);
270
271 return toMap(userProperties);
272 }
273
274 protected abstract List<O> parseCliOptions(LocalContext context) throws ParserException, IOException;
275
276 protected abstract O assembleOptions(List<O> parsedOptions);
277
278 protected List<CoreExtension> readCoreExtensionsDescriptor(LocalContext context)
279 throws ParserException, IOException {
280 ArrayList<CoreExtension> extensions = new ArrayList<>();
281 String installationExtensionsFile = context.userProperties.get(Constants.MAVEN_INSTALLATION_EXTENSIONS);
282 extensions.addAll(readCoreExtensionsDescriptorFromFile(
283 context.installationDirectory.resolve(installationExtensionsFile)));
284
285 String projectExtensionsFile = context.userProperties.get(Constants.MAVEN_PROJECT_EXTENSIONS);
286 extensions.addAll(readCoreExtensionsDescriptorFromFile(context.cwd.resolve(projectExtensionsFile)));
287
288 String userExtensionsFile = context.userProperties.get(Constants.MAVEN_USER_EXTENSIONS);
289 extensions.addAll(readCoreExtensionsDescriptorFromFile(context.userHomeDirectory.resolve(userExtensionsFile)));
290
291 return extensions;
292 }
293
294 protected List<CoreExtension> readCoreExtensionsDescriptorFromFile(Path extensionsFile)
295 throws ParserException, IOException {
296 try {
297 if (extensionsFile != null && Files.exists(extensionsFile)) {
298 try (InputStream is = Files.newInputStream(extensionsFile)) {
299 return new CoreExtensionsStaxReader().read(is, true).getExtensions();
300 }
301 }
302 return List.of();
303 } catch (XMLStreamException e) {
304 throw new ParserException("Failed to parse extensions file: " + extensionsFile, e);
305 }
306 }
307 }