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