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