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 }