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