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