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