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.cli;
20  
21  import java.io.BufferedInputStream;
22  import java.io.Console;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileNotFoundException;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.PrintStream;
30  import java.nio.charset.Charset;
31  import java.nio.file.Files;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.HashSet;
35  import java.util.LinkedHashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Map.Entry;
39  import java.util.Properties;
40  import java.util.Set;
41  import java.util.StringTokenizer;
42  import java.util.regex.Matcher;
43  import java.util.regex.Pattern;
44  import java.util.stream.Stream;
45  
46  import com.google.inject.AbstractModule;
47  import org.apache.commons.cli.CommandLine;
48  import org.apache.commons.cli.Option;
49  import org.apache.commons.cli.ParseException;
50  import org.apache.commons.cli.UnrecognizedOptionException;
51  import org.apache.commons.lang3.math.NumberUtils;
52  import org.apache.maven.BuildAbort;
53  import org.apache.maven.InternalErrorException;
54  import org.apache.maven.Maven;
55  import org.apache.maven.building.FileSource;
56  import org.apache.maven.building.Problem;
57  import org.apache.maven.building.Source;
58  import org.apache.maven.cli.configuration.ConfigurationProcessor;
59  import org.apache.maven.cli.configuration.SettingsXmlConfigurationProcessor;
60  import org.apache.maven.cli.event.DefaultEventSpyContext;
61  import org.apache.maven.cli.event.ExecutionEventLogger;
62  import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
63  import org.apache.maven.cli.internal.extension.model.CoreExtension;
64  import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
65  import org.apache.maven.cli.logging.Slf4jConfiguration;
66  import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
67  import org.apache.maven.cli.logging.Slf4jLoggerManager;
68  import org.apache.maven.cli.logging.Slf4jStdoutLogger;
69  import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
70  import org.apache.maven.cli.transfer.QuietMavenTransferListener;
71  import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
72  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
73  import org.apache.maven.exception.DefaultExceptionHandler;
74  import org.apache.maven.exception.ExceptionHandler;
75  import org.apache.maven.exception.ExceptionSummary;
76  import org.apache.maven.execution.DefaultMavenExecutionRequest;
77  import org.apache.maven.execution.ExecutionListener;
78  import org.apache.maven.execution.MavenExecutionRequest;
79  import org.apache.maven.execution.MavenExecutionRequestPopulationException;
80  import org.apache.maven.execution.MavenExecutionRequestPopulator;
81  import org.apache.maven.execution.MavenExecutionResult;
82  import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
83  import org.apache.maven.extension.internal.CoreExports;
84  import org.apache.maven.extension.internal.CoreExtensionEntry;
85  import org.apache.maven.lifecycle.LifecycleExecutionException;
86  import org.apache.maven.model.building.ModelProcessor;
87  import org.apache.maven.project.MavenProject;
88  import org.apache.maven.properties.internal.EnvironmentUtils;
89  import org.apache.maven.properties.internal.SystemProperties;
90  import org.apache.maven.session.scope.internal.SessionScopeModule;
91  import org.apache.maven.shared.utils.logging.MessageBuilder;
92  import org.apache.maven.shared.utils.logging.MessageUtils;
93  import org.apache.maven.toolchain.building.DefaultToolchainsBuildingRequest;
94  import org.apache.maven.toolchain.building.ToolchainsBuilder;
95  import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
96  import org.codehaus.plexus.ContainerConfiguration;
97  import org.codehaus.plexus.DefaultContainerConfiguration;
98  import org.codehaus.plexus.DefaultPlexusContainer;
99  import org.codehaus.plexus.PlexusConstants;
100 import org.codehaus.plexus.PlexusContainer;
101 import org.codehaus.plexus.classworlds.ClassWorld;
102 import org.codehaus.plexus.classworlds.realm.ClassRealm;
103 import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
104 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
105 import org.codehaus.plexus.logging.LoggerManager;
106 import org.codehaus.plexus.util.StringUtils;
107 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
108 import org.eclipse.aether.transfer.TransferListener;
109 import org.slf4j.ILoggerFactory;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
112 import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
113 import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
114 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
115 import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
116 import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
117 
118 import static org.apache.maven.cli.CLIManager.COLOR;
119 import static org.apache.maven.cli.ResolveFile.resolveFile;
120 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
121 
122 // TODO push all common bits back to plexus cli and prepare for transition to Guice. We don't need 50 ways to make CLIs
123 
124 /**
125  * @author Jason van Zyl
126  */
127 public class MavenCli {
128     public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
129 
130     public static final String MULTIMODULE_PROJECT_DIRECTORY = "maven.multiModuleProjectDirectory";
131 
132     public static final String USER_HOME = System.getProperty("user.home");
133 
134     public static final File USER_MAVEN_CONFIGURATION_HOME = new File(USER_HOME, ".m2");
135 
136     public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File(USER_MAVEN_CONFIGURATION_HOME, "toolchains.xml");
137 
138     public static final File DEFAULT_GLOBAL_TOOLCHAINS_FILE =
139             new File(System.getProperty("maven.conf"), "toolchains.xml");
140 
141     private static final String EXT_CLASS_PATH = "maven.ext.class.path";
142 
143     private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
144 
145     private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config";
146 
147     public static final String STYLE_COLOR_PROPERTY = "style.color";
148 
149     private ClassWorld classWorld;
150 
151     private LoggerManager plexusLoggerManager;
152 
153     private ILoggerFactory slf4jLoggerFactory;
154 
155     private Logger slf4jLogger;
156 
157     private EventSpyDispatcher eventSpyDispatcher;
158 
159     private ModelProcessor modelProcessor;
160 
161     private Maven maven;
162 
163     private MavenExecutionRequestPopulator executionRequestPopulator;
164 
165     private ToolchainsBuilder toolchainsBuilder;
166 
167     private DefaultSecDispatcher dispatcher;
168 
169     private Map<String, ConfigurationProcessor> configurationProcessors;
170 
171     private CLIManager cliManager;
172 
173     private static final Pattern NEXT_LINE = Pattern.compile("\r?\n");
174 
175     public MavenCli() {
176         this(null);
177     }
178 
179     // This supports painless invocation by the Verifier during embedded execution of the core ITs
180     public MavenCli(ClassWorld classWorld) {
181         this.classWorld = classWorld;
182     }
183 
184     public static void main(String[] args) {
185         int result = main(args, null);
186 
187         System.exit(result);
188     }
189 
190     public static int main(String[] args, ClassWorld classWorld) {
191         MavenCli cli = new MavenCli();
192 
193         MessageUtils.systemInstall();
194         MessageUtils.registerShutdownHook();
195         int result = cli.doMain(new CliRequest(args, classWorld));
196         MessageUtils.systemUninstall();
197 
198         return result;
199     }
200 
201     // TODO need to externalize CliRequest
202     public static int doMain(String[] args, ClassWorld classWorld) {
203         MavenCli cli = new MavenCli();
204         return cli.doMain(new CliRequest(args, classWorld));
205     }
206 
207     /**
208      * This supports painless invocation by the Verifier during embedded execution of the core ITs.
209      * See <a href="http://maven.apache.org/shared/maven-verifier/xref/org/apache/maven/it/Embedded3xLauncher.html">
210      * <code>Embedded3xLauncher</code> in <code>maven-verifier</code></a>
211      */
212     public int doMain(String[] args, String workingDirectory, PrintStream stdout, PrintStream stderr) {
213         PrintStream oldout = System.out;
214         PrintStream olderr = System.err;
215 
216         final Set<String> realms;
217         if (classWorld != null) {
218             realms = new HashSet<>();
219             for (ClassRealm realm : classWorld.getRealms()) {
220                 realms.add(realm.getId());
221             }
222         } else {
223             realms = Collections.emptySet();
224         }
225 
226         try {
227             if (stdout != null) {
228                 System.setOut(stdout);
229             }
230             if (stderr != null) {
231                 System.setErr(stderr);
232             }
233 
234             CliRequest cliRequest = new CliRequest(args, classWorld);
235             cliRequest.workingDirectory = workingDirectory;
236 
237             return doMain(cliRequest);
238         } finally {
239             if (classWorld != null) {
240                 for (ClassRealm realm : new ArrayList<>(classWorld.getRealms())) {
241                     String realmId = realm.getId();
242                     if (!realms.contains(realmId)) {
243                         try {
244                             classWorld.disposeRealm(realmId);
245                         } catch (NoSuchRealmException ignored) {
246                             // can't happen
247                         }
248                     }
249                 }
250             }
251             System.setOut(oldout);
252             System.setErr(olderr);
253         }
254     }
255 
256     // TODO need to externalize CliRequest
257     public int doMain(CliRequest cliRequest) {
258         PlexusContainer localContainer = null;
259         try {
260             initialize(cliRequest);
261             cli(cliRequest);
262             properties(cliRequest);
263             logging(cliRequest);
264             informativeCommands(cliRequest);
265             version(cliRequest);
266             localContainer = container(cliRequest);
267             commands(cliRequest);
268             configure(cliRequest);
269             toolchains(cliRequest);
270             populateRequest(cliRequest);
271             encryption(cliRequest);
272             return execute(cliRequest);
273         } catch (ExitException e) {
274             return e.exitCode;
275         } catch (UnrecognizedOptionException e) {
276             // pure user error, suppress stack trace
277             return 1;
278         } catch (BuildAbort e) {
279             CLIReportingUtils.showError(slf4jLogger, "ABORTED", e, cliRequest.showErrors);
280 
281             return 2;
282         } catch (Exception e) {
283             CLIReportingUtils.showError(slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors);
284 
285             return 1;
286         } finally {
287             if (localContainer != null) {
288                 localContainer.dispose();
289             }
290         }
291     }
292 
293     void initialize(CliRequest cliRequest) throws ExitException {
294         if (cliRequest.workingDirectory == null) {
295             cliRequest.workingDirectory = System.getProperty("user.dir");
296         }
297 
298         if (cliRequest.multiModuleProjectDirectory == null) {
299             String basedirProperty = System.getProperty(MULTIMODULE_PROJECT_DIRECTORY);
300             if (basedirProperty == null) {
301                 System.err.format("-D%s system property is not set.", MULTIMODULE_PROJECT_DIRECTORY);
302                 throw new ExitException(1);
303             }
304             File basedir = basedirProperty != null ? new File(basedirProperty) : new File("");
305             try {
306                 cliRequest.multiModuleProjectDirectory = basedir.getCanonicalFile();
307             } catch (IOException e) {
308                 cliRequest.multiModuleProjectDirectory = basedir.getAbsoluteFile();
309             }
310         }
311 
312         //
313         // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
314         // Windows paths.
315         //
316         String mavenHome = System.getProperty("maven.home");
317 
318         if (mavenHome != null) {
319             System.setProperty("maven.home", new File(mavenHome).getAbsolutePath());
320         }
321     }
322 
323     void cli(CliRequest cliRequest) throws Exception {
324         //
325         // Parsing errors can happen during the processing of the arguments and we prefer not having to check if
326         // the logger is null and construct this so we can use an SLF4J logger everywhere.
327         //
328         slf4jLogger = new Slf4jStdoutLogger();
329 
330         cliManager = new CLIManager();
331 
332         CommandLine mavenConfig = null;
333         try {
334             File configFile = new File(cliRequest.multiModuleProjectDirectory, MVN_MAVEN_CONFIG);
335 
336             if (configFile.isFile()) {
337                 try (Stream<String> lines = Files.lines(configFile.toPath(), Charset.defaultCharset())) {
338                     String[] args = lines.filter(arg -> !arg.isEmpty()).toArray(String[]::new);
339                     mavenConfig = cliManager.parse(args);
340                     List<?> unrecognized = mavenConfig.getArgList();
341                     if (!unrecognized.isEmpty()) {
342                         // This file can only contain options, not args (goals or phases)
343                         throw new ParseException("Unrecognized maven.config file entries: " + unrecognized);
344                     }
345                 }
346             }
347         } catch (ParseException e) {
348             System.err.println("Unable to parse maven.config file options: " + e.getMessage());
349             cliManager.displayHelp(System.out);
350             throw e;
351         }
352 
353         try {
354             CommandLine mavenCli = cliManager.parse(cliRequest.args);
355             if (mavenConfig == null) {
356                 cliRequest.commandLine = mavenCli;
357             } else {
358                 cliRequest.commandLine = cliMerge(mavenConfig, mavenCli);
359             }
360         } catch (ParseException e) {
361             System.err.println("Unable to parse command line options: " + e.getMessage());
362             cliManager.displayHelp(System.out);
363             throw e;
364         }
365 
366         // check for presence of unsupported command line options
367         try {
368             if (cliRequest.commandLine.hasOption("llr")) {
369                 throw new UnrecognizedOptionException("Option '-llr' is not supported starting with Maven 3.9.1");
370             }
371         } catch (ParseException e) {
372             System.err.println("Unsupported options: " + e.getMessage());
373             cliManager.displayHelp(System.out);
374             throw e;
375         }
376     }
377 
378     private void informativeCommands(CliRequest cliRequest) throws ExitException {
379         if (cliRequest.commandLine.hasOption(CLIManager.HELP)) {
380             cliManager.displayHelp(System.out);
381             throw new ExitException(0);
382         }
383 
384         if (cliRequest.commandLine.hasOption(CLIManager.VERSION)) {
385             if (cliRequest.commandLine.hasOption(CLIManager.QUIET)) {
386                 System.out.println(CLIReportingUtils.showVersionMinimal());
387             } else {
388                 System.out.println(CLIReportingUtils.showVersion());
389             }
390             throw new ExitException(0);
391         }
392     }
393 
394     private CommandLine cliMerge(CommandLine mavenConfig, CommandLine mavenCli) {
395         CommandLine.Builder commandLineBuilder = new CommandLine.Builder();
396 
397         // the args are easy, CLI only since maven.config file can only contain options
398         for (String arg : mavenCli.getArgs()) {
399             commandLineBuilder.addArg(arg);
400         }
401 
402         /* Although this looks wrong in terms of order Commons CLI stores the value of options in
403          * an array and when a value is potentionally overriden it is added to the array. The single
404          * arg option value is retrieved and instead of returning values[values.length-1] it returns
405          * values[0] which means that the original value instead of the overridden one is returned
406          * (first wins). With properties values are truely overriden since at the end a map is used
407          * to merge which means last wins.
408          *
409          * TODO Report this behavioral bug with Commons CLI
410          */
411         // now add all options, except for user properties with CLI first then maven.config file
412         List<Option> setPropertyOptions = new ArrayList<>();
413         for (Option opt : mavenCli.getOptions()) {
414             if (String.valueOf(CLIManager.SET_USER_PROPERTY).equals(opt.getOpt())) {
415                 setPropertyOptions.add(opt);
416             } else {
417                 commandLineBuilder.addOption(opt);
418             }
419         }
420         for (Option opt : mavenConfig.getOptions()) {
421             commandLineBuilder.addOption(opt);
422         }
423         // finally add the CLI user properties
424         for (Option opt : setPropertyOptions) {
425             commandLineBuilder.addOption(opt);
426         }
427         return commandLineBuilder.build();
428     }
429 
430     /**
431      * configure logging
432      */
433     void logging(CliRequest cliRequest) {
434         // LOG LEVEL
435         cliRequest.debug = cliRequest.commandLine.hasOption(CLIManager.DEBUG);
436         cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption(CLIManager.QUIET);
437         cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.ERRORS);
438 
439         slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
440         Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration(slf4jLoggerFactory);
441 
442         if (cliRequest.debug) {
443             cliRequest.request.setLoggingLevel(MavenExecutionRequest.LOGGING_LEVEL_DEBUG);
444             slf4jConfiguration.setRootLoggerLevel(Slf4jConfiguration.Level.DEBUG);
445         } else if (cliRequest.quiet) {
446             cliRequest.request.setLoggingLevel(MavenExecutionRequest.LOGGING_LEVEL_ERROR);
447             slf4jConfiguration.setRootLoggerLevel(Slf4jConfiguration.Level.ERROR);
448         }
449         // else fall back to default log level specified in conf
450         // see https://issues.apache.org/jira/browse/MNG-2570
451 
452         // LOG COLOR
453         String styleColor = cliRequest.getUserProperties().getProperty(STYLE_COLOR_PROPERTY, "auto");
454         styleColor = cliRequest.commandLine.getOptionValue(COLOR, styleColor);
455         if ("always".equals(styleColor) || "yes".equals(styleColor) || "force".equals(styleColor)) {
456             MessageUtils.setColorEnabled(true);
457         } else if ("never".equals(styleColor) || "no".equals(styleColor) || "none".equals(styleColor)) {
458             MessageUtils.setColorEnabled(false);
459         } else if (!"auto".equals(styleColor) && !"tty".equals(styleColor) && !"if-tty".equals(styleColor)) {
460             throw new IllegalArgumentException(
461                     "Invalid color configuration value '" + styleColor + "'. Supported are 'auto', 'always', 'never'.");
462         } else if (cliRequest.commandLine.hasOption(CLIManager.BATCH_MODE)
463                 || cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
464             MessageUtils.setColorEnabled(false);
465         }
466 
467         // LOG STREAMS
468         if (cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
469             File logFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.LOG_FILE));
470             logFile = resolveFile(logFile, cliRequest.workingDirectory);
471 
472             // redirect stdout and stderr to file
473             try {
474                 PrintStream ps = new PrintStream(new FileOutputStream(logFile));
475                 System.setOut(ps);
476                 System.setErr(ps);
477             } catch (FileNotFoundException e) {
478                 //
479                 // Ignore
480                 //
481             }
482         }
483 
484         slf4jConfiguration.activate();
485 
486         plexusLoggerManager = new Slf4jLoggerManager();
487         slf4jLogger = slf4jLoggerFactory.getLogger(this.getClass().getName());
488     }
489 
490     private void version(CliRequest cliRequest) {
491         if (cliRequest.debug || cliRequest.commandLine.hasOption(CLIManager.SHOW_VERSION)) {
492             System.out.println(CLIReportingUtils.showVersion());
493         }
494     }
495 
496     private void commands(CliRequest cliRequest) {
497         if (cliRequest.showErrors) {
498             slf4jLogger.info("Error stacktraces are turned on.");
499         }
500 
501         if (MavenExecutionRequest.CHECKSUM_POLICY_WARN.equals(cliRequest.request.getGlobalChecksumPolicy())) {
502             slf4jLogger.info("Disabling strict checksum verification on all artifact downloads.");
503         } else if (MavenExecutionRequest.CHECKSUM_POLICY_FAIL.equals(cliRequest.request.getGlobalChecksumPolicy())) {
504             slf4jLogger.info("Enabling strict checksum verification on all artifact downloads.");
505         }
506 
507         if (slf4jLogger.isDebugEnabled()) {
508             slf4jLogger.debug("Message scheme: {}", (MessageUtils.isColorEnabled() ? "color" : "plain"));
509             if (MessageUtils.isColorEnabled()) {
510                 MessageBuilder buff = MessageUtils.buffer();
511                 buff.a("Message styles: ");
512                 buff.a(MessageUtils.level().debug("debug")).a(' ');
513                 buff.a(MessageUtils.level().info("info")).a(' ');
514                 buff.a(MessageUtils.level().warning("warning")).a(' ');
515                 buff.a(MessageUtils.level().error("error")).a(' ');
516 
517                 buff.success("success").a(' ');
518                 buff.failure("failure").a(' ');
519                 buff.strong("strong").a(' ');
520                 buff.mojo("mojo").a(' ');
521                 buff.project("project");
522                 slf4jLogger.debug(buff.toString());
523             }
524         }
525     }
526 
527     // Needed to make this method package visible to make writing a unit test possible
528     // Maybe it's better to move some of those methods to separate class (SoC).
529     void properties(CliRequest cliRequest) {
530         populateProperties(cliRequest.commandLine, cliRequest.systemProperties, cliRequest.userProperties);
531     }
532 
533     PlexusContainer container(CliRequest cliRequest) throws Exception {
534         if (cliRequest.classWorld == null) {
535             cliRequest.classWorld =
536                     new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader());
537         }
538 
539         ClassRealm coreRealm = cliRequest.classWorld.getClassRealm("plexus.core");
540         if (coreRealm == null) {
541             coreRealm = cliRequest.classWorld.getRealms().iterator().next();
542         }
543 
544         List<File> extClassPath = parseExtClasspath(cliRequest);
545 
546         CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm);
547         List<CoreExtensionEntry> extensions =
548                 loadCoreExtensions(cliRequest, coreRealm, coreEntry.getExportedArtifacts());
549 
550         ClassRealm containerRealm = setupContainerRealm(cliRequest.classWorld, coreRealm, extClassPath, extensions);
551 
552         ContainerConfiguration cc = new DefaultContainerConfiguration()
553                 .setClassWorld(cliRequest.classWorld)
554                 .setRealm(containerRealm)
555                 .setClassPathScanning(PlexusConstants.SCANNING_INDEX)
556                 .setAutoWiring(true)
557                 .setJSR250Lifecycle(true)
558                 .setName("maven");
559 
560         Set<String> exportedArtifacts = new HashSet<>(coreEntry.getExportedArtifacts());
561         Set<String> exportedPackages = new HashSet<>(coreEntry.getExportedPackages());
562         for (CoreExtensionEntry extension : extensions) {
563             exportedArtifacts.addAll(extension.getExportedArtifacts());
564             exportedPackages.addAll(extension.getExportedPackages());
565         }
566 
567         final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);
568 
569         DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
570             @Override
571             protected void configure() {
572                 bind(ILoggerFactory.class).toInstance(slf4jLoggerFactory);
573                 bind(CoreExports.class).toInstance(exports);
574             }
575         });
576 
577         // NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
578         container.setLookupRealm(null);
579         Thread.currentThread().setContextClassLoader(container.getContainerRealm());
580 
581         container.setLoggerManager(plexusLoggerManager);
582 
583         for (CoreExtensionEntry extension : extensions) {
584             container.discoverComponents(
585                     extension.getClassRealm(),
586                     new SessionScopeModule(container),
587                     new MojoExecutionScopeModule(container));
588         }
589 
590         customizeContainer(container);
591 
592         container.getLoggerManager().setThresholds(cliRequest.request.getLoggingLevel());
593 
594         eventSpyDispatcher = container.lookup(EventSpyDispatcher.class);
595 
596         DefaultEventSpyContext eventSpyContext = new DefaultEventSpyContext();
597         Map<String, Object> data = eventSpyContext.getData();
598         data.put("plexus", container);
599         data.put("workingDirectory", cliRequest.workingDirectory);
600         data.put("systemProperties", cliRequest.systemProperties);
601         data.put("userProperties", cliRequest.userProperties);
602         data.put("versionProperties", CLIReportingUtils.getBuildProperties());
603         eventSpyDispatcher.init(eventSpyContext);
604 
605         // refresh logger in case container got customized by spy
606         slf4jLogger = slf4jLoggerFactory.getLogger(this.getClass().getName());
607 
608         maven = container.lookup(Maven.class);
609 
610         executionRequestPopulator = container.lookup(MavenExecutionRequestPopulator.class);
611 
612         modelProcessor = createModelProcessor(container);
613 
614         configurationProcessors = container.lookupMap(ConfigurationProcessor.class);
615 
616         toolchainsBuilder = container.lookup(ToolchainsBuilder.class);
617 
618         dispatcher = (DefaultSecDispatcher) container.lookup(SecDispatcher.class, "maven");
619 
620         return container;
621     }
622 
623     private List<CoreExtensionEntry> loadCoreExtensions(
624             CliRequest cliRequest, ClassRealm containerRealm, Set<String> providedArtifacts) throws Exception {
625         if (cliRequest.multiModuleProjectDirectory == null) {
626             return Collections.emptyList();
627         }
628 
629         File extensionsFile = new File(cliRequest.multiModuleProjectDirectory, EXTENSIONS_FILENAME);
630         if (!extensionsFile.isFile()) {
631             return Collections.emptyList();
632         }
633 
634         List<CoreExtension> extensions = readCoreExtensionsDescriptor(extensionsFile);
635         if (extensions.isEmpty()) {
636             return Collections.emptyList();
637         }
638 
639         ContainerConfiguration cc = new DefaultContainerConfiguration() //
640                 .setClassWorld(cliRequest.classWorld) //
641                 .setRealm(containerRealm) //
642                 .setClassPathScanning(PlexusConstants.SCANNING_INDEX) //
643                 .setAutoWiring(true) //
644                 .setJSR250Lifecycle(true) //
645                 .setName("maven");
646 
647         DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
648             @Override
649             protected void configure() {
650                 bind(ILoggerFactory.class).toInstance(slf4jLoggerFactory);
651             }
652         });
653 
654         try {
655             container.setLookupRealm(null);
656 
657             container.setLoggerManager(plexusLoggerManager);
658 
659             container.getLoggerManager().setThresholds(cliRequest.request.getLoggingLevel());
660 
661             Thread.currentThread().setContextClassLoader(container.getContainerRealm());
662 
663             executionRequestPopulator = container.lookup(MavenExecutionRequestPopulator.class);
664 
665             configurationProcessors = container.lookupMap(ConfigurationProcessor.class);
666 
667             configure(cliRequest);
668 
669             MavenExecutionRequest request = DefaultMavenExecutionRequest.copy(cliRequest.request);
670 
671             request = populateRequest(cliRequest, request);
672 
673             request = executionRequestPopulator.populateDefaults(request);
674 
675             BootstrapCoreExtensionManager resolver = container.lookup(BootstrapCoreExtensionManager.class);
676 
677             return Collections.unmodifiableList(resolver.loadCoreExtensions(request, providedArtifacts, extensions));
678 
679         } finally {
680             executionRequestPopulator = null;
681             container.dispose();
682         }
683     }
684 
685     private List<CoreExtension> readCoreExtensionsDescriptor(File extensionsFile)
686             throws IOException, XmlPullParserException {
687         CoreExtensionsXpp3Reader parser = new CoreExtensionsXpp3Reader();
688 
689         try (InputStream is = new BufferedInputStream(new FileInputStream(extensionsFile))) {
690 
691             return parser.read(is).getExtensions();
692         }
693     }
694 
695     private ClassRealm setupContainerRealm(
696             ClassWorld classWorld, ClassRealm coreRealm, List<File> extClassPath, List<CoreExtensionEntry> extensions)
697             throws Exception {
698         if (!extClassPath.isEmpty() || !extensions.isEmpty()) {
699             ClassRealm extRealm = classWorld.newRealm("maven.ext", null);
700 
701             extRealm.setParentRealm(coreRealm);
702 
703             slf4jLogger.debug("Populating class realm {}", extRealm.getId());
704 
705             for (File file : extClassPath) {
706                 slf4jLogger.debug("  Included {}", file);
707 
708                 extRealm.addURL(file.toURI().toURL());
709             }
710 
711             for (CoreExtensionEntry entry : reverse(extensions)) {
712                 Set<String> exportedPackages = entry.getExportedPackages();
713                 ClassRealm realm = entry.getClassRealm();
714                 for (String exportedPackage : exportedPackages) {
715                     extRealm.importFrom(realm, exportedPackage);
716                 }
717                 if (exportedPackages.isEmpty()) {
718                     // sisu uses realm imports to establish component visibility
719                     extRealm.importFrom(realm, realm.getId());
720                 }
721             }
722 
723             return extRealm;
724         }
725 
726         return coreRealm;
727     }
728 
729     private static <T> List<T> reverse(List<T> list) {
730         List<T> copy = new ArrayList<>(list);
731         Collections.reverse(copy);
732         return copy;
733     }
734 
735     private List<File> parseExtClasspath(CliRequest cliRequest) {
736         String extClassPath = cliRequest.userProperties.getProperty(EXT_CLASS_PATH);
737         if (extClassPath == null) {
738             extClassPath = cliRequest.systemProperties.getProperty(EXT_CLASS_PATH);
739         }
740 
741         List<File> jars = new ArrayList<>();
742 
743         if (StringUtils.isNotEmpty(extClassPath)) {
744             for (String jar : StringUtils.split(extClassPath, File.pathSeparator)) {
745                 File file = resolveFile(new File(jar), cliRequest.workingDirectory);
746 
747                 slf4jLogger.debug("  Included {}", file);
748 
749                 jars.add(file);
750             }
751         }
752 
753         return jars;
754     }
755 
756     //
757     // This should probably be a separate tool and not be baked into Maven.
758     //
759     private void encryption(CliRequest cliRequest) throws Exception {
760         if (cliRequest.commandLine.hasOption(CLIManager.ENCRYPT_MASTER_PASSWORD)) {
761             String passwd = cliRequest.commandLine.getOptionValue(CLIManager.ENCRYPT_MASTER_PASSWORD);
762 
763             if (passwd == null) {
764                 Console cons = System.console();
765                 char[] password = (cons == null) ? null : cons.readPassword("Master password: ");
766                 if (password != null) {
767                     // Cipher uses Strings
768                     passwd = String.copyValueOf(password);
769 
770                     // Sun/Oracle advises to empty the char array
771                     java.util.Arrays.fill(password, ' ');
772                 }
773             }
774 
775             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
776 
777             System.out.println(cipher.encryptAndDecorate(passwd, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION));
778 
779             throw new ExitException(0);
780         } else if (cliRequest.commandLine.hasOption(CLIManager.ENCRYPT_PASSWORD)) {
781             String passwd = cliRequest.commandLine.getOptionValue(CLIManager.ENCRYPT_PASSWORD);
782 
783             if (passwd == null) {
784                 Console cons = System.console();
785                 char[] password = (cons == null) ? null : cons.readPassword("Password: ");
786                 if (password != null) {
787                     // Cipher uses Strings
788                     passwd = String.copyValueOf(password);
789 
790                     // Sun/Oracle advises to empty the char array
791                     java.util.Arrays.fill(password, ' ');
792                 }
793             }
794 
795             String configurationFile = dispatcher.getConfigurationFile();
796 
797             if (configurationFile.startsWith("~")) {
798                 configurationFile = System.getProperty("user.home") + configurationFile.substring(1);
799             }
800 
801             String file = System.getProperty(DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile);
802 
803             String master = null;
804 
805             SettingsSecurity sec = SecUtil.read(file, true);
806             if (sec != null) {
807                 master = sec.getMaster();
808             }
809 
810             if (master == null) {
811                 throw new IllegalStateException("Master password is not set in the setting security file: " + file);
812             }
813 
814             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
815             String masterPasswd = cipher.decryptDecorated(master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION);
816             System.out.println(cipher.encryptAndDecorate(passwd, masterPasswd));
817 
818             throw new ExitException(0);
819         }
820     }
821 
822     private int execute(CliRequest cliRequest) throws MavenExecutionRequestPopulationException {
823         MavenExecutionRequest request = executionRequestPopulator.populateDefaults(cliRequest.request);
824 
825         eventSpyDispatcher.onEvent(request);
826 
827         MavenExecutionResult result = maven.execute(request);
828 
829         eventSpyDispatcher.onEvent(result);
830 
831         eventSpyDispatcher.close();
832 
833         if (result.hasExceptions()) {
834             ExceptionHandler handler = new DefaultExceptionHandler();
835 
836             Map<String, String> references = new LinkedHashMap<>();
837 
838             MavenProject project = null;
839 
840             for (Throwable exception : result.getExceptions()) {
841                 ExceptionSummary summary = handler.handleException(exception);
842 
843                 logSummary(summary, references, "", cliRequest.showErrors);
844 
845                 if (project == null && exception instanceof LifecycleExecutionException) {
846                     project = ((LifecycleExecutionException) exception).getProject();
847                 }
848             }
849 
850             slf4jLogger.error("");
851 
852             if (!cliRequest.showErrors) {
853                 slf4jLogger.error(
854                         "To see the full stack trace of the errors, re-run Maven with the {} switch.",
855                         buffer().strong("-e"));
856             }
857             if (!slf4jLogger.isDebugEnabled()) {
858                 slf4jLogger.error(
859                         "Re-run Maven using the {} switch to enable full debug logging.", buffer().strong("-X"));
860             }
861 
862             if (!references.isEmpty()) {
863                 slf4jLogger.error("");
864                 slf4jLogger.error("For more information about the errors and possible solutions"
865                         + ", please read the following articles:");
866 
867                 for (Map.Entry<String, String> entry : references.entrySet()) {
868                     slf4jLogger.error("{} {}", buffer().strong(entry.getValue()), entry.getKey());
869                 }
870             }
871 
872             if (project != null
873                     && !project.equals(result.getTopologicallySortedProjects().get(0))) {
874                 slf4jLogger.error("");
875                 slf4jLogger.error("After correcting the problems, you can resume the build with the command");
876                 slf4jLogger.error(buffer().a("  ")
877                         .strong("mvn <args> -rf " + getResumeFrom(result.getTopologicallySortedProjects(), project))
878                         .toString());
879             }
880 
881             if (MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(cliRequest.request.getReactorFailureBehavior())) {
882                 slf4jLogger.info("Build failures were ignored.");
883 
884                 return 0;
885             } else {
886                 return 1;
887             }
888         } else {
889             return 0;
890         }
891     }
892 
893     /**
894      * A helper method to determine the value to resume the build with {@code -rf} taking into account the
895      * edge case where multiple modules in the reactor have the same artifactId.
896      * <p>
897      * {@code -rf :artifactId} will pick up the first module which matches, but when multiple modules in the
898      * reactor have the same artifactId, effective failed module might be later in build reactor.
899      * This means that developer will either have to type groupId or wait for build execution of all modules
900      * which were fine, but they are still before one which reported errors.
901      * <p>Then the returned value is {@code groupId:artifactId} when there is a name clash and
902      * {@code :artifactId} if there is no conflict.
903      *
904      * @param mavenProjects Maven projects which are part of build execution.
905      * @param failedProject Project which has failed.
906      * @return Value for -rf flag to resume build exactly from place where it failed ({@code :artifactId} in
907      *    general and {@code groupId:artifactId} when there is a name clash).
908      */
909     private String getResumeFrom(List<MavenProject> mavenProjects, MavenProject failedProject) {
910         for (MavenProject buildProject : mavenProjects) {
911             if (failedProject.getArtifactId().equals(buildProject.getArtifactId())
912                     && !failedProject.equals(buildProject)) {
913                 return failedProject.getGroupId() + ":" + failedProject.getArtifactId();
914             }
915         }
916         return ":" + failedProject.getArtifactId();
917     }
918 
919     private void logSummary(
920             ExceptionSummary summary, Map<String, String> references, String indent, boolean showErrors) {
921         String referenceKey = "";
922 
923         if (StringUtils.isNotEmpty(summary.getReference())) {
924             referenceKey = references.get(summary.getReference());
925             if (referenceKey == null) {
926                 referenceKey = "[Help " + (references.size() + 1) + "]";
927                 references.put(summary.getReference(), referenceKey);
928             }
929         }
930 
931         String msg = summary.getMessage();
932 
933         if (StringUtils.isNotEmpty(referenceKey)) {
934             if (msg.indexOf('\n') < 0) {
935                 msg += " -> " + buffer().strong(referenceKey);
936             } else {
937                 msg += "\n-> " + buffer().strong(referenceKey);
938             }
939         }
940 
941         String[] lines = NEXT_LINE.split(msg);
942         String currentColor = "";
943 
944         for (int i = 0; i < lines.length; i++) {
945             // add eventual current color inherited from previous line
946             String line = currentColor + lines[i];
947 
948             // look for last ANSI escape sequence to check if nextColor
949             Matcher matcher = LAST_ANSI_SEQUENCE.matcher(line);
950             String nextColor = "";
951             if (matcher.find()) {
952                 nextColor = matcher.group(1);
953                 if (ANSI_RESET.equals(nextColor)) {
954                     // last ANSI escape code is reset: no next color
955                     nextColor = "";
956                 }
957             }
958 
959             // effective line, with indent and reset if end is colored
960             line = indent + line + ("".equals(nextColor) ? "" : ANSI_RESET);
961 
962             if ((i == lines.length - 1) && (showErrors || (summary.getException() instanceof InternalErrorException))) {
963                 slf4jLogger.error(line, summary.getException());
964             } else {
965                 slf4jLogger.error(line);
966             }
967 
968             currentColor = nextColor;
969         }
970 
971         indent += "  ";
972 
973         for (ExceptionSummary child : summary.getChildren()) {
974             logSummary(child, references, indent, showErrors);
975         }
976     }
977 
978     private static final Pattern LAST_ANSI_SEQUENCE = Pattern.compile("(\u001B\\[[;\\d]*[ -/]*[@-~])[^\u001B]*$");
979 
980     private static final String ANSI_RESET = "\u001B\u005Bm";
981 
982     private void configure(CliRequest cliRequest) throws Exception {
983         //
984         // This is not ideal but there are events specifically for configuration from the CLI which I don't
985         // believe are really valid but there are ITs which assert the right events are published so this
986         // needs to be supported so the EventSpyDispatcher needs to be put in the CliRequest so that
987         // it can be accessed by configuration processors.
988         //
989         cliRequest.request.setEventSpyDispatcher(eventSpyDispatcher);
990 
991         //
992         // We expect at most 2 implementations to be available. The SettingsXmlConfigurationProcessor implementation
993         // is always available in the core and likely always will be, but we may have another ConfigurationProcessor
994         // present supplied by the user. The rule is that we only allow the execution of one ConfigurationProcessor.
995         // If there is more than one then we execute the one supplied by the user, otherwise we execute the
996         // the default SettingsXmlConfigurationProcessor.
997         //
998         int userSuppliedConfigurationProcessorCount = configurationProcessors.size() - 1;
999 
1000         if (userSuppliedConfigurationProcessorCount == 0) {
1001             //
1002             // Our settings.xml source is historically how we have configured Maven from the CLI so we are going to
1003             // have to honour its existence forever. So let's run it.
1004             //
1005             configurationProcessors.get(SettingsXmlConfigurationProcessor.HINT).process(cliRequest);
1006         } else if (userSuppliedConfigurationProcessorCount == 1) {
1007             //
1008             // Run the user supplied ConfigurationProcessor
1009             //
1010             for (Entry<String, ConfigurationProcessor> entry : configurationProcessors.entrySet()) {
1011                 String hint = entry.getKey();
1012                 if (!hint.equals(SettingsXmlConfigurationProcessor.HINT)) {
1013                     ConfigurationProcessor configurationProcessor = entry.getValue();
1014                     configurationProcessor.process(cliRequest);
1015                 }
1016             }
1017         } else if (userSuppliedConfigurationProcessorCount > 1) {
1018             //
1019             // There are too many ConfigurationProcessors so we don't know which one to run so report the error.
1020             //
1021             StringBuilder sb = new StringBuilder(String.format(
1022                     "\nThere can only be one user supplied ConfigurationProcessor, there are %s:\n\n",
1023                     userSuppliedConfigurationProcessorCount));
1024             for (Entry<String, ConfigurationProcessor> entry : configurationProcessors.entrySet()) {
1025                 String hint = entry.getKey();
1026                 if (!hint.equals(SettingsXmlConfigurationProcessor.HINT)) {
1027                     ConfigurationProcessor configurationProcessor = entry.getValue();
1028                     sb.append(String.format(
1029                             "%s\n", configurationProcessor.getClass().getName()));
1030                 }
1031             }
1032             sb.append("\n");
1033             throw new Exception(sb.toString());
1034         }
1035     }
1036 
1037     void toolchains(CliRequest cliRequest) throws Exception {
1038         File userToolchainsFile;
1039 
1040         if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_USER_TOOLCHAINS)) {
1041             userToolchainsFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_USER_TOOLCHAINS));
1042             userToolchainsFile = resolveFile(userToolchainsFile, cliRequest.workingDirectory);
1043 
1044             if (!userToolchainsFile.isFile()) {
1045                 throw new FileNotFoundException(
1046                         "The specified user toolchains file does not exist: " + userToolchainsFile);
1047             }
1048         } else {
1049             userToolchainsFile = DEFAULT_USER_TOOLCHAINS_FILE;
1050         }
1051 
1052         File globalToolchainsFile;
1053 
1054         if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)) {
1055             globalToolchainsFile =
1056                     new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS));
1057             globalToolchainsFile = resolveFile(globalToolchainsFile, cliRequest.workingDirectory);
1058 
1059             if (!globalToolchainsFile.isFile()) {
1060                 throw new FileNotFoundException(
1061                         "The specified global toolchains file does not exist: " + globalToolchainsFile);
1062             }
1063         } else {
1064             globalToolchainsFile = DEFAULT_GLOBAL_TOOLCHAINS_FILE;
1065         }
1066 
1067         cliRequest.request.setGlobalToolchainsFile(globalToolchainsFile);
1068         cliRequest.request.setUserToolchainsFile(userToolchainsFile);
1069 
1070         DefaultToolchainsBuildingRequest toolchainsRequest = new DefaultToolchainsBuildingRequest();
1071         if (globalToolchainsFile.isFile()) {
1072             toolchainsRequest.setGlobalToolchainsSource(new FileSource(globalToolchainsFile));
1073         }
1074         if (userToolchainsFile.isFile()) {
1075             toolchainsRequest.setUserToolchainsSource(new FileSource(userToolchainsFile));
1076         }
1077 
1078         eventSpyDispatcher.onEvent(toolchainsRequest);
1079 
1080         slf4jLogger.debug(
1081                 "Reading global toolchains from {}",
1082                 getLocation(toolchainsRequest.getGlobalToolchainsSource(), globalToolchainsFile));
1083         slf4jLogger.debug(
1084                 "Reading user toolchains from {}",
1085                 getLocation(toolchainsRequest.getUserToolchainsSource(), userToolchainsFile));
1086 
1087         ToolchainsBuildingResult toolchainsResult = toolchainsBuilder.build(toolchainsRequest);
1088 
1089         eventSpyDispatcher.onEvent(toolchainsResult);
1090 
1091         executionRequestPopulator.populateFromToolchains(cliRequest.request, toolchainsResult.getEffectiveToolchains());
1092 
1093         if (!toolchainsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled()) {
1094             slf4jLogger.warn("");
1095             slf4jLogger.warn("Some problems were encountered while building the effective toolchains");
1096 
1097             for (Problem problem : toolchainsResult.getProblems()) {
1098                 slf4jLogger.warn("{} @ {}", problem.getMessage(), problem.getLocation());
1099             }
1100 
1101             slf4jLogger.warn("");
1102         }
1103     }
1104 
1105     private Object getLocation(Source source, File defaultLocation) {
1106         if (source != null) {
1107             return source.getLocation();
1108         }
1109         return defaultLocation;
1110     }
1111 
1112     private MavenExecutionRequest populateRequest(CliRequest cliRequest) {
1113         return populateRequest(cliRequest, cliRequest.request);
1114     }
1115 
1116     @SuppressWarnings("checkstyle:methodlength")
1117     private MavenExecutionRequest populateRequest(CliRequest cliRequest, MavenExecutionRequest request) {
1118         CommandLine commandLine = cliRequest.commandLine;
1119         String workingDirectory = cliRequest.workingDirectory;
1120         boolean quiet = cliRequest.quiet;
1121         boolean showErrors = cliRequest.showErrors;
1122 
1123         String[] deprecatedOptions = {"up", "npu", "cpu", "npr"};
1124         for (String deprecatedOption : deprecatedOptions) {
1125             if (commandLine.hasOption(deprecatedOption)) {
1126                 slf4jLogger.warn(
1127                         "Command line option -{} is deprecated and will be removed in future Maven versions.",
1128                         deprecatedOption);
1129             }
1130         }
1131 
1132         // ----------------------------------------------------------------------
1133         // Now that we have everything that we need we will fire up plexus and
1134         // bring the maven component to life for use.
1135         // ----------------------------------------------------------------------
1136 
1137         if (commandLine.hasOption(CLIManager.BATCH_MODE)) {
1138             request.setInteractiveMode(false);
1139         }
1140 
1141         boolean noSnapshotUpdates = false;
1142         if (commandLine.hasOption(CLIManager.SUPRESS_SNAPSHOT_UPDATES)) {
1143             noSnapshotUpdates = true;
1144         }
1145 
1146         // ----------------------------------------------------------------------
1147         //
1148         // ----------------------------------------------------------------------
1149 
1150         List<String> goals = commandLine.getArgList();
1151 
1152         boolean recursive = true;
1153 
1154         // this is the default behavior.
1155         String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
1156 
1157         if (commandLine.hasOption(CLIManager.NON_RECURSIVE)) {
1158             recursive = false;
1159         }
1160 
1161         if (commandLine.hasOption(CLIManager.FAIL_FAST)) {
1162             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
1163         } else if (commandLine.hasOption(CLIManager.FAIL_AT_END)) {
1164             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END;
1165         } else if (commandLine.hasOption(CLIManager.FAIL_NEVER)) {
1166             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER;
1167         }
1168 
1169         if (commandLine.hasOption(CLIManager.OFFLINE)) {
1170             request.setOffline(true);
1171         }
1172 
1173         boolean updateSnapshots = false;
1174 
1175         if (commandLine.hasOption(CLIManager.UPDATE_SNAPSHOTS)) {
1176             updateSnapshots = true;
1177         }
1178 
1179         String globalChecksumPolicy = null;
1180 
1181         if (commandLine.hasOption(CLIManager.CHECKSUM_FAILURE_POLICY)) {
1182             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
1183         } else if (commandLine.hasOption(CLIManager.CHECKSUM_WARNING_POLICY)) {
1184             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN;
1185         }
1186 
1187         File baseDirectory = new File(workingDirectory, "").getAbsoluteFile();
1188 
1189         // ----------------------------------------------------------------------
1190         // Profile Activation
1191         // ----------------------------------------------------------------------
1192 
1193         List<String> activeProfiles = new ArrayList<>();
1194 
1195         List<String> inactiveProfiles = new ArrayList<>();
1196 
1197         if (commandLine.hasOption(CLIManager.ACTIVATE_PROFILES)) {
1198             String[] profileOptionValues = commandLine.getOptionValues(CLIManager.ACTIVATE_PROFILES);
1199             if (profileOptionValues != null) {
1200                 for (String profileOptionValue : profileOptionValues) {
1201                     StringTokenizer profileTokens = new StringTokenizer(profileOptionValue, ",");
1202 
1203                     while (profileTokens.hasMoreTokens()) {
1204                         String profileAction = profileTokens.nextToken().trim();
1205 
1206                         if (profileAction.startsWith("-") || profileAction.startsWith("!")) {
1207                             inactiveProfiles.add(profileAction.substring(1));
1208                         } else if (profileAction.startsWith("+")) {
1209                             activeProfiles.add(profileAction.substring(1));
1210                         } else {
1211                             activeProfiles.add(profileAction);
1212                         }
1213                     }
1214                 }
1215             }
1216         }
1217 
1218         TransferListener transferListener;
1219 
1220         if (quiet || cliRequest.commandLine.hasOption(CLIManager.NO_TRANSFER_PROGRESS)) {
1221             transferListener = new QuietMavenTransferListener();
1222         } else if (request.isInteractiveMode() && !cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
1223             //
1224             // If we're logging to a file then we don't want the console transfer listener as it will spew
1225             // download progress all over the place
1226             //
1227             transferListener = getConsoleTransferListener(cliRequest.commandLine.hasOption(CLIManager.DEBUG));
1228         } else {
1229             transferListener = getBatchTransferListener();
1230         }
1231 
1232         ExecutionListener executionListener = new ExecutionEventLogger();
1233         if (eventSpyDispatcher != null) {
1234             executionListener = eventSpyDispatcher.chainListener(executionListener);
1235         }
1236 
1237         String alternatePomFile = null;
1238         if (commandLine.hasOption(CLIManager.ALTERNATE_POM_FILE)) {
1239             alternatePomFile = commandLine.getOptionValue(CLIManager.ALTERNATE_POM_FILE);
1240         }
1241 
1242         request.setBaseDirectory(baseDirectory)
1243                 .setGoals(goals)
1244                 .setSystemProperties(cliRequest.systemProperties)
1245                 .setUserProperties(cliRequest.userProperties)
1246                 .setReactorFailureBehavior(reactorFailureBehaviour) // default: fail fast
1247                 .setRecursive(recursive) // default: true
1248                 .setShowErrors(showErrors) // default: false
1249                 .addActiveProfiles(activeProfiles) // optional
1250                 .addInactiveProfiles(inactiveProfiles) // optional
1251                 .setExecutionListener(executionListener)
1252                 .setTransferListener(transferListener) // default: batch mode which goes along with interactive
1253                 .setUpdateSnapshots(updateSnapshots) // default: false
1254                 .setNoSnapshotUpdates(noSnapshotUpdates) // default: false
1255                 .setGlobalChecksumPolicy(globalChecksumPolicy) // default: warn
1256                 .setMultiModuleProjectDirectory(cliRequest.multiModuleProjectDirectory);
1257 
1258         if (alternatePomFile != null) {
1259             File pom = resolveFile(new File(alternatePomFile), workingDirectory);
1260             if (pom.isDirectory()) {
1261                 pom = new File(pom, "pom.xml");
1262             }
1263 
1264             request.setPom(pom);
1265         } else if (modelProcessor != null) {
1266             File pom = modelProcessor.locatePom(baseDirectory);
1267 
1268             if (pom.isFile()) {
1269                 request.setPom(pom);
1270             }
1271         }
1272 
1273         if ((request.getPom() != null) && (request.getPom().getParentFile() != null)) {
1274             request.setBaseDirectory(request.getPom().getParentFile());
1275         }
1276 
1277         if (commandLine.hasOption(CLIManager.RESUME_FROM)) {
1278             request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
1279         }
1280 
1281         if (commandLine.hasOption(CLIManager.PROJECT_LIST)) {
1282             String[] projectOptionValues = commandLine.getOptionValues(CLIManager.PROJECT_LIST);
1283 
1284             List<String> inclProjects = new ArrayList<>();
1285             List<String> exclProjects = new ArrayList<>();
1286 
1287             if (projectOptionValues != null) {
1288                 for (String projectOptionValue : projectOptionValues) {
1289                     StringTokenizer projectTokens = new StringTokenizer(projectOptionValue, ",");
1290 
1291                     while (projectTokens.hasMoreTokens()) {
1292                         String projectAction = projectTokens.nextToken().trim();
1293 
1294                         if (projectAction.startsWith("-") || projectAction.startsWith("!")) {
1295                             exclProjects.add(projectAction.substring(1));
1296                         } else if (projectAction.startsWith("+")) {
1297                             inclProjects.add(projectAction.substring(1));
1298                         } else {
1299                             inclProjects.add(projectAction);
1300                         }
1301                     }
1302                 }
1303             }
1304 
1305             request.setSelectedProjects(inclProjects);
1306             request.setExcludedProjects(exclProjects);
1307         }
1308 
1309         if (commandLine.hasOption(CLIManager.ALSO_MAKE) && !commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
1310             request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_UPSTREAM);
1311         } else if (!commandLine.hasOption(CLIManager.ALSO_MAKE)
1312                 && commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
1313             request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM);
1314         } else if (commandLine.hasOption(CLIManager.ALSO_MAKE)
1315                 && commandLine.hasOption(CLIManager.ALSO_MAKE_DEPENDENTS)) {
1316             request.setMakeBehavior(MavenExecutionRequest.REACTOR_MAKE_BOTH);
1317         }
1318 
1319         String localRepoProperty = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
1320 
1321         // TODO Investigate why this can also be a Java system property and not just a Maven user property like
1322         // other properties
1323         if (localRepoProperty == null) {
1324             localRepoProperty = request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
1325         }
1326 
1327         if (localRepoProperty != null) {
1328             request.setLocalRepositoryPath(localRepoProperty);
1329         }
1330 
1331         request.setCacheNotFound(true);
1332         request.setCacheTransferError(false);
1333 
1334         //
1335         // Builder, concurrency and parallelism
1336         //
1337         // We preserve the existing methods for builder selection which is to look for various inputs in the threading
1338         // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration
1339         // parameters but this is sufficient for now. Ultimately we want components like Builders to provide a way to
1340         // extend the command line to accept its own configuration parameters.
1341         //
1342         final String threadConfiguration =
1343                 commandLine.hasOption(CLIManager.THREADS) ? commandLine.getOptionValue(CLIManager.THREADS) : null;
1344 
1345         if (threadConfiguration != null) {
1346             int degreeOfConcurrency = calculateDegreeOfConcurrency(threadConfiguration);
1347             if (degreeOfConcurrency > 1) {
1348                 request.setBuilderId("multithreaded");
1349                 request.setDegreeOfConcurrency(degreeOfConcurrency);
1350             }
1351         }
1352 
1353         //
1354         // Allow the builder to be overridden by the user if requested. The builders are now pluggable.
1355         //
1356         if (commandLine.hasOption(CLIManager.BUILDER)) {
1357             request.setBuilderId(commandLine.getOptionValue(CLIManager.BUILDER));
1358         }
1359 
1360         return request;
1361     }
1362 
1363     int calculateDegreeOfConcurrency(String threadConfiguration) {
1364         if (threadConfiguration.endsWith("C")) {
1365             threadConfiguration = threadConfiguration.substring(0, threadConfiguration.length() - 1);
1366 
1367             if (!NumberUtils.isParsable(threadConfiguration)) {
1368                 throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration
1369                         + "C'. Supported are int and float values ending with C.");
1370             }
1371 
1372             float coreMultiplier = Float.parseFloat(threadConfiguration);
1373 
1374             if (coreMultiplier <= 0.0f) {
1375                 throw new IllegalArgumentException("Invalid threads core multiplier value: '" + threadConfiguration
1376                         + "C'. Value must be positive.");
1377             }
1378 
1379             int procs = Runtime.getRuntime().availableProcessors();
1380             int threads = (int) (coreMultiplier * procs);
1381             return threads == 0 ? 1 : threads;
1382         } else {
1383             if (!NumberUtils.isParsable(threadConfiguration)) {
1384                 throw new IllegalArgumentException(
1385                         "Invalid threads value: '" + threadConfiguration + "'. Supported are int values.");
1386             }
1387 
1388             try {
1389                 int threads = Integer.parseInt(threadConfiguration);
1390 
1391                 if (threads <= 0) {
1392                     throw new IllegalArgumentException(
1393                             "Invalid threads value: '" + threadConfiguration + "'. Value must be positive.");
1394                 }
1395 
1396                 return threads;
1397             } catch (NumberFormatException e) {
1398                 throw new IllegalArgumentException(
1399                         "Invalid threads value: '" + threadConfiguration + "'. Supported are integer values.");
1400             }
1401         }
1402     }
1403 
1404     // ----------------------------------------------------------------------
1405     // Properties handling
1406     // ----------------------------------------------------------------------
1407 
1408     static void populateProperties(CommandLine commandLine, Properties systemProperties, Properties userProperties) {
1409         EnvironmentUtils.addEnvVars(systemProperties);
1410 
1411         // ----------------------------------------------------------------------
1412         // Options that are set on the command line become system properties
1413         // and therefore are set in the session properties. System properties
1414         // are most dominant.
1415         // ----------------------------------------------------------------------
1416 
1417         if (commandLine.hasOption(CLIManager.SET_USER_PROPERTY)) {
1418             String[] defStrs = commandLine.getOptionValues(CLIManager.SET_USER_PROPERTY);
1419 
1420             if (defStrs != null) {
1421                 for (String defStr : defStrs) {
1422                     setCliProperty(defStr, userProperties);
1423                 }
1424             }
1425         }
1426 
1427         SystemProperties.addSystemProperties(systemProperties);
1428 
1429         // ----------------------------------------------------------------------
1430         // Properties containing info about the currently running version of Maven
1431         // These override any corresponding properties set on the command line
1432         // ----------------------------------------------------------------------
1433 
1434         Properties buildProperties = CLIReportingUtils.getBuildProperties();
1435 
1436         String mavenVersion = buildProperties.getProperty(CLIReportingUtils.BUILD_VERSION_PROPERTY);
1437         systemProperties.setProperty("maven.version", mavenVersion);
1438 
1439         String mavenBuildVersion = CLIReportingUtils.createMavenVersionString(buildProperties);
1440         systemProperties.setProperty("maven.build.version", mavenBuildVersion);
1441     }
1442 
1443     private static void setCliProperty(String property, Properties properties) {
1444         String name;
1445 
1446         String value;
1447 
1448         int i = property.indexOf('=');
1449 
1450         if (i <= 0) {
1451             name = property.trim();
1452 
1453             value = "true";
1454         } else {
1455             name = property.substring(0, i).trim();
1456 
1457             value = property.substring(i + 1);
1458         }
1459 
1460         properties.setProperty(name, value);
1461 
1462         // ----------------------------------------------------------------------
1463         // I'm leaving the setting of system properties here as not to break
1464         // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
1465         // ----------------------------------------------------------------------
1466 
1467         System.setProperty(name, value);
1468     }
1469 
1470     static class ExitException extends Exception {
1471         int exitCode;
1472 
1473         ExitException(int exitCode) {
1474             this.exitCode = exitCode;
1475         }
1476     }
1477 
1478     //
1479     // Customizations available via the CLI
1480     //
1481 
1482     protected TransferListener getConsoleTransferListener(boolean printResourceNames) {
1483         return new ConsoleMavenTransferListener(System.out, printResourceNames);
1484     }
1485 
1486     protected TransferListener getBatchTransferListener() {
1487         return new Slf4jMavenTransferListener();
1488     }
1489 
1490     protected void customizeContainer(PlexusContainer container) {}
1491 
1492     protected ModelProcessor createModelProcessor(PlexusContainer container) throws ComponentLookupException {
1493         return container.lookup(ModelProcessor.class);
1494     }
1495 }