1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.invoker;
20
21 import java.io.File;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Objects;
28 import java.util.Optional;
29 import java.util.Properties;
30 import java.util.function.Consumer;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import org.apache.maven.shared.invoker.InvocationRequest;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38
39
40
41
42
43 class InvokerProperties {
44
45 private final Logger logger = LoggerFactory.getLogger(InvokerProperties.class);
46
47 private static final String SELECTOR_PREFIX = "selector.";
48
49 private static final Pattern ENVIRONMENT_VARIABLES_PATTERN =
50 Pattern.compile("invoker\\.environmentVariables\\.([A-Za-z][^.]+)(\\.(\\d+))?");
51
52
53 private Boolean defaultDebug;
54 private Boolean defaultQuiet;
55 private List<String> defaultGoals;
56 private List<String> defaultProfiles;
57 private String defaultMavenOpts;
58 private Integer defaultTimeoutInSeconds;
59 private Map<String, String> defaultEnvironmentVariables;
60 private File defaultMavenExecutable;
61 private Boolean defaultUpdateSnapshots;
62 private String defaultUserPropertiesFiles;
63
64 private enum InvocationProperty {
65 PROJECT("invoker.project"),
66 BUILD_RESULT("invoker.buildResult"),
67 GOALS("invoker.goals"),
68 PROFILES("invoker.profiles"),
69 MAVEN_EXECUTABLE("invoker.mavenExecutable"),
70 MAVEN_OPTS("invoker.mavenOpts"),
71 FAILURE_BEHAVIOR("invoker.failureBehavior"),
72 NON_RECURSIVE("invoker.nonRecursive"),
73 OFFLINE("invoker.offline"),
74 SYSTEM_PROPERTIES_FILE("invoker.systemPropertiesFile"),
75 USER_PROPERTIES_FILE("invoker.userPropertiesFile"),
76 DEBUG("invoker.debug"),
77 QUIET("invoker.quiet"),
78 SETTINGS_FILE("invoker.settingsFile"),
79 TIMEOUT_IN_SECONDS("invoker.timeoutInSeconds"),
80 UPDATE_SNAPSHOTS("invoker.updateSnapshots");
81
82 private final String key;
83
84 InvocationProperty(final String s) {
85 this.key = s;
86 }
87
88 @Override
89 public String toString() {
90 return key;
91 }
92 }
93
94 private enum SelectorProperty {
95 JAVA_VERSION(".java.version"),
96 MAVEN_VERSION(".maven.version"),
97 OS_FAMILY(".os.family");
98
99 private final String suffix;
100
101 SelectorProperty(String suffix) {
102 this.suffix = suffix;
103 }
104
105 @Override
106 public String toString() {
107 return suffix;
108 }
109 }
110
111
112
113
114 private final Properties properties;
115
116
117
118
119
120
121
122 InvokerProperties(Properties properties) {
123 this.properties = (properties != null) ? properties : new Properties();
124 }
125
126
127
128
129
130 public void setDefaultDebug(boolean defaultDebug) {
131 this.defaultDebug = defaultDebug;
132 }
133
134
135
136
137
138 public void setDefaultQuiet(boolean defaultQuiet) {
139 this.defaultQuiet = defaultQuiet;
140 }
141
142
143
144
145
146 public void setDefaultGoals(List<String> defaultGoals) {
147 this.defaultGoals = defaultGoals;
148 }
149
150
151
152
153
154 public void setDefaultProfiles(List<String> defaultProfiles) {
155 this.defaultProfiles = defaultProfiles;
156 }
157
158
159
160
161
162 public void setDefaultMavenExecutable(String defaultMavenExecutable) {
163 if (Objects.nonNull(defaultMavenExecutable) && !defaultMavenExecutable.isEmpty()) {
164 this.defaultMavenExecutable = new File(defaultMavenExecutable);
165 }
166 }
167
168
169
170
171
172 public void setDefaultMavenOpts(String defaultMavenOpts) {
173 this.defaultMavenOpts = defaultMavenOpts;
174 }
175
176
177
178
179
180 public void setDefaultTimeoutInSeconds(int defaultTimeoutInSeconds) {
181 this.defaultTimeoutInSeconds = defaultTimeoutInSeconds;
182 }
183
184
185
186
187
188 public void setDefaultEnvironmentVariables(Map<String, String> defaultEnvironmentVariables) {
189 this.defaultEnvironmentVariables = defaultEnvironmentVariables;
190 }
191
192
193
194
195
196 public void setDefaultUpdateSnapshots(boolean defaultUpdateSnapshots) {
197 this.defaultUpdateSnapshots = defaultUpdateSnapshots;
198 }
199
200
201
202
203
204 public void setDefaultUserPropertiesFiles(String defaultUserPropertiesFiles) {
205 this.defaultUserPropertiesFiles = defaultUserPropertiesFiles;
206 }
207
208
209
210
211
212
213 public Properties getProperties() {
214 return this.properties;
215 }
216
217
218
219
220
221
222 public String getJobName() {
223 return this.properties.getProperty("invoker.name", "");
224 }
225
226
227
228
229
230
231 public String getJobDescription() {
232 return this.properties.getProperty("invoker.description", "");
233 }
234
235
236
237
238
239
240 public int getOrdinal() {
241 return Integer.parseInt(this.properties.getProperty("invoker.ordinal", "0"));
242 }
243
244
245
246
247
248
249 public String getJreVersion() {
250 return this.properties.getProperty("invoker.java.version", "");
251 }
252
253
254
255
256
257
258 public String getJreVersion(int index) {
259 return this.properties.getProperty(SELECTOR_PREFIX + index + SelectorProperty.JAVA_VERSION, getJreVersion());
260 }
261
262
263
264
265
266
267
268 public String getMavenVersion() {
269 return this.properties.getProperty("invoker.maven.version", "");
270 }
271
272
273
274
275
276
277 public String getMavenVersion(int index) {
278 return this.properties.getProperty(SELECTOR_PREFIX + index + SelectorProperty.MAVEN_VERSION, getMavenVersion());
279 }
280
281
282
283
284
285
286 public String getOsFamily() {
287 return this.properties.getProperty("invoker.os.family", "");
288 }
289
290
291
292
293
294
295
296
297 public String getOsFamily(int index) {
298 return this.properties.getProperty(SELECTOR_PREFIX + index + SelectorProperty.OS_FAMILY, getOsFamily());
299 }
300
301 public Collection<InvokerToolchain> getToolchains() {
302 return getToolchains(Pattern.compile("invoker\\.toolchain\\.([^.]+)\\.(.+)"));
303 }
304
305 public Collection<InvokerToolchain> getToolchains(int index) {
306 return getToolchains(Pattern.compile("selector\\." + index + "\\.invoker\\.toolchain\\.([^.]+)\\.(.+)"));
307 }
308
309 private Collection<InvokerToolchain> getToolchains(Pattern p) {
310 Map<String, InvokerToolchain> toolchains = new HashMap<>();
311 for (Map.Entry<Object, Object> entry : this.properties.entrySet()) {
312 Matcher m = p.matcher(entry.getKey().toString());
313 if (m.matches()) {
314 String type = m.group(1);
315 String providesKey = m.group(2);
316 String providesValue = entry.getValue().toString();
317
318 InvokerToolchain tc = toolchains.get(type);
319 if (tc == null) {
320 tc = new InvokerToolchain(type);
321 toolchains.put(type, tc);
322 }
323 tc.addProvides(providesKey, providesValue);
324 }
325 }
326 return toolchains.values();
327 }
328
329
330
331
332
333
334
335
336 private Map<String, String> getEnvironmentVariables(int index) {
337
338 Map<String, String> envItems = new HashMap<>();
339
340 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
341 Matcher matcher =
342 ENVIRONMENT_VARIABLES_PATTERN.matcher(entry.getKey().toString());
343 if (matcher.matches()) {
344
345 if (String.valueOf(index).equals(matcher.group(3))) {
346
347 envItems.put(matcher.group(1), entry.getValue().toString());
348 } else if (matcher.group(3) == null) {
349
350 if (!envItems.containsKey(matcher.group(1))) {
351 envItems.put(matcher.group(1), entry.getValue().toString());
352 }
353 }
354 }
355 }
356 return envItems;
357 }
358
359
360
361
362
363
364
365 public boolean isInvocationDefined(int index) {
366 return Arrays.stream(InvocationProperty.values())
367 .map(InvocationProperty::toString)
368 .map(v -> properties.getProperty(v + '.' + index))
369 .anyMatch(Objects::nonNull);
370 }
371
372
373
374
375
376
377
378
379 public boolean isSelectorDefined(int index) {
380 return Arrays.stream(SelectorProperty.values())
381 .map(v -> v.suffix)
382 .map(v -> properties.getProperty(SELECTOR_PREFIX + index + v))
383 .anyMatch(Objects::nonNull);
384 }
385
386 private <T> void setIfNotNull(Consumer<T> consumer, T value) {
387 if (value != null) {
388 consumer.accept(value);
389 }
390 }
391
392
393
394
395
396
397
398
399 public void configureInvocation(InvocationRequest request, int index) {
400 get(InvocationProperty.PROJECT, index).ifPresent(project -> {
401 File file = new File(request.getBaseDirectory(), project);
402 if (file.isFile()) {
403 request.setBaseDirectory(file.getParentFile());
404 request.setPomFile(file);
405 } else {
406 request.setBaseDirectory(file);
407 request.setPomFile(null);
408 }
409 });
410
411 setIfNotNull(
412 request::setGoals,
413 get(InvocationProperty.GOALS, index)
414 .map(s -> s.trim().split("\\s*[ ,]+\\s*"))
415 .map(Arrays::asList)
416 .filter(l -> !l.isEmpty())
417 .orElse(defaultGoals));
418
419 setIfNotNull(
420 request::setProfiles,
421 get(InvocationProperty.PROFILES, index)
422 .map(s -> s.trim().split("\\s*[ ,]+\\s*"))
423 .map(Arrays::asList)
424 .filter(l -> !l.isEmpty())
425 .orElse(defaultProfiles));
426
427 setIfNotNull(
428 request::setMavenExecutable,
429 get(InvocationProperty.MAVEN_EXECUTABLE, index).map(File::new).orElse(defaultMavenExecutable));
430
431 setIfNotNull(
432 request::setMavenOpts, get(InvocationProperty.MAVEN_OPTS, index).orElse(defaultMavenOpts));
433
434 get(InvocationProperty.FAILURE_BEHAVIOR, index)
435 .map(InvocationRequest.ReactorFailureBehavior::valueOfByLongOption)
436 .ifPresent(request::setReactorFailureBehavior);
437
438 get(InvocationProperty.NON_RECURSIVE, index)
439 .map(Boolean::parseBoolean)
440 .map(b -> !b)
441 .ifPresent(request::setRecursive);
442
443 get(InvocationProperty.OFFLINE, index).map(Boolean::parseBoolean).ifPresent(request::setOffline);
444
445 setIfNotNull(
446 request::setDebug,
447 get(InvocationProperty.DEBUG, index).map(Boolean::parseBoolean).orElse(defaultDebug));
448
449 setIfNotNull(
450 request::setQuiet,
451 get(InvocationProperty.QUIET, index).map(Boolean::parseBoolean).orElse(defaultQuiet));
452
453 setIfNotNull(
454 request::setTimeoutInSeconds,
455 get(InvocationProperty.TIMEOUT_IN_SECONDS, index)
456 .map(Integer::parseInt)
457 .orElse(defaultTimeoutInSeconds));
458
459 setIfNotNull(
460 request::setUpdateSnapshots,
461 get(InvocationProperty.UPDATE_SNAPSHOTS, index)
462 .map(Boolean::parseBoolean)
463 .orElse(defaultUpdateSnapshots));
464
465 Optional.ofNullable(defaultEnvironmentVariables).ifPresent(evn -> evn.forEach(request::addShellEnvironment));
466
467 getEnvironmentVariables(index).forEach(request::addShellEnvironment);
468 }
469
470
471
472
473
474
475
476
477
478 public boolean isExpectedResult(int exitCode, int index) {
479 boolean nonZeroExit = "failure"
480 .equalsIgnoreCase(get(InvocationProperty.BUILD_RESULT, index).orElse(null));
481 return (exitCode != 0) == nonZeroExit;
482 }
483
484
485
486
487
488
489
490 public String getUserPropertiesFile(int index) {
491 Optional<String> userProperties = get(InvocationProperty.USER_PROPERTIES_FILE, index);
492 Optional<String> systemProperties = get(InvocationProperty.SYSTEM_PROPERTIES_FILE, index);
493
494 if (userProperties.isPresent() && systemProperties.isPresent()) {
495 throw new IllegalArgumentException("only one property '" + InvocationProperty.USER_PROPERTIES_FILE
496 + "' or '" + InvocationProperty.SYSTEM_PROPERTIES_FILE + "' can be used");
497 }
498
499 if (userProperties.isPresent()) {
500 return userProperties.get();
501 }
502
503 if (systemProperties.isPresent()) {
504 logger.warn(
505 "property {} is deprecated - please use {}",
506 InvocationProperty.SYSTEM_PROPERTIES_FILE,
507 InvocationProperty.USER_PROPERTIES_FILE);
508 return systemProperties.get();
509 }
510
511 return defaultUserPropertiesFiles;
512 }
513
514
515
516
517
518
519
520 public String getSettingsFile(int index) {
521 return get(InvocationProperty.SETTINGS_FILE, index).orElse(null);
522 }
523
524
525
526
527
528
529
530
531
532
533
534 Optional<String> get(String key, int index) {
535 if (index < 0) {
536 throw new IllegalArgumentException("invalid invocation index: " + index);
537 }
538
539
540 String value = Optional.ofNullable(properties.getProperty(key + '.' + index))
541 .orElseGet(() -> properties.getProperty(key));
542
543 return Optional.ofNullable(value).map(String::trim).filter(s -> !s.isEmpty());
544 }
545
546 private Optional<String> get(InvocationProperty prop, int index) {
547 return get(prop.toString(), index);
548 }
549 }