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