View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cling.invoker;
20  
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.util.ArrayList;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.function.Consumer;
33  import java.util.function.Function;
34  
35  import org.apache.maven.api.Constants;
36  import org.apache.maven.api.ProtoSession;
37  import org.apache.maven.api.cli.Invoker;
38  import org.apache.maven.api.cli.InvokerException;
39  import org.apache.maven.api.cli.InvokerRequest;
40  import org.apache.maven.api.cli.Logger;
41  import org.apache.maven.api.cli.Options;
42  import org.apache.maven.api.services.BuilderProblem;
43  import org.apache.maven.api.services.Interpolator;
44  import org.apache.maven.api.services.Lookup;
45  import org.apache.maven.api.services.MavenException;
46  import org.apache.maven.api.services.MessageBuilder;
47  import org.apache.maven.api.services.SettingsBuilder;
48  import org.apache.maven.api.services.SettingsBuilderRequest;
49  import org.apache.maven.api.services.SettingsBuilderResult;
50  import org.apache.maven.api.services.Source;
51  import org.apache.maven.api.settings.Mirror;
52  import org.apache.maven.api.settings.Profile;
53  import org.apache.maven.api.settings.Proxy;
54  import org.apache.maven.api.settings.Repository;
55  import org.apache.maven.api.settings.Server;
56  import org.apache.maven.api.settings.Settings;
57  import org.apache.maven.api.spi.PropertyContributor;
58  import org.apache.maven.artifact.InvalidRepositoryException;
59  import org.apache.maven.artifact.repository.ArtifactRepository;
60  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
61  import org.apache.maven.artifact.repository.MavenArtifactRepository;
62  import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
63  import org.apache.maven.bridge.MavenRepositorySystem;
64  import org.apache.maven.cling.invoker.spi.PropertyContributorsHolder;
65  import org.apache.maven.cling.logging.Slf4jConfiguration;
66  import org.apache.maven.cling.logging.Slf4jConfigurationFactory;
67  import org.apache.maven.cling.utils.CLIReportingUtils;
68  import org.apache.maven.execution.MavenExecutionRequest;
69  import org.apache.maven.internal.impl.SettingsUtilsV4;
70  import org.apache.maven.jline.FastTerminal;
71  import org.apache.maven.jline.MessageUtils;
72  import org.apache.maven.logging.LoggingOutputStream;
73  import org.apache.maven.logging.api.LogLevelRecorder;
74  import org.apache.maven.slf4j.MavenSimpleLogger;
75  import org.jline.terminal.Terminal;
76  import org.jline.terminal.TerminalBuilder;
77  import org.jline.terminal.impl.AbstractPosixTerminal;
78  import org.jline.terminal.spi.TerminalExt;
79  import org.slf4j.LoggerFactory;
80  import org.slf4j.spi.LocationAwareLogger;
81  
82  import static java.util.Objects.requireNonNull;
83  import static org.apache.maven.cling.invoker.Utils.toMavenExecutionRequestLoggingLevel;
84  import static org.apache.maven.cling.invoker.Utils.toProperties;
85  
86  /**
87   * Lookup invoker implementation, that boots up DI container.
88   *
89   * @param <C> The context type.
90   */
91  public abstract class LookupInvoker<C extends LookupContext> implements Invoker {
92      protected final ProtoLookup protoLookup;
93  
94      public LookupInvoker(ProtoLookup protoLookup) {
95          this.protoLookup = requireNonNull(protoLookup);
96      }
97  
98      @Override
99      public int invoke(InvokerRequest invokerRequest) throws InvokerException {
100         requireNonNull(invokerRequest);
101 
102         Properties oldProps = (Properties) System.getProperties().clone();
103         ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
104         try (C context = createContext(invokerRequest)) {
105             try {
106                 if (context.containerCapsule != null
107                         && context.containerCapsule.currentThreadClassLoader().isPresent()) {
108                     Thread.currentThread()
109                             .setContextClassLoader(context.containerCapsule
110                                     .currentThreadClassLoader()
111                                     .get());
112                 }
113                 return doInvoke(context);
114             } catch (InvokerException.ExitException e) {
115                 return e.getExitCode();
116             } catch (Exception e) {
117                 throw handleException(context, e);
118             }
119         } finally {
120             Thread.currentThread().setContextClassLoader(oldCL);
121             System.setProperties(oldProps);
122         }
123     }
124 
125     protected int doInvoke(C context) throws Exception {
126         pushCoreProperties(context);
127         validate(context);
128         prepare(context);
129         configureLogging(context);
130         createTerminal(context);
131         activateLogging(context);
132         helpOrVersionAndMayExit(context);
133         preCommands(context);
134         container(context);
135         postContainer(context);
136         pushUserProperties(context);
137         lookup(context);
138         init(context);
139         postCommands(context);
140         settings(context);
141         return execute(context);
142     }
143 
144     protected InvokerException handleException(C context, Exception e) throws InvokerException {
145         boolean showStackTrace = context.invokerRequest.options().showErrors().orElse(false);
146         if (showStackTrace) {
147             context.logger.error(
148                     "Error executing " + context.invokerRequest.parserRequest().commandName() + ".", e);
149         } else {
150             context.logger.error(
151                     "Error executing " + context.invokerRequest.parserRequest().commandName() + ".");
152             context.logger.error(e.getMessage());
153             for (Throwable cause = e.getCause(); cause != null && cause != cause.getCause(); cause = cause.getCause()) {
154                 context.logger.error("Caused by: " + cause.getMessage());
155             }
156         }
157         return new InvokerException(e.getMessage(), e);
158     }
159 
160     protected abstract C createContext(InvokerRequest invokerRequest) throws InvokerException;
161 
162     protected void pushCoreProperties(C context) throws Exception {
163         System.setProperty(
164                 Constants.MAVEN_HOME,
165                 context.invokerRequest.installationDirectory().toString());
166     }
167 
168     protected void pushUserProperties(C context) throws Exception {
169         ProtoSession protoSession = context.protoSession;
170         HashSet<String> sys = new HashSet<>(protoSession.getSystemProperties().keySet());
171         protoSession.getUserProperties().entrySet().stream()
172                 .filter(k -> !sys.contains(k.getKey()))
173                 .forEach(k -> System.setProperty(k.getKey(), k.getValue()));
174     }
175 
176     protected void validate(C context) throws Exception {}
177 
178     protected void prepare(C context) throws Exception {}
179 
180     protected void configureLogging(C context) throws Exception {
181         // LOG COLOR
182         Options mavenOptions = context.invokerRequest.options();
183         Map<String, String> userProperties = context.protoSession.getUserProperties();
184         String styleColor = mavenOptions
185                 .color()
186                 .orElse(userProperties.getOrDefault(
187                         Constants.MAVEN_STYLE_COLOR_PROPERTY, userProperties.getOrDefault("style.color", "auto")));
188         if ("always".equals(styleColor) || "yes".equals(styleColor) || "force".equals(styleColor)) {
189             context.coloredOutput = true;
190         } else if ("never".equals(styleColor) || "no".equals(styleColor) || "none".equals(styleColor)) {
191             context.coloredOutput = false;
192         } else if (!"auto".equals(styleColor) && !"tty".equals(styleColor) && !"if-tty".equals(styleColor)) {
193             throw new IllegalArgumentException(
194                     "Invalid color configuration value '" + styleColor + "'. Supported are 'auto', 'always', 'never'.");
195         } else {
196             boolean isBatchMode = !mavenOptions.forceInteractive().orElse(false)
197                     && mavenOptions.nonInteractive().orElse(false);
198             if (isBatchMode || mavenOptions.logFile().isPresent()) {
199                 context.coloredOutput = false;
200             }
201         }
202 
203         context.loggerFactory = LoggerFactory.getILoggerFactory();
204         context.slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration(context.loggerFactory);
205 
206         context.loggerLevel = Slf4jConfiguration.Level.INFO;
207         if (mavenOptions.verbose().orElse(false)) {
208             context.loggerLevel = Slf4jConfiguration.Level.DEBUG;
209         } else if (mavenOptions.quiet().orElse(false)) {
210             context.loggerLevel = Slf4jConfiguration.Level.ERROR;
211         }
212         context.slf4jConfiguration.setRootLoggerLevel(context.loggerLevel);
213         // else fall back to default log level specified in conf
214         // see https://issues.apache.org/jira/browse/MNG-2570
215     }
216 
217     protected void createTerminal(C context) {
218         MessageUtils.systemInstall(
219                 builder -> {
220                     builder.streams(
221                             context.invokerRequest.in().orElse(null),
222                             context.invokerRequest.out().orElse(null));
223                     builder.systemOutput(TerminalBuilder.SystemOutput.ForcedSysOut);
224                     // The exec builder suffers from https://github.com/jline/jline3/issues/1098
225                     // We could re-enable it when fixed to provide support for non-standard architectures,
226                     // for which JLine does not provide any native library.
227                     builder.exec(false);
228                     if (context.coloredOutput != null) {
229                         builder.color(context.coloredOutput);
230                     }
231                 },
232                 terminal -> doConfigureWithTerminal(context, terminal));
233 
234         context.terminal = MessageUtils.getTerminal();
235         // JLine is quite slow to start due to the native library unpacking and loading
236         // so boot it asynchronously
237         context.closeables.add(MessageUtils::systemUninstall);
238         MessageUtils.registerShutdownHook(); // safety belt
239         if (context.coloredOutput != null) {
240             MessageUtils.setColorEnabled(context.coloredOutput);
241         }
242     }
243 
244     protected void doConfigureWithTerminal(C context, Terminal terminal) {
245         Options options = context.invokerRequest.options();
246         if (options.rawStreams().isEmpty() || !options.rawStreams().get()) {
247             MavenSimpleLogger stdout = (MavenSimpleLogger) context.loggerFactory.getLogger("stdout");
248             MavenSimpleLogger stderr = (MavenSimpleLogger) context.loggerFactory.getLogger("stderr");
249             stdout.setLogLevel(LocationAwareLogger.INFO_INT);
250             stderr.setLogLevel(LocationAwareLogger.INFO_INT);
251             System.setOut(new LoggingOutputStream(s -> stdout.info("[stdout] " + s)).printStream());
252             System.setErr(new LoggingOutputStream(s -> stderr.warn("[stderr] " + s)).printStream());
253             // no need to set them back, this is already handled by MessageUtils.systemUninstall() above
254         }
255     }
256 
257     protected Consumer<String> determineWriter(C context) {
258         if (context.writer == null) {
259             context.writer = doDetermineWriter(context);
260         }
261         return context.writer;
262     }
263 
264     protected Consumer<String> doDetermineWriter(C context) {
265         Options options = context.invokerRequest.options();
266         if (options.logFile().isPresent()) {
267             Path logFile = context.cwdResolver.apply(options.logFile().get());
268             try {
269                 PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile), true);
270                 context.closeables.add(printWriter);
271                 return printWriter::println;
272             } catch (IOException e) {
273                 throw new MavenException("Unable to redirect logging to " + logFile, e);
274             }
275         } else {
276             // Given the terminal creation has been offloaded to a different thread,
277             // do not pass directly the terminal writer
278             return msg -> {
279                 PrintWriter pw = context.terminal.writer();
280                 pw.println(msg);
281                 pw.flush();
282             };
283         }
284     }
285 
286     protected void activateLogging(C context) throws Exception {
287         InvokerRequest invokerRequest = context.invokerRequest;
288         Options mavenOptions = invokerRequest.options();
289 
290         context.slf4jConfiguration.activate();
291         org.slf4j.Logger l = context.loggerFactory.getLogger(this.getClass().getName());
292         context.logger = (level, message, error) -> l.atLevel(org.slf4j.event.Level.valueOf(level.name()))
293                 .setCause(error)
294                 .log(message);
295 
296         if (mavenOptions.failOnSeverity().isPresent()) {
297             String logLevelThreshold = mavenOptions.failOnSeverity().get();
298             if (context.loggerFactory instanceof LogLevelRecorder recorder) {
299                 LogLevelRecorder.Level level =
300                         switch (logLevelThreshold.toLowerCase(Locale.ENGLISH)) {
301                             case "warn", "warning" -> LogLevelRecorder.Level.WARN;
302                             case "error" -> LogLevelRecorder.Level.ERROR;
303                             default -> throw new IllegalArgumentException(
304                                     logLevelThreshold
305                                             + " is not a valid log severity threshold. Valid severities are WARN/WARNING and ERROR.");
306                         };
307                 recorder.setMaxLevelAllowed(level);
308                 context.logger.info("Enabled to break the build on log level " + logLevelThreshold + ".");
309             } else {
310                 context.logger.warn("Expected LoggerFactory to be of type '" + LogLevelRecorder.class.getName()
311                         + "', but found '"
312                         + context.loggerFactory.getClass().getName() + "' instead. "
313                         + "The --fail-on-severity flag will not take effect.");
314             }
315         }
316     }
317 
318     protected void helpOrVersionAndMayExit(C context) throws Exception {
319         InvokerRequest invokerRequest = context.invokerRequest;
320         if (invokerRequest.options().help().isPresent()) {
321             Consumer<String> writer = determineWriter(context);
322             invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), writer);
323             throw new InvokerException.ExitException(0);
324         }
325         if (invokerRequest.options().showVersionAndExit().isPresent()) {
326             showVersion(context);
327             throw new InvokerException.ExitException(0);
328         }
329     }
330 
331     protected void showVersion(C context) {
332         Consumer<String> writer = determineWriter(context);
333         InvokerRequest invokerRequest = context.invokerRequest;
334         if (invokerRequest.options().quiet().orElse(false)) {
335             writer.accept(CLIReportingUtils.showVersionMinimal());
336         } else if (invokerRequest.options().verbose().orElse(false)) {
337             writer.accept(CLIReportingUtils.showVersion(
338                     ProcessHandle.current().info().commandLine().orElse(null), describe(context.terminal)));
339 
340         } else {
341             writer.accept(CLIReportingUtils.showVersion());
342         }
343     }
344 
345     protected String describe(Terminal terminal) {
346         if (terminal == null) {
347             return null;
348         }
349         if (terminal instanceof FastTerminal ft) {
350             terminal = ft.getTerminal();
351         }
352         List<String> subs = new ArrayList<>();
353         subs.add("type=" + terminal.getType());
354         if (terminal instanceof TerminalExt te) {
355             subs.add("provider=" + te.getProvider().name());
356         }
357         if (terminal instanceof AbstractPosixTerminal pt) {
358             subs.add("pty=" + pt.getPty().getClass().getName());
359         }
360         return terminal.getClass().getSimpleName() + " (" + String.join(", ", subs) + ")";
361     }
362 
363     protected void preCommands(C context) throws Exception {
364         Options mavenOptions = context.invokerRequest.options();
365         boolean verbose = mavenOptions.verbose().orElse(false);
366         boolean version = mavenOptions.showVersion().orElse(false);
367         if (verbose || version) {
368             showVersion(context);
369         }
370     }
371 
372     protected void container(C context) throws Exception {
373         context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(this, context);
374         context.closeables.add(context::closeContainer);
375         context.lookup = context.containerCapsule.getLookup();
376 
377         // refresh logger in case container got customized by spy
378         org.slf4j.Logger l = context.loggerFactory.getLogger(this.getClass().getName());
379         context.logger = (level, message, error) -> l.atLevel(org.slf4j.event.Level.valueOf(level.name()))
380                 .setCause(error)
381                 .log(message);
382     }
383 
384     protected ContainerCapsuleFactory<C> createContainerCapsuleFactory() {
385         return new PlexusContainerCapsuleFactory<>();
386     }
387 
388     protected void postContainer(C context) throws Exception {
389         ProtoSession protoSession = context.protoSession;
390         for (PropertyContributor propertyContributor : context.lookup
391                 .lookup(PropertyContributorsHolder.class)
392                 .getPropertyContributors()
393                 .values()) {
394             protoSession = protoSession.toBuilder()
395                     .withUserProperties(propertyContributor.contribute(protoSession))
396                     .build();
397         }
398         context.protoSession = protoSession;
399     }
400 
401     protected void lookup(C context) throws Exception {}
402 
403     protected void init(C context) throws Exception {}
404 
405     protected void postCommands(C context) throws Exception {
406         InvokerRequest invokerRequest = context.invokerRequest;
407         Logger logger = context.logger;
408         if (invokerRequest.options().showErrors().orElse(false)) {
409             logger.info("Error stacktraces are turned on.");
410         }
411         if (context.invokerRequest.options().verbose().orElse(false)) {
412             logger.debug("Message scheme: " + (MessageUtils.isColorEnabled() ? "color" : "plain"));
413             if (MessageUtils.isColorEnabled()) {
414                 MessageBuilder buff = MessageUtils.builder();
415                 buff.a("Message styles: ");
416                 buff.trace("trace").a(' ');
417                 buff.debug("debug").a(' ');
418                 buff.info("info").a(' ');
419                 buff.warning("warning").a(' ');
420                 buff.error("error").a(' ');
421                 buff.success("success").a(' ');
422                 buff.failure("failure").a(' ');
423                 buff.strong("strong").a(' ');
424                 buff.mojo("mojo").a(' ');
425                 buff.project("project");
426                 logger.debug(buff.toString());
427             }
428         }
429     }
430 
431     protected void settings(C context) throws Exception {
432         settings(context, context.lookup.lookup(SettingsBuilder.class));
433     }
434 
435     protected void settings(C context, SettingsBuilder settingsBuilder) throws Exception {
436         Options mavenOptions = context.invokerRequest.options();
437         Path userSettingsFile = null;
438 
439         if (mavenOptions.altUserSettings().isPresent()) {
440             userSettingsFile =
441                     context.cwdResolver.apply(mavenOptions.altUserSettings().get());
442 
443             if (!Files.isRegularFile(userSettingsFile)) {
444                 throw new FileNotFoundException("The specified user settings file does not exist: " + userSettingsFile);
445             }
446         } else {
447             String userSettingsFileStr =
448                     context.protoSession.getUserProperties().get(Constants.MAVEN_USER_SETTINGS);
449             if (userSettingsFileStr != null) {
450                 userSettingsFile = context.userResolver.apply(userSettingsFileStr);
451             }
452         }
453 
454         Path projectSettingsFile = null;
455 
456         if (mavenOptions.altProjectSettings().isPresent()) {
457             projectSettingsFile =
458                     context.cwdResolver.apply(mavenOptions.altProjectSettings().get());
459 
460             if (!Files.isRegularFile(projectSettingsFile)) {
461                 throw new FileNotFoundException(
462                         "The specified project settings file does not exist: " + projectSettingsFile);
463             }
464         } else {
465             String projectSettingsFileStr =
466                     context.protoSession.getUserProperties().get(Constants.MAVEN_PROJECT_SETTINGS);
467             if (projectSettingsFileStr != null) {
468                 projectSettingsFile = context.cwdResolver.apply(projectSettingsFileStr);
469             }
470         }
471 
472         Path installationSettingsFile = null;
473 
474         if (mavenOptions.altInstallationSettings().isPresent()) {
475             installationSettingsFile = context.cwdResolver.apply(
476                     mavenOptions.altInstallationSettings().get());
477 
478             if (!Files.isRegularFile(installationSettingsFile)) {
479                 throw new FileNotFoundException(
480                         "The specified installation settings file does not exist: " + installationSettingsFile);
481             }
482         } else {
483             String installationSettingsFileStr =
484                     context.protoSession.getUserProperties().get(Constants.MAVEN_INSTALLATION_SETTINGS);
485             if (installationSettingsFileStr != null) {
486                 installationSettingsFile = context.installationResolver.apply(installationSettingsFileStr);
487             }
488         }
489 
490         Function<String, String> interpolationSource = Interpolator.chain(
491                 context.protoSession.getUserProperties()::get, context.protoSession.getSystemProperties()::get);
492         SettingsBuilderRequest settingsRequest = SettingsBuilderRequest.builder()
493                 .session(context.protoSession)
494                 .installationSettingsSource(
495                         installationSettingsFile != null && Files.exists(installationSettingsFile)
496                                 ? Source.fromPath(installationSettingsFile)
497                                 : null)
498                 .projectSettingsSource(
499                         projectSettingsFile != null && Files.exists(projectSettingsFile)
500                                 ? Source.fromPath(projectSettingsFile)
501                                 : null)
502                 .userSettingsSource(
503                         userSettingsFile != null && Files.exists(userSettingsFile)
504                                 ? Source.fromPath(userSettingsFile)
505                                 : null)
506                 .interpolationSource(interpolationSource)
507                 .build();
508 
509         customizeSettingsRequest(context, settingsRequest);
510 
511         context.logger.debug("Reading installation settings from '" + installationSettingsFile + "'");
512         context.logger.debug("Reading project settings from '" + projectSettingsFile + "'");
513         context.logger.debug("Reading user settings from '" + userSettingsFile + "'");
514 
515         SettingsBuilderResult settingsResult = settingsBuilder.build(settingsRequest);
516         customizeSettingsResult(context, settingsResult);
517 
518         context.effectiveSettings = settingsResult.getEffectiveSettings();
519         context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode());
520         context.localRepositoryPath = localRepositoryPath(context);
521 
522         if (!settingsResult.getProblems().isEmpty()) {
523             context.logger.warn("");
524             context.logger.warn("Some problems were encountered while building the effective settings");
525 
526             for (BuilderProblem problem : settingsResult.getProblems()) {
527                 context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
528             }
529             context.logger.warn("");
530         }
531     }
532 
533     protected void customizeSettingsRequest(C context, SettingsBuilderRequest settingsBuilderRequest)
534             throws Exception {}
535 
536     protected void customizeSettingsResult(C context, SettingsBuilderResult settingsBuilderResult) throws Exception {}
537 
538     protected boolean mayDisableInteractiveMode(C context, boolean proposedInteractive) {
539         if (!context.invokerRequest.options().forceInteractive().orElse(false)) {
540             if (context.invokerRequest.options().nonInteractive().orElse(false)) {
541                 return false;
542             } else {
543                 boolean runningOnCI = isRunningOnCI(context);
544                 if (runningOnCI) {
545                     context.logger.info(
546                             "Making this build non-interactive, because the environment variable CI equals \"true\"."
547                                     + " Disable this detection by removing that variable or adding --force-interactive.");
548                     return false;
549                 }
550             }
551         }
552         return proposedInteractive;
553     }
554 
555     protected Path localRepositoryPath(C context) {
556         // user override
557         String userDefinedLocalRepo = context.protoSession.getUserProperties().get(Constants.MAVEN_REPO_LOCAL);
558         if (userDefinedLocalRepo == null) {
559             userDefinedLocalRepo = context.protoSession.getUserProperties().get(Constants.MAVEN_REPO_LOCAL);
560             if (userDefinedLocalRepo != null) {
561                 context.logger.warn("The property '" + Constants.MAVEN_REPO_LOCAL
562                         + "' has been set using a JVM system property which is deprecated. "
563                         + "The property can be passed as a Maven argument or in the Maven project configuration file,"
564                         + "usually located at ${session.rootDirectory}/.mvn/maven.properties.");
565             }
566         }
567         if (userDefinedLocalRepo != null) {
568             return context.cwdResolver.apply(userDefinedLocalRepo);
569         }
570         // settings
571         userDefinedLocalRepo = context.effectiveSettings.getLocalRepository();
572         if (userDefinedLocalRepo != null && !userDefinedLocalRepo.isEmpty()) {
573             return context.userResolver.apply(userDefinedLocalRepo);
574         }
575         // defaults
576         return context.userResolver
577                 .apply(context.protoSession.getUserProperties().get(Constants.MAVEN_USER_CONF))
578                 .resolve("repository");
579     }
580 
581     protected void populateRequest(C context, Lookup lookup, MavenExecutionRequest request) throws Exception {
582         populateRequestFromSettings(request, context.effectiveSettings);
583 
584         Options options = context.invokerRequest.options();
585         request.setLoggingLevel(toMavenExecutionRequestLoggingLevel(context.loggerLevel));
586         request.setLocalRepositoryPath(context.localRepositoryPath.toFile());
587         request.setLocalRepository(createLocalArtifactRepository(context.localRepositoryPath));
588 
589         request.setInteractiveMode(context.interactive);
590         request.setShowErrors(options.showErrors().orElse(false));
591         request.setBaseDirectory(context.invokerRequest.topDirectory().toFile());
592         request.setSystemProperties(toProperties(context.protoSession.getSystemProperties()));
593         request.setUserProperties(toProperties(context.protoSession.getUserProperties()));
594 
595         request.setTopDirectory(context.invokerRequest.topDirectory());
596         if (context.invokerRequest.rootDirectory().isPresent()) {
597             request.setMultiModuleProjectDirectory(
598                     context.invokerRequest.rootDirectory().get().toFile());
599             request.setRootDirectory(context.invokerRequest.rootDirectory().get());
600         }
601 
602         request.addPluginGroup("org.apache.maven.plugins");
603         request.addPluginGroup("org.codehaus.mojo");
604     }
605 
606     /**
607      * TODO: get rid of this!!!
608      */
609     @Deprecated
610     private ArtifactRepository createLocalArtifactRepository(Path baseDirectory) {
611         DefaultRepositoryLayout layout = new DefaultRepositoryLayout();
612         ArtifactRepositoryPolicy blah = new ArtifactRepositoryPolicy(
613                 true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE);
614         return new MavenArtifactRepository(
615                 "local", "file://" + baseDirectory.toUri().getRawPath(), layout, blah, blah);
616     }
617 
618     protected void populateRequestFromSettings(MavenExecutionRequest request, Settings settings) throws Exception {
619         if (settings == null) {
620             return;
621         }
622         request.setOffline(settings.isOffline());
623         request.setInteractiveMode(settings.isInteractiveMode());
624         request.setPluginGroups(settings.getPluginGroups());
625         request.setLocalRepositoryPath(settings.getLocalRepository());
626         for (Server server : settings.getServers()) {
627             request.addServer(new org.apache.maven.settings.Server(server));
628         }
629 
630         //  <proxies>
631         //    <proxy>
632         //      <active>true</active>
633         //      <protocol>http</protocol>
634         //      <host>proxy.somewhere.com</host>
635         //      <port>8080</port>
636         //      <username>proxyuser</username>
637         //      <password>somepassword</password>
638         //      <nonProxyHosts>www.google.com|*.somewhere.com</nonProxyHosts>
639         //    </proxy>
640         //  </proxies>
641 
642         for (Proxy proxy : settings.getProxies()) {
643             if (!proxy.isActive()) {
644                 continue;
645             }
646             request.addProxy(new org.apache.maven.settings.Proxy(proxy));
647         }
648 
649         // <mirrors>
650         //   <mirror>
651         //     <id>nexus</id>
652         //     <mirrorOf>*</mirrorOf>
653         //     <url>http://repository.sonatype.org/content/groups/public</url>
654         //   </mirror>
655         // </mirrors>
656 
657         for (Mirror mirror : settings.getMirrors()) {
658             request.addMirror(new org.apache.maven.settings.Mirror(mirror));
659         }
660 
661         for (Repository remoteRepository : settings.getRepositories()) {
662             try {
663                 request.addRemoteRepository(MavenRepositorySystem.buildArtifactRepository(
664                         new org.apache.maven.settings.Repository(remoteRepository)));
665             } catch (InvalidRepositoryException e) {
666                 // do nothing for now
667             }
668         }
669 
670         for (Repository pluginRepository : settings.getPluginRepositories()) {
671             try {
672                 request.addPluginArtifactRepository(MavenRepositorySystem.buildArtifactRepository(
673                         new org.apache.maven.settings.Repository(pluginRepository)));
674             } catch (InvalidRepositoryException e) {
675                 // do nothing for now
676             }
677         }
678 
679         request.setActiveProfiles(settings.getActiveProfiles());
680         for (Profile rawProfile : settings.getProfiles()) {
681             request.addProfile(
682                     new org.apache.maven.model.Profile(SettingsUtilsV4.convertFromSettingsProfile(rawProfile)));
683 
684             if (settings.getActiveProfiles().contains(rawProfile.getId())) {
685                 List<Repository> remoteRepositories = rawProfile.getRepositories();
686                 for (Repository remoteRepository : remoteRepositories) {
687                     try {
688                         request.addRemoteRepository(MavenRepositorySystem.buildArtifactRepository(
689                                 new org.apache.maven.settings.Repository(remoteRepository)));
690                     } catch (InvalidRepositoryException e) {
691                         // do nothing for now
692                     }
693                 }
694 
695                 List<Repository> pluginRepositories = rawProfile.getPluginRepositories();
696                 for (Repository pluginRepository : pluginRepositories) {
697                     try {
698                         request.addPluginArtifactRepository(MavenRepositorySystem.buildArtifactRepository(
699                                 new org.apache.maven.settings.Repository(pluginRepository)));
700                     } catch (InvalidRepositoryException e) {
701                         // do nothing for now
702                     }
703                 }
704             }
705         }
706     }
707 
708     protected int calculateDegreeOfConcurrency(String threadConfiguration) {
709         try {
710             if (threadConfiguration.endsWith("C")) {
711                 String str = threadConfiguration.substring(0, threadConfiguration.length() - 1);
712                 float coreMultiplier = Float.parseFloat(str);
713 
714                 if (coreMultiplier <= 0.0f) {
715                     throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration
716                             + "'. Value must be positive.");
717                 }
718 
719                 int procs = Runtime.getRuntime().availableProcessors();
720                 int threads = (int) (coreMultiplier * procs);
721                 return threads == 0 ? 1 : threads;
722             } else {
723                 int threads = Integer.parseInt(threadConfiguration);
724                 if (threads <= 0) {
725                     throw new IllegalArgumentException(
726                             "Invalid threads value: '" + threadConfiguration + "'. Value must be positive.");
727                 }
728                 return threads;
729             }
730         } catch (NumberFormatException e) {
731             throw new IllegalArgumentException("Invalid threads value: '" + threadConfiguration
732                     + "'. Supported are int and float values ending with C.");
733         }
734     }
735 
736     protected boolean isRunningOnCI(C context) {
737         String ciEnv = context.protoSession.getSystemProperties().get("env.CI");
738         return ciEnv != null && !"false".equals(ciEnv);
739     }
740 
741     protected abstract int execute(C context) throws Exception;
742 }