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