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