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 java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.io.PrintStream;
26 import java.io.PrintWriter;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.Objects;
37 import java.util.Properties;
38 import java.util.function.Consumer;
39 import java.util.function.UnaryOperator;
40
41 import org.apache.maven.api.Constants;
42 import org.apache.maven.api.ProtoSession;
43 import org.apache.maven.api.annotations.Nullable;
44 import org.apache.maven.api.cli.Invoker;
45 import org.apache.maven.api.cli.InvokerException;
46 import org.apache.maven.api.cli.InvokerRequest;
47 import org.apache.maven.api.cli.Logger;
48 import org.apache.maven.api.cli.Options;
49 import org.apache.maven.api.cli.logging.AccumulatingLogger;
50 import org.apache.maven.api.services.BuilderProblem;
51 import org.apache.maven.api.services.Interpolator;
52 import org.apache.maven.api.services.Lookup;
53 import org.apache.maven.api.services.MavenException;
54 import org.apache.maven.api.services.MessageBuilder;
55 import org.apache.maven.api.services.SettingsBuilder;
56 import org.apache.maven.api.services.SettingsBuilderRequest;
57 import org.apache.maven.api.services.SettingsBuilderResult;
58 import org.apache.maven.api.services.Sources;
59 import org.apache.maven.api.settings.Mirror;
60 import org.apache.maven.api.settings.Profile;
61 import org.apache.maven.api.settings.Proxy;
62 import org.apache.maven.api.settings.Repository;
63 import org.apache.maven.api.settings.Server;
64 import org.apache.maven.api.settings.Settings;
65 import org.apache.maven.api.spi.PropertyContributor;
66 import org.apache.maven.artifact.repository.ArtifactRepository;
67 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
68 import org.apache.maven.artifact.repository.MavenArtifactRepository;
69 import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
70 import org.apache.maven.bridge.MavenRepositorySystem;
71 import org.apache.maven.cling.invoker.logging.Slf4jLogger;
72 import org.apache.maven.cling.invoker.logging.SystemLogger;
73 import org.apache.maven.cling.invoker.spi.PropertyContributorsHolder;
74 import org.apache.maven.cling.logging.Slf4jConfiguration;
75 import org.apache.maven.cling.logging.Slf4jConfigurationFactory;
76 import org.apache.maven.cling.utils.CLIReportingUtils;
77 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
78 import org.apache.maven.execution.MavenExecutionRequest;
79 import org.apache.maven.impl.SettingsUtilsV4;
80 import org.apache.maven.jline.FastTerminal;
81 import org.apache.maven.jline.MessageUtils;
82 import org.apache.maven.logging.BuildEventListener;
83 import org.apache.maven.logging.LoggingOutputStream;
84 import org.apache.maven.logging.ProjectBuildLogAppender;
85 import org.apache.maven.logging.SimpleBuildEventListener;
86 import org.apache.maven.logging.api.LogLevelRecorder;
87 import org.apache.maven.slf4j.MavenSimpleLogger;
88 import org.codehaus.plexus.PlexusContainer;
89 import org.jline.terminal.Terminal;
90 import org.jline.terminal.TerminalBuilder;
91 import org.jline.terminal.impl.AbstractPosixTerminal;
92 import org.jline.terminal.spi.TerminalExt;
93 import org.slf4j.LoggerFactory;
94 import org.slf4j.spi.LocationAwareLogger;
95
96 import static java.util.Objects.requireNonNull;
97 import static org.apache.maven.cling.invoker.Utils.toMavenExecutionRequestLoggingLevel;
98 import static org.apache.maven.cling.invoker.Utils.toProperties;
99
100
101
102
103
104
105 public abstract class LookupInvoker<C extends LookupContext> implements Invoker {
106 protected final Lookup protoLookup;
107
108 @Nullable
109 protected final Consumer<LookupContext> contextConsumer;
110
111 public LookupInvoker(Lookup protoLookup, @Nullable Consumer<LookupContext> contextConsumer) {
112 this.protoLookup = requireNonNull(protoLookup);
113 this.contextConsumer = contextConsumer;
114 }
115
116 @Override
117 public final int invoke(InvokerRequest invokerRequest) {
118 requireNonNull(invokerRequest);
119
120 Properties oldProps = new Properties();
121 oldProps.putAll(System.getProperties());
122 ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
123 try (C context = createContext(invokerRequest)) {
124 if (contextConsumer != null) {
125 contextConsumer.accept(context);
126 }
127 try {
128 if (context.containerCapsule != null
129 && context.containerCapsule.currentThreadClassLoader().isPresent()) {
130 Thread.currentThread()
131 .setContextClassLoader(context.containerCapsule
132 .currentThreadClassLoader()
133 .get());
134 }
135 return doInvoke(context);
136 } catch (InvokerException.ExitException e) {
137
138 throw e;
139 } catch (Exception e) {
140
141 throw handleException(context, e);
142 }
143 } finally {
144 Thread.currentThread().setContextClassLoader(oldCL);
145 System.setProperties(oldProps);
146 }
147 }
148
149 protected int doInvoke(C context) throws Exception {
150 validate(context);
151 pushCoreProperties(context);
152 pushUserProperties(context);
153 configureLogging(context);
154 createTerminal(context);
155 activateLogging(context);
156 helpOrVersionAndMayExit(context);
157 preCommands(context);
158 container(context);
159 postContainer(context);
160 pushUserProperties(context);
161 lookup(context);
162 init(context);
163 postCommands(context);
164 settings(context);
165 return execute(context);
166 }
167
168 protected InvokerException.ExitException handleException(C context, Exception e) {
169 printErrors(
170 context,
171 context.invokerRequest.options().showErrors().orElse(false),
172 List.of(new Logger.Entry(Logger.Level.ERROR, e.getMessage(), e.getCause())),
173 context.logger);
174 return new InvokerException.ExitException(2);
175 }
176
177 protected void printErrors(C context, boolean showStackTrace, List<Logger.Entry> entries, Logger logger) {
178
179 if (logger instanceof AccumulatingLogger) {
180 logger = new SystemLogger(context.invokerRequest.stdErr().orElse(null));
181 }
182
183 logger.error("Error executing " + context.invokerRequest.parserRequest().commandName() + ".");
184 for (Logger.Entry entry : entries) {
185 if (showStackTrace) {
186 logger.log(entry.level(), entry.message(), entry.error());
187 } else {
188 logger.error(entry.message());
189 for (Throwable cause = entry.error();
190 cause != null && cause != cause.getCause();
191 cause = cause.getCause()) {
192 logger.log(entry.level(), "Caused by: " + cause.getMessage());
193 }
194 }
195 }
196 }
197
198 protected abstract C createContext(InvokerRequest invokerRequest);
199
200 protected void validate(C context) throws Exception {
201 if (context.invokerRequest.parsingFailed()) {
202
203
204 List<Logger.Entry> entries =
205 context.invokerRequest.parserRequest().logger().drain();
206 printErrors(
207 context,
208 context.invokerRequest
209 .parserRequest()
210 .args()
211 .contains(CommonsCliOptions.CLIManager.SHOW_ERRORS_CLI_ARG),
212 entries,
213 context.logger);
214
215 throw new InvokerException.ExitException(1);
216 }
217
218
219 context.invokerRequest
220 .options()
221 .warnAboutDeprecatedOptions(context.invokerRequest.parserRequest(), context.logger::warn);
222 }
223
224 protected void pushCoreProperties(C context) throws Exception {
225 System.setProperty(
226 Constants.MAVEN_HOME,
227 context.invokerRequest.installationDirectory().toString());
228 }
229
230
231
232
233
234
235
236
237 protected void pushUserProperties(C context) throws Exception {
238 ProtoSession protoSession = context.protoSession;
239 HashSet<String> sys = new HashSet<>(protoSession.getSystemProperties().keySet());
240 if (context.pushedUserProperties == null) {
241 context.pushedUserProperties = new HashSet<>();
242 protoSession.getUserProperties().entrySet().stream()
243 .filter(k -> !sys.contains(k.getKey()))
244 .peek(k -> context.pushedUserProperties.add(k.getKey()))
245 .forEach(k -> System.setProperty(k.getKey(), k.getValue()));
246 } else {
247 protoSession.getUserProperties().entrySet().stream()
248 .filter(k -> context.pushedUserProperties.contains(k.getKey()) || !sys.contains(k.getKey()))
249 .forEach(k -> System.setProperty(k.getKey(), k.getValue()));
250 }
251 }
252
253 protected void configureLogging(C context) throws Exception {
254
255 Options mavenOptions = context.invokerRequest.options();
256 Map<String, String> userProperties = context.protoSession.getUserProperties();
257 String styleColor = mavenOptions
258 .color()
259 .orElse(userProperties.getOrDefault(
260 Constants.MAVEN_STYLE_COLOR_PROPERTY, userProperties.getOrDefault("style.color", "auto")))
261 .toLowerCase(Locale.ENGLISH);
262 if ("always".equals(styleColor) || "yes".equals(styleColor) || "force".equals(styleColor)) {
263 context.coloredOutput = true;
264 } else if ("never".equals(styleColor) || "no".equals(styleColor) || "none".equals(styleColor)) {
265 context.coloredOutput = false;
266 } else if (!"auto".equals(styleColor) && !"tty".equals(styleColor) && !"if-tty".equals(styleColor)) {
267 throw new IllegalArgumentException(
268 "Invalid color configuration value '" + styleColor + "'. Supported are 'auto', 'always', 'never'.");
269 } else {
270 boolean isBatchMode = !mavenOptions.forceInteractive().orElse(false)
271 && mavenOptions.nonInteractive().orElse(false);
272 if (isBatchMode || mavenOptions.logFile().isPresent()) {
273 context.coloredOutput = false;
274 }
275 }
276
277 context.loggerFactory = LoggerFactory.getILoggerFactory();
278 context.slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration(context.loggerFactory);
279
280 context.loggerLevel = Slf4jConfiguration.Level.INFO;
281 if (mavenOptions.verbose().orElse(false)) {
282 context.loggerLevel = Slf4jConfiguration.Level.DEBUG;
283 } else if (mavenOptions.quiet().orElse(false)) {
284 context.loggerLevel = Slf4jConfiguration.Level.ERROR;
285 }
286 context.slf4jConfiguration.setRootLoggerLevel(context.loggerLevel);
287
288
289 }
290
291 protected BuildEventListener determineBuildEventListener(C context) {
292 if (context.buildEventListener == null) {
293 context.buildEventListener = doDetermineBuildEventListener(context);
294 }
295 return context.buildEventListener;
296 }
297
298 protected BuildEventListener doDetermineBuildEventListener(C context) {
299 Consumer<String> writer = determineWriter(context);
300 return new SimpleBuildEventListener(writer);
301 }
302
303 protected final void createTerminal(C context) {
304 if (context.terminal == null) {
305
306 ProjectBuildLogAppender projectBuildLogAppender =
307 new ProjectBuildLogAppender(determineBuildEventListener(context));
308 context.closeables.add(projectBuildLogAppender);
309
310 MessageUtils.systemInstall(
311 builder -> doCreateTerminal(context, builder),
312 terminal -> doConfigureWithTerminal(context, terminal));
313
314 context.terminal = MessageUtils.getTerminal();
315 context.closeables.add(MessageUtils::systemUninstall);
316 MessageUtils.registerShutdownHook();
317 } else {
318 doConfigureWithTerminal(context, context.terminal);
319 }
320 }
321
322
323
324
325
326
327 protected void doCreateTerminal(C context, TerminalBuilder builder) {
328 if (context.invokerRequest.embedded()) {
329 InputStream in = context.invokerRequest.stdIn().orElse(InputStream.nullInputStream());
330 OutputStream out = context.invokerRequest.stdOut().orElse(OutputStream.nullOutputStream());
331 builder.streams(in, out);
332 builder.provider(TerminalBuilder.PROP_PROVIDER_EXEC);
333 context.coloredOutput = context.coloredOutput != null ? context.coloredOutput : false;
334 context.closeables.add(out::flush);
335 } else {
336 builder.systemOutput(TerminalBuilder.SystemOutput.ForcedSysOut);
337 }
338 if (context.coloredOutput != null) {
339 builder.color(context.coloredOutput);
340 }
341 }
342
343
344
345
346 protected final void doConfigureWithTerminal(C context, Terminal terminal) {
347 context.terminal = terminal;
348 Options options = context.invokerRequest.options();
349
350
351
352
353
354
355 MessageUtils.setColorEnabled(
356 context.coloredOutput != null ? context.coloredOutput : !Terminal.TYPE_DUMB.equals(terminal.getType()));
357
358
359 if (options.rawStreams().orElse(false)) {
360 doConfigureWithTerminalWithRawStreamsEnabled(context);
361 } else {
362 doConfigureWithTerminalWithRawStreamsDisabled(context);
363 }
364 }
365
366
367
368
369 protected void doConfigureWithTerminalWithRawStreamsEnabled(C context) {}
370
371
372
373
374 protected void doConfigureWithTerminalWithRawStreamsDisabled(C context) {
375 MavenSimpleLogger stdout = (MavenSimpleLogger) context.loggerFactory.getLogger("stdout");
376 MavenSimpleLogger stderr = (MavenSimpleLogger) context.loggerFactory.getLogger("stderr");
377 stdout.setLogLevel(LocationAwareLogger.INFO_INT);
378 stderr.setLogLevel(LocationAwareLogger.INFO_INT);
379 PrintStream psOut = new LoggingOutputStream(s -> stdout.info("[stdout] " + s)).printStream();
380 context.closeables.add(() -> LoggingOutputStream.forceFlush(psOut));
381 PrintStream psErr = new LoggingOutputStream(s -> stderr.warn("[stderr] " + s)).printStream();
382 context.closeables.add(() -> LoggingOutputStream.forceFlush(psErr));
383 System.setOut(psOut);
384 System.setErr(psErr);
385
386 }
387
388 protected Consumer<String> determineWriter(C context) {
389 if (context.writer == null) {
390 context.writer = doDetermineWriter(context);
391 }
392 return context.writer;
393 }
394
395 protected Consumer<String> doDetermineWriter(C context) {
396 Options options = context.invokerRequest.options();
397 if (options.logFile().isPresent()) {
398 Path logFile = context.cwd.resolve(options.logFile().get());
399 try {
400 PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile), true);
401 context.closeables.add(printWriter);
402 return printWriter::println;
403 } catch (IOException e) {
404 throw new MavenException("Unable to redirect logging to " + logFile, e);
405 }
406 } else {
407
408
409 return msg -> {
410 PrintWriter pw = context.terminal.writer();
411 pw.println(msg);
412 pw.flush();
413 };
414 }
415 }
416
417 protected void activateLogging(C context) throws Exception {
418 InvokerRequest invokerRequest = context.invokerRequest;
419 Options mavenOptions = invokerRequest.options();
420
421 context.slf4jConfiguration.activate();
422 if (mavenOptions.failOnSeverity().isPresent()) {
423 String logLevelThreshold = mavenOptions.failOnSeverity().get();
424 if (context.loggerFactory instanceof LogLevelRecorder recorder) {
425 LogLevelRecorder.Level level =
426 switch (logLevelThreshold.toLowerCase(Locale.ENGLISH)) {
427 case "warn", "warning" -> LogLevelRecorder.Level.WARN;
428 case "error" -> LogLevelRecorder.Level.ERROR;
429 default -> throw new IllegalArgumentException(
430 logLevelThreshold
431 + " is not a valid log severity threshold. Valid severities are WARN/WARNING and ERROR.");
432 };
433 recorder.setMaxLevelAllowed(level);
434 context.logger.info("Enabled to break the build on log level " + logLevelThreshold + ".");
435 } else {
436 context.logger.warn("Expected LoggerFactory to be of type '" + LogLevelRecorder.class.getName()
437 + "', but found '"
438 + context.loggerFactory.getClass().getName() + "' instead. "
439 + "The --fail-on-severity flag will not take effect.");
440 }
441 }
442
443
444 Logger logger =
445 new Slf4jLogger(context.loggerFactory.getLogger(getClass().getName()));
446 context.logger.drain().forEach(e -> logger.log(e.level(), e.message(), e.error()));
447 context.logger = logger;
448 }
449
450 protected void helpOrVersionAndMayExit(C context) throws Exception {
451 InvokerRequest invokerRequest = context.invokerRequest;
452 if (invokerRequest.options().help().isPresent()) {
453 Consumer<String> writer = determineWriter(context);
454 invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), writer);
455 throw new InvokerException.ExitException(0);
456 }
457 if (invokerRequest.options().showVersionAndExit().isPresent()) {
458 showVersion(context);
459 throw new InvokerException.ExitException(0);
460 }
461 }
462
463 protected void showVersion(C context) {
464 Consumer<String> writer = determineWriter(context);
465 InvokerRequest invokerRequest = context.invokerRequest;
466 if (invokerRequest.options().quiet().orElse(false)) {
467 writer.accept(CLIReportingUtils.showVersionMinimal());
468 } else if (invokerRequest.options().verbose().orElse(false)) {
469 writer.accept(CLIReportingUtils.showVersion(
470 ProcessHandle.current().info().commandLine().orElse(null), describe(context.terminal)));
471
472 } else {
473 writer.accept(CLIReportingUtils.showVersion());
474 }
475 }
476
477 protected String describe(Terminal terminal) {
478 if (terminal == null) {
479 return null;
480 }
481 if (terminal instanceof FastTerminal ft) {
482 terminal = ft.getTerminal();
483 }
484 List<String> subs = new ArrayList<>();
485 subs.add("type=" + terminal.getType());
486 if (terminal instanceof TerminalExt te) {
487 subs.add("provider=" + te.getProvider().name());
488 }
489 if (terminal instanceof AbstractPosixTerminal pt) {
490 subs.add("pty=" + pt.getPty().getClass().getName());
491 }
492 return terminal.getClass().getSimpleName() + " (" + String.join(", ", subs) + ")";
493 }
494
495 protected void preCommands(C context) throws Exception {
496 Options mavenOptions = context.invokerRequest.options();
497 boolean verbose = mavenOptions.verbose().orElse(false);
498 boolean version = mavenOptions.showVersion().orElse(false);
499 if (verbose || version) {
500 showVersion(context);
501 }
502 }
503
504 protected void container(C context) throws Exception {
505 if (context.lookup == null) {
506 context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(this, context);
507 context.closeables.add(context::closeContainer);
508 context.lookup = context.containerCapsule.getLookup();
509 } else {
510 context.containerCapsule.updateLogging(context);
511 }
512 }
513
514 protected ContainerCapsuleFactory<C> createContainerCapsuleFactory() {
515 return new PlexusContainerCapsuleFactory<>();
516 }
517
518 protected void postContainer(C context) throws Exception {
519 ProtoSession protoSession = context.protoSession;
520 for (PropertyContributor propertyContributor : context.lookup
521 .lookup(PropertyContributorsHolder.class)
522 .getPropertyContributors()
523 .values()) {
524 protoSession = protoSession.toBuilder()
525 .withUserProperties(propertyContributor.contribute(protoSession))
526 .build();
527 }
528 context.protoSession = protoSession;
529 }
530
531 protected void lookup(C context) throws Exception {
532 if (context.eventSpyDispatcher == null) {
533 context.eventSpyDispatcher = context.lookup.lookup(EventSpyDispatcher.class);
534 }
535 }
536
537 protected void init(C context) throws Exception {
538 Map<String, Object> data = new HashMap<>();
539 data.put("plexus", context.lookup.lookup(PlexusContainer.class));
540 data.put("workingDirectory", context.cwd.get().toString());
541 data.put("systemProperties", toProperties(context.protoSession.getSystemProperties()));
542 data.put("userProperties", toProperties(context.protoSession.getUserProperties()));
543 data.put("versionProperties", CLIReportingUtils.getBuildProperties());
544 context.eventSpyDispatcher.init(() -> data);
545 }
546
547 protected void postCommands(C context) throws Exception {
548 InvokerRequest invokerRequest = context.invokerRequest;
549 Logger logger = context.logger;
550 if (invokerRequest.options().showErrors().orElse(false)) {
551 logger.info("Error stacktraces are turned on.");
552 }
553 if (context.invokerRequest.options().verbose().orElse(false)) {
554 logger.debug("Message scheme: " + (MessageUtils.isColorEnabled() ? "color" : "plain"));
555 if (MessageUtils.isColorEnabled()) {
556 MessageBuilder buff = MessageUtils.builder();
557 buff.a("Message styles: ");
558 buff.trace("trace").a(' ');
559 buff.debug("debug").a(' ');
560 buff.info("info").a(' ');
561 buff.warning("warning").a(' ');
562 buff.error("error").a(' ');
563 buff.success("success").a(' ');
564 buff.failure("failure").a(' ');
565 buff.strong("strong").a(' ');
566 buff.mojo("mojo").a(' ');
567 buff.project("project");
568 logger.debug(buff.toString());
569 }
570 }
571 }
572
573 protected void settings(C context) throws Exception {
574 if (context.effectiveSettings == null) {
575 settings(context, true, context.lookup.lookup(SettingsBuilder.class));
576 }
577 }
578
579
580
581
582
583
584
585
586
587
588
589
590 protected Runnable settings(C context, boolean emitSettingsWarnings, SettingsBuilder settingsBuilder)
591 throws Exception {
592 Options mavenOptions = context.invokerRequest.options();
593
594 Path userSettingsFile = null;
595 if (mavenOptions.altUserSettings().isPresent()) {
596 userSettingsFile =
597 context.cwd.resolve(mavenOptions.altUserSettings().get());
598
599 if (!Files.isRegularFile(userSettingsFile)) {
600 throw new FileNotFoundException("The specified user settings file does not exist: " + userSettingsFile);
601 }
602 } else {
603 String userSettingsFileStr =
604 context.protoSession.getUserProperties().get(Constants.MAVEN_USER_SETTINGS);
605 if (userSettingsFileStr != null) {
606 userSettingsFile =
607 context.userDirectory.resolve(userSettingsFileStr).normalize();
608 }
609 }
610
611 Path projectSettingsFile = null;
612 if (mavenOptions.altProjectSettings().isPresent()) {
613 projectSettingsFile =
614 context.cwd.resolve(mavenOptions.altProjectSettings().get());
615
616 if (!Files.isRegularFile(projectSettingsFile)) {
617 throw new FileNotFoundException(
618 "The specified project settings file does not exist: " + projectSettingsFile);
619 }
620 } else {
621 String projectSettingsFileStr =
622 context.protoSession.getUserProperties().get(Constants.MAVEN_PROJECT_SETTINGS);
623 if (projectSettingsFileStr != null) {
624 projectSettingsFile = context.cwd.resolve(projectSettingsFileStr);
625 }
626 }
627
628 Path installationSettingsFile = null;
629 if (mavenOptions.altInstallationSettings().isPresent()) {
630 installationSettingsFile =
631 context.cwd.resolve(mavenOptions.altInstallationSettings().get());
632
633 if (!Files.isRegularFile(installationSettingsFile)) {
634 throw new FileNotFoundException(
635 "The specified installation settings file does not exist: " + installationSettingsFile);
636 }
637 } else {
638 String installationSettingsFileStr =
639 context.protoSession.getUserProperties().get(Constants.MAVEN_INSTALLATION_SETTINGS);
640 if (installationSettingsFileStr != null) {
641 installationSettingsFile = context.installationDirectory
642 .resolve(installationSettingsFileStr)
643 .normalize();
644 }
645 }
646
647 context.installationSettingsPath = installationSettingsFile;
648 context.projectSettingsPath = projectSettingsFile;
649 context.userSettingsPath = userSettingsFile;
650
651 UnaryOperator<String> interpolationSource = Interpolator.chain(
652 context.protoSession.getUserProperties()::get, context.protoSession.getSystemProperties()::get);
653 SettingsBuilderRequest settingsRequest = SettingsBuilderRequest.builder()
654 .session(context.protoSession)
655 .installationSettingsSource(
656 installationSettingsFile != null && Files.exists(installationSettingsFile)
657 ? Sources.fromPath(installationSettingsFile)
658 : null)
659 .projectSettingsSource(
660 projectSettingsFile != null && Files.exists(projectSettingsFile)
661 ? Sources.fromPath(projectSettingsFile)
662 : null)
663 .userSettingsSource(
664 userSettingsFile != null && Files.exists(userSettingsFile)
665 ? Sources.fromPath(userSettingsFile)
666 : null)
667 .interpolationSource(interpolationSource)
668 .build();
669
670 customizeSettingsRequest(context, settingsRequest);
671 if (context.eventSpyDispatcher != null) {
672 context.eventSpyDispatcher.onEvent(settingsRequest);
673 }
674
675 context.logger.debug("Reading installation settings from '" + installationSettingsFile + "'");
676 context.logger.debug("Reading project settings from '" + projectSettingsFile + "'");
677 context.logger.debug("Reading user settings from '" + userSettingsFile + "'");
678
679 SettingsBuilderResult settingsResult = settingsBuilder.build(settingsRequest);
680 customizeSettingsResult(context, settingsResult);
681 if (context.eventSpyDispatcher != null) {
682 context.eventSpyDispatcher.onEvent(settingsResult);
683 }
684
685 context.effectiveSettings = settingsResult.getEffectiveSettings();
686 context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode());
687 context.localRepositoryPath = localRepositoryPath(context);
688
689 if (emitSettingsWarnings && settingsResult.getProblems().hasWarningProblems()) {
690 int totalProblems = settingsResult.getProblems().totalProblemsReported();
691 context.logger.info("");
692 context.logger.info(String.format(
693 "%s %s encountered while building the effective settings (use -e to see details)",
694 totalProblems, (totalProblems == 1) ? "problem was" : "problems were"));
695
696 if (context.invokerRequest.options().showErrors().orElse(false)) {
697 for (BuilderProblem problem :
698 settingsResult.getProblems().problems().toList()) {
699 context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
700 }
701 }
702 context.logger.info("");
703 }
704 return () -> {
705 context.installationSettingsPath = null;
706 context.projectSettingsPath = null;
707 context.userSettingsPath = null;
708 context.effectiveSettings = null;
709 context.interactive = true;
710 context.localRepositoryPath = null;
711 };
712 }
713
714 protected void customizeSettingsRequest(C context, SettingsBuilderRequest settingsBuilderRequest)
715 throws Exception {}
716
717 protected void customizeSettingsResult(C context, SettingsBuilderResult settingsBuilderResult) throws Exception {}
718
719 protected boolean mayDisableInteractiveMode(C context, boolean proposedInteractive) {
720 if (!context.invokerRequest.options().forceInteractive().orElse(false)) {
721 if (context.invokerRequest.options().nonInteractive().orElse(false)) {
722 return false;
723 } else {
724 boolean runningOnCI = isRunningOnCI(context);
725 if (runningOnCI) {
726 context.logger.info(
727 "Making this build non-interactive, because the environment variable CI equals \"true\"."
728 + " Disable this detection by removing that variable or adding --force-interactive.");
729 return false;
730 }
731 }
732 }
733 return proposedInteractive;
734 }
735
736 protected Path localRepositoryPath(C context) {
737
738 String userDefinedLocalRepo = context.protoSession.getUserProperties().get(Constants.MAVEN_REPO_LOCAL);
739 if (userDefinedLocalRepo == null) {
740 userDefinedLocalRepo = context.protoSession.getUserProperties().get(Constants.MAVEN_REPO_LOCAL);
741 if (userDefinedLocalRepo != null) {
742 context.logger.warn("The property '" + Constants.MAVEN_REPO_LOCAL
743 + "' has been set using a JVM system property which is deprecated. "
744 + "The property can be passed as a Maven argument or in the Maven project configuration file,"
745 + "usually located at ${session.rootDirectory}/.mvn/maven.properties.");
746 }
747 }
748 if (userDefinedLocalRepo != null) {
749 return context.cwd.resolve(userDefinedLocalRepo);
750 }
751
752 userDefinedLocalRepo = context.effectiveSettings.getLocalRepository();
753 if (userDefinedLocalRepo != null && !userDefinedLocalRepo.isEmpty()) {
754 return context.userDirectory.resolve(userDefinedLocalRepo).normalize();
755 }
756
757 return context.userDirectory
758 .resolve(context.protoSession.getUserProperties().get(Constants.MAVEN_USER_CONF))
759 .resolve("repository")
760 .normalize();
761 }
762
763 protected void populateRequest(C context, Lookup lookup, MavenExecutionRequest request) throws Exception {
764 populateRequestFromSettings(request, context.effectiveSettings);
765
766 Options options = context.invokerRequest.options();
767 request.setLoggingLevel(toMavenExecutionRequestLoggingLevel(context.loggerLevel));
768 request.setLocalRepositoryPath(context.localRepositoryPath.toFile());
769 request.setLocalRepository(createLocalArtifactRepository(context.localRepositoryPath));
770
771 request.setInteractiveMode(context.interactive);
772 request.setShowErrors(options.showErrors().orElse(false));
773 request.setBaseDirectory(context.invokerRequest.topDirectory().toFile());
774 request.setSystemProperties(toProperties(context.protoSession.getSystemProperties()));
775 request.setUserProperties(toProperties(context.protoSession.getUserProperties()));
776
777 request.setInstallationSettingsFile(
778 context.installationSettingsPath != null ? context.installationSettingsPath.toFile() : null);
779 request.setProjectSettingsFile(
780 context.projectSettingsPath != null ? context.projectSettingsPath.toFile() : null);
781 request.setUserSettingsFile(context.userSettingsPath != null ? context.userSettingsPath.toFile() : null);
782
783 request.setTopDirectory(context.invokerRequest.topDirectory());
784 if (context.invokerRequest.rootDirectory().isPresent()) {
785 request.setMultiModuleProjectDirectory(
786 context.invokerRequest.rootDirectory().get().toFile());
787 request.setRootDirectory(context.invokerRequest.rootDirectory().get());
788 }
789
790 request.addPluginGroup("org.apache.maven.plugins");
791 request.addPluginGroup("org.codehaus.mojo");
792 }
793
794
795
796
797 @Deprecated
798 private ArtifactRepository createLocalArtifactRepository(Path baseDirectory) {
799 DefaultRepositoryLayout layout = new DefaultRepositoryLayout();
800 ArtifactRepositoryPolicy blah = new ArtifactRepositoryPolicy(
801 true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE);
802 return new MavenArtifactRepository(
803 "local", "file://" + baseDirectory.toUri().getRawPath(), layout, blah, blah);
804 }
805
806 protected void populateRequestFromSettings(MavenExecutionRequest request, Settings settings) throws Exception {
807 if (settings == null) {
808 return;
809 }
810 request.setOffline(settings.isOffline());
811 request.setInteractiveMode(settings.isInteractiveMode());
812 request.setPluginGroups(settings.getPluginGroups());
813 request.setLocalRepositoryPath(settings.getLocalRepository());
814 for (Server server : settings.getServers()) {
815 request.addServer(new org.apache.maven.settings.Server(server));
816 }
817
818
819
820
821
822
823
824
825
826
827
828
829
830 for (Proxy proxy : settings.getProxies()) {
831 if (!proxy.isActive()) {
832 continue;
833 }
834 request.addProxy(new org.apache.maven.settings.Proxy(proxy));
835 }
836
837
838
839
840
841
842
843
844
845 for (Mirror mirror : settings.getMirrors()) {
846 request.addMirror(new org.apache.maven.settings.Mirror(mirror));
847 }
848
849
850 LinkedHashMap<String, Repository> remoteRepositories = new LinkedHashMap<>();
851 LinkedHashMap<String, Repository> remotePluginRepositories = new LinkedHashMap<>();
852
853
854 for (Repository remoteRepository : settings.getRepositories()) {
855 remoteRepositories.put(remoteRepository.getId(), remoteRepository);
856 }
857 for (Repository pluginRepository : settings.getPluginRepositories()) {
858 remotePluginRepositories.put(pluginRepository.getId(), pluginRepository);
859 }
860
861
862 for (Profile rawProfile : settings.getProfiles()) {
863 request.addProfile(
864 new org.apache.maven.model.Profile(SettingsUtilsV4.convertFromSettingsProfile(rawProfile)));
865
866 if (settings.getActiveProfiles().contains(rawProfile.getId())) {
867 for (Repository remoteRepository : rawProfile.getRepositories()) {
868 remoteRepositories.put(remoteRepository.getId(), remoteRepository);
869 }
870
871 for (Repository pluginRepository : rawProfile.getPluginRepositories()) {
872 remotePluginRepositories.put(pluginRepository.getId(), pluginRepository);
873 }
874 }
875 }
876
877
878 request.setActiveProfiles(settings.getActiveProfiles());
879 request.setRemoteRepositories(remoteRepositories.values().stream()
880 .map(r -> {
881 try {
882 return MavenRepositorySystem.buildArtifactRepository(
883 new org.apache.maven.settings.Repository(r));
884 } catch (Exception e) {
885
886 return null;
887 }
888 })
889 .filter(Objects::nonNull)
890 .toList());
891 request.setPluginArtifactRepositories(remotePluginRepositories.values().stream()
892 .map(r -> {
893 try {
894 return MavenRepositorySystem.buildArtifactRepository(
895 new org.apache.maven.settings.Repository(r));
896 } catch (Exception e) {
897
898 return null;
899 }
900 })
901 .filter(Objects::nonNull)
902 .toList());
903 }
904
905 protected int calculateDegreeOfConcurrency(String threadConfiguration) {
906 try {
907 if (threadConfiguration.endsWith("C")) {
908 String str = threadConfiguration.substring(0, threadConfiguration.length() - 1);
909 float coreMultiplier = Float.parseFloat(str);
910
911 if (coreMultiplier <= 0.0f) {
912 throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration
913 + "'. Value must be positive.");
914 }
915
916 int procs = Runtime.getRuntime().availableProcessors();
917 int threads = (int) (coreMultiplier * procs);
918 return threads == 0 ? 1 : threads;
919 } else {
920 int threads = Integer.parseInt(threadConfiguration);
921 if (threads <= 0) {
922 throw new IllegalArgumentException(
923 "Invalid threads value: '" + threadConfiguration + "'. Value must be positive.");
924 }
925 return threads;
926 }
927 } catch (NumberFormatException e) {
928 throw new IllegalArgumentException("Invalid threads value: '" + threadConfiguration
929 + "'. Supported are int and float values ending with C.");
930 }
931 }
932
933 protected boolean isRunningOnCI(C context) {
934 String ciEnv = context.protoSession.getSystemProperties().get("env.CI");
935 return ciEnv != null && !"false".equals(ciEnv);
936 }
937
938 protected abstract int execute(C context) throws Exception;
939 }