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 java.io.Console;
23  import java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.PrintStream;
27  import java.util.ArrayList;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.StringTokenizer;
33  
34  import org.apache.commons.cli.CommandLine;
35  import org.apache.commons.cli.ParseException;
36  import org.apache.commons.cli.UnrecognizedOptionException;
37  import org.apache.maven.BuildAbort;
38  import org.apache.maven.InternalErrorException;
39  import org.apache.maven.Maven;
40  import org.apache.maven.cli.event.DefaultEventSpyContext;
41  import org.apache.maven.cli.event.ExecutionEventLogger;
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.MavenExecutionRequestPopulator;
57  import org.apache.maven.execution.MavenExecutionResult;
58  import org.apache.maven.lifecycle.LifecycleExecutionException;
59  import org.apache.maven.model.building.ModelProcessor;
60  import org.apache.maven.project.MavenProject;
61  import org.apache.maven.properties.internal.EnvironmentUtils;
62  import org.apache.maven.properties.internal.SystemProperties;
63  import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
64  import org.apache.maven.settings.building.SettingsBuilder;
65  import org.apache.maven.settings.building.SettingsBuildingRequest;
66  import org.apache.maven.settings.building.SettingsBuildingResult;
67  import org.apache.maven.settings.building.SettingsProblem;
68  import org.apache.maven.settings.building.SettingsSource;
69  import org.codehaus.plexus.ContainerConfiguration;
70  import org.codehaus.plexus.DefaultContainerConfiguration;
71  import org.codehaus.plexus.DefaultPlexusContainer;
72  import org.codehaus.plexus.PlexusConstants;
73  import org.codehaus.plexus.PlexusContainer;
74  import org.codehaus.plexus.classworlds.ClassWorld;
75  import org.codehaus.plexus.classworlds.realm.ClassRealm;
76  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
77  import org.codehaus.plexus.logging.LoggerManager;
78  import org.codehaus.plexus.util.StringUtils;
79  import org.eclipse.aether.transfer.TransferListener;
80  import org.slf4j.ILoggerFactory;
81  import org.slf4j.Logger;
82  import org.slf4j.LoggerFactory;
83  import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
84  import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
85  import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
86  import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
87  import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
88  
89  import com.google.inject.AbstractModule;
90  
91  // TODO: push all common bits back to plexus cli and prepare for transition to Guice. We don't need 50 ways to make CLIs
92  
93  /**
94   * @author Jason van Zyl
95   * @noinspection UseOfSystemOutOrSystemErr,ACCESS_STATIC_VIA_INSTANCE
96   */
97  public class MavenCli
98  {
99      public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
100 
101     public static final String THREADS_DEPRECATED = "maven.threads.experimental";
102 
103     public static final String userHome = System.getProperty( "user.home" );
104 
105     public static final File userMavenConfigurationHome = new File( userHome, ".m2" );
106 
107     public static final File DEFAULT_USER_SETTINGS_FILE = new File( userMavenConfigurationHome, "settings.xml" );
108 
109     public static final File DEFAULT_GLOBAL_SETTINGS_FILE =
110         new File( System.getProperty( "maven.home", System.getProperty( "user.dir", "" ) ), "conf/settings.xml" );
111 
112     public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File( userMavenConfigurationHome, "toolchains.xml" );
113 
114     private static final String EXT_CLASS_PATH = "maven.ext.class.path";
115 
116     private ClassWorld classWorld;
117 
118     private LoggerManager plexusLoggerManager;
119 
120     private ILoggerFactory slf4jLoggerFactory;
121     
122     private Logger slf4jLogger;
123     
124     private EventSpyDispatcher eventSpyDispatcher;
125 
126     private ModelProcessor modelProcessor;
127 
128     private Maven maven;
129 
130     private MavenExecutionRequestPopulator executionRequestPopulator;
131 
132     private SettingsBuilder settingsBuilder;
133 
134     private DefaultSecDispatcher dispatcher;
135 
136     public MavenCli()
137     {
138         this( null );
139     }
140 
141     // This supports painless invocation by the Verifier during embedded execution of the core ITs
142     public MavenCli( ClassWorld classWorld )
143     {
144         this.classWorld = classWorld;
145     }
146 
147     public static void main( String[] args )
148     {
149         int result = main( args, null );
150 
151         System.exit( result );
152     }
153 
154     /** @noinspection ConfusingMainMethod */
155     public static int main( String[] args, ClassWorld classWorld )
156     {
157         MavenCli cli = new MavenCli();
158         return cli.doMain( new CliRequest( args, classWorld ) );
159     }
160 
161     // TODO: need to externalize CliRequest
162     public static int doMain( String[] args, ClassWorld classWorld )
163     {
164         MavenCli cli = new MavenCli();
165         return cli.doMain( new CliRequest( args, classWorld ) );
166     }
167 
168     // This supports painless invocation by the Verifier during embedded execution of the core ITs
169     public int doMain( String[] args, String workingDirectory, PrintStream stdout, PrintStream stderr )
170     {
171         PrintStream oldout = System.out;
172         PrintStream olderr = System.err;
173 
174         try
175         {
176             if ( stdout != null )
177             {
178                 System.setOut( stdout );
179             }
180             if ( stderr != null )
181             {
182                 System.setErr( stderr );
183             }
184 
185             CliRequest cliRequest = new CliRequest( args, classWorld );
186             cliRequest.workingDirectory = workingDirectory;
187 
188             return doMain( cliRequest );
189         }
190         finally
191         {
192             System.setOut( oldout );
193             System.setErr( olderr );
194         }
195     }
196 
197     // TODO: need to externalize CliRequest
198     public int doMain( CliRequest cliRequest )
199     {
200         PlexusContainer localContainer = null;
201         try
202         {
203             initialize( cliRequest );
204             cli( cliRequest );
205             logging( cliRequest );
206             version( cliRequest );
207             properties( cliRequest );
208             localContainer = container( cliRequest );
209             commands( cliRequest );
210             settings( cliRequest );
211             populateRequest( cliRequest );
212             encryption( cliRequest );
213             repository( cliRequest );
214             return execute( cliRequest );
215         }
216         catch ( ExitException e )
217         {
218             return e.exitCode;
219         }
220         catch ( UnrecognizedOptionException e )
221         {
222             // pure user error, suppress stack trace
223             return 1;
224         }
225         catch ( BuildAbort e )
226         {
227             CLIReportingUtils.showError( slf4jLogger, "ABORTED", e, cliRequest.showErrors );
228 
229             return 2;
230         }
231         catch ( Exception e )
232         {
233             CLIReportingUtils.showError( slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors );
234 
235             return 1;
236         }
237         finally
238         {
239             if ( localContainer != null )
240             {
241                 localContainer.dispose();
242             }
243         }
244     }
245 
246     private void initialize( CliRequest cliRequest )
247     {
248         if ( cliRequest.workingDirectory == null )
249         {
250             cliRequest.workingDirectory = System.getProperty( "user.dir" );
251         }
252 
253         //
254         // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
255         // Windows paths.
256         //
257         String mavenHome = System.getProperty( "maven.home" );
258 
259         if ( mavenHome != null )
260         {
261             System.setProperty( "maven.home", new File( mavenHome ).getAbsolutePath() );
262         }
263     }
264 
265     private void cli( CliRequest cliRequest )
266         throws Exception
267     {
268         //
269         // Parsing errors can happen during the processing of the arguments and we prefer not having to check if the logger is null
270         // and construct this so we can use an SLF4J logger everywhere.
271         //
272         slf4jLogger = new Slf4jStdoutLogger();
273 
274         CLIManager cliManager = new CLIManager();
275 
276         try
277         {
278             cliRequest.commandLine = cliManager.parse( cliRequest.args );
279         }
280         catch ( ParseException e )
281         {
282             System.err.println( "Unable to parse command line options: " + e.getMessage() );
283             cliManager.displayHelp( System.out );
284             throw e;
285         }
286 
287         if ( cliRequest.commandLine.hasOption( CLIManager.HELP ) )
288         {
289             cliManager.displayHelp( System.out );
290             throw new ExitException( 0 );
291         }
292 
293         if ( cliRequest.commandLine.hasOption( CLIManager.VERSION ) )
294         {
295             System.out.println( CLIReportingUtils.showVersion() );
296             throw new ExitException( 0 );
297         }
298     }    
299 
300     /**
301      * configure logging
302      */
303     private void logging( CliRequest cliRequest )
304     {
305         cliRequest.debug = cliRequest.commandLine.hasOption( CLIManager.DEBUG );
306         cliRequest.quiet = !cliRequest.debug && cliRequest.commandLine.hasOption( CLIManager.QUIET );
307         cliRequest.showErrors = cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.ERRORS );
308 
309         slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
310         Slf4jConfiguration slf4jConfiguration = Slf4jConfigurationFactory.getConfiguration( slf4jLoggerFactory );
311 
312         if ( cliRequest.debug )
313         {
314             cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_DEBUG );
315             slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.DEBUG );
316         }
317         else if ( cliRequest.quiet )
318         {
319             cliRequest.request.setLoggingLevel( MavenExecutionRequest.LOGGING_LEVEL_ERROR );
320             slf4jConfiguration.setRootLoggerLevel( Slf4jConfiguration.Level.ERROR );
321         }
322         // else fall back to default log level specified in conf
323         // see http://jira.codehaus.org/browse/MNG-2570
324 
325         if ( cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
326         {
327             File logFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.LOG_FILE ) );
328             logFile = resolveFile( logFile, cliRequest.workingDirectory );
329 
330             // redirect stdout and stderr to file
331             try
332             {
333                 PrintStream ps = new PrintStream( new FileOutputStream( logFile ) );
334                 System.setOut( ps );
335                 System.setErr( ps );
336             }
337             catch ( FileNotFoundException e )
338             {
339                 //
340                 // Ignore
341                 //
342             }
343         }
344 
345         slf4jConfiguration.activate();
346 
347         plexusLoggerManager = new Slf4jLoggerManager();
348         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
349     }
350 
351     private void version( CliRequest cliRequest )
352     {
353         if ( cliRequest.debug || cliRequest.commandLine.hasOption( CLIManager.SHOW_VERSION ) )
354         {
355             System.out.println( CLIReportingUtils.showVersion() );
356         }
357     }
358 
359     private void commands( CliRequest cliRequest )
360     {
361         if ( cliRequest.showErrors )
362         {
363             slf4jLogger.info( "Error stacktraces are turned on." );
364         }
365 
366         if ( MavenExecutionRequest.CHECKSUM_POLICY_WARN.equals( cliRequest.request.getGlobalChecksumPolicy() ) )
367         {
368             slf4jLogger.info( "Disabling strict checksum verification on all artifact downloads." );
369         }
370         else if ( MavenExecutionRequest.CHECKSUM_POLICY_FAIL.equals( cliRequest.request.getGlobalChecksumPolicy() ) )
371         {
372             slf4jLogger.info( "Enabling strict checksum verification on all artifact downloads." );
373         }
374     }
375 
376     private void properties( CliRequest cliRequest )
377     {
378         populateProperties( cliRequest.commandLine, cliRequest.systemProperties, cliRequest.userProperties );
379     }
380 
381     private PlexusContainer container( CliRequest cliRequest )
382         throws Exception
383     {
384         if ( cliRequest.classWorld == null )
385         {
386             cliRequest.classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
387         }
388 
389         DefaultPlexusContainer container;
390 
391         ContainerConfiguration cc = new DefaultContainerConfiguration()
392             .setClassWorld( cliRequest.classWorld )
393             .setRealm( setupContainerRealm( cliRequest ) )
394             .setClassPathScanning( PlexusConstants.SCANNING_INDEX )
395             .setAutoWiring( true )
396             .setName( "maven" );
397 
398         container = new DefaultPlexusContainer( cc, new AbstractModule()
399         {
400             protected void configure()
401             {
402                 bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory );
403             }
404         } );
405 
406         // NOTE: To avoid inconsistencies, we'll use the TCCL exclusively for lookups
407         container.setLookupRealm( null );
408 
409         container.setLoggerManager( plexusLoggerManager );
410 
411         customizeContainer( container );
412 
413         container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() );
414 
415         Thread.currentThread().setContextClassLoader( container.getContainerRealm() );
416 
417         eventSpyDispatcher = container.lookup( EventSpyDispatcher.class );
418 
419         DefaultEventSpyContext eventSpyContext = new DefaultEventSpyContext();
420         Map<String, Object> data = eventSpyContext.getData();
421         data.put( "plexus", container );
422         data.put( "workingDirectory", cliRequest.workingDirectory );
423         data.put( "systemProperties", cliRequest.systemProperties );
424         data.put( "userProperties", cliRequest.userProperties );
425         data.put( "versionProperties", CLIReportingUtils.getBuildProperties() );
426         eventSpyDispatcher.init( eventSpyContext );
427 
428         // refresh logger in case container got customized by spy
429         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
430 
431         maven = container.lookup( Maven.class );
432 
433         executionRequestPopulator = container.lookup( MavenExecutionRequestPopulator.class );
434 
435         modelProcessor = createModelProcessor( container );
436 
437         settingsBuilder = container.lookup( SettingsBuilder.class );
438 
439         dispatcher = (DefaultSecDispatcher) container.lookup( SecDispatcher.class, "maven" );
440 
441         return container;
442     }
443 
444     private ClassRealm setupContainerRealm( CliRequest cliRequest )
445         throws Exception
446     {
447         ClassRealm containerRealm = null;
448 
449         String extClassPath = cliRequest.userProperties.getProperty( EXT_CLASS_PATH );
450         if ( extClassPath == null )
451         {
452             extClassPath = cliRequest.systemProperties.getProperty( EXT_CLASS_PATH );
453         }
454 
455         if ( StringUtils.isNotEmpty( extClassPath ) )
456         {
457             String[] jars = StringUtils.split( extClassPath, File.pathSeparator );
458 
459             if ( jars.length > 0 )
460             {
461                 ClassRealm coreRealm = cliRequest.classWorld.getClassRealm( "plexus.core" );
462                 if ( coreRealm == null )
463                 {
464                     coreRealm = cliRequest.classWorld.getRealms().iterator().next();
465                 }
466 
467                 ClassRealm extRealm = cliRequest.classWorld.newRealm( "maven.ext", null );
468 
469                 slf4jLogger.debug( "Populating class realm " + extRealm.getId() );
470 
471                 for ( String jar : jars )
472                 {
473                     File file = resolveFile( new File( jar ), cliRequest.workingDirectory );
474 
475                     slf4jLogger.debug( "  Included " + file );
476 
477                     extRealm.addURL( file.toURI().toURL() );
478                 }
479 
480                 extRealm.setParentRealm( coreRealm );
481 
482                 containerRealm = extRealm;
483             }
484         }
485 
486         return containerRealm;
487     }
488 
489     //
490     // This should probably be a separate tool and not be baked into Maven.
491     //
492     private void encryption( CliRequest cliRequest )
493         throws Exception
494     {
495         if ( cliRequest.commandLine.hasOption( CLIManager.ENCRYPT_MASTER_PASSWORD ) )
496         {
497             String passwd = cliRequest.commandLine.getOptionValue( CLIManager.ENCRYPT_MASTER_PASSWORD );
498             
499             if ( passwd == null )
500             {
501                 Console cons;
502                 char[] password;
503                 if ( ( cons = System.console() ) != null
504                     && ( password = cons.readPassword( "Master password: " ) ) != null )
505                 {
506                     // Cipher uses Strings
507                     passwd = String.copyValueOf( password );
508                     
509                     // Sun/Oracle advises to empty the char array
510                     java.util.Arrays.fill( password, ' ' );
511                 }
512             }
513 
514             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
515 
516             System.out.println( cipher.encryptAndDecorate( passwd, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION ) );
517 
518             throw new ExitException( 0 );
519         }
520         else if ( cliRequest.commandLine.hasOption( CLIManager.ENCRYPT_PASSWORD ) )
521         {
522             String passwd = cliRequest.commandLine.getOptionValue( CLIManager.ENCRYPT_PASSWORD );
523             
524             if ( passwd == null )
525             {
526                 Console cons;
527                 char[] password;
528                 if ( ( cons = System.console() ) != null
529                     && ( password = cons.readPassword( "Password: " ) ) != null )
530                 {
531                     // Cipher uses Strings
532                     passwd = String.copyValueOf( password );
533 
534                     // Sun/Oracle advises to empty the char array
535                     java.util.Arrays.fill( password, ' ' );
536                 }
537             }
538 
539             String configurationFile = dispatcher.getConfigurationFile();
540 
541             if ( configurationFile.startsWith( "~" ) )
542             {
543                 configurationFile = System.getProperty( "user.home" ) + configurationFile.substring( 1 );
544             }
545 
546             String file = System.getProperty( DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION, configurationFile );
547 
548             String master = null;
549 
550             SettingsSecurity sec = SecUtil.read( file, true );
551             if ( sec != null )
552             {
553                 master = sec.getMaster();
554             }
555 
556             if ( master == null )
557             {
558                 throw new IllegalStateException( "Master password is not set in the setting security file: " + file );
559             }
560 
561             DefaultPlexusCipher cipher = new DefaultPlexusCipher();
562             String masterPasswd = cipher.decryptDecorated( master, DefaultSecDispatcher.SYSTEM_PROPERTY_SEC_LOCATION );
563             System.out.println( cipher.encryptAndDecorate( passwd, masterPasswd ) );
564 
565             throw new ExitException( 0 );
566         }
567     }
568 
569     private void repository( CliRequest cliRequest )
570         throws Exception
571     {
572         if ( cliRequest.commandLine.hasOption( CLIManager.LEGACY_LOCAL_REPOSITORY ) || Boolean.getBoolean( "maven.legacyLocalRepo" ) )
573         {
574            cliRequest.request.setUseLegacyLocalRepository( true );
575         }
576     }
577 
578     private int execute( CliRequest cliRequest )
579     {
580         eventSpyDispatcher.onEvent( cliRequest.request );
581 
582         MavenExecutionResult result = maven.execute( cliRequest.request );
583 
584         eventSpyDispatcher.onEvent( result );
585 
586         eventSpyDispatcher.close();
587 
588         if ( result.hasExceptions() )
589         {
590             ExceptionHandler handler = new DefaultExceptionHandler();
591 
592             Map<String, String> references = new LinkedHashMap<String, String>();
593 
594             MavenProject project = null;
595 
596             for ( Throwable exception : result.getExceptions() )
597             {
598                 ExceptionSummary summary = handler.handleException( exception );
599 
600                 logSummary( summary, references, "", cliRequest.showErrors );
601 
602                 if ( project == null && exception instanceof LifecycleExecutionException )
603                 {
604                     project = ( (LifecycleExecutionException) exception ).getProject();
605                 }
606             }
607 
608             slf4jLogger.error( "" );
609 
610             if ( !cliRequest.showErrors )
611             {
612                 slf4jLogger.error( "To see the full stack trace of the errors, re-run Maven with the -e switch." );
613             }
614             if ( !slf4jLogger.isDebugEnabled() )
615             {
616                 slf4jLogger.error( "Re-run Maven using the -X switch to enable full debug logging." );
617             }
618 
619             if ( !references.isEmpty() )
620             {
621                 slf4jLogger.error( "" );
622                 slf4jLogger.error( "For more information about the errors and possible solutions"
623                               + ", please read the following articles:" );
624 
625                 for ( Map.Entry<String, String> entry : references.entrySet() )
626                 {
627                     slf4jLogger.error( entry.getValue() + " " + entry.getKey() );
628                 }
629             }
630 
631             if ( project != null && !project.equals( result.getTopologicallySortedProjects().get( 0 ) ) )
632             {
633                 slf4jLogger.error( "" );
634                 slf4jLogger.error( "After correcting the problems, you can resume the build with the command" );
635                 slf4jLogger.error( "  mvn <goals> -rf :" + project.getArtifactId() );
636             }
637 
638             if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( cliRequest.request.getReactorFailureBehavior() ) )
639             {
640                 slf4jLogger.info( "Build failures were ignored." );
641 
642                 return 0;
643             }
644             else
645             {
646                 return 1;
647             }
648         }
649         else
650         {
651             return 0;
652         }
653     }
654 
655     private void logSummary( ExceptionSummary summary, Map<String, String> references, String indent,
656                              boolean showErrors )
657     {
658         String referenceKey = "";
659 
660         if ( StringUtils.isNotEmpty( summary.getReference() ) )
661         {
662             referenceKey = references.get( summary.getReference() );
663             if ( referenceKey == null )
664             {
665                 referenceKey = "[Help " + ( references.size() + 1 ) + "]";
666                 references.put( summary.getReference(), referenceKey );
667             }
668         }
669 
670         String msg = summary.getMessage();
671 
672         if ( StringUtils.isNotEmpty( referenceKey ) )
673         {
674             if ( msg.indexOf( '\n' ) < 0 )
675             {
676                 msg += " -> " + referenceKey;
677             }
678             else
679             {
680                 msg += "\n-> " + referenceKey;
681             }
682         }
683 
684         String[] lines = msg.split( "(\r\n)|(\r)|(\n)" );
685 
686         for ( int i = 0; i < lines.length; i++ )
687         {
688             String line = indent + lines[i].trim();
689 
690             if ( i == lines.length - 1 && ( showErrors || ( summary.getException() instanceof InternalErrorException ) ) )
691             {
692                 slf4jLogger.error( line, summary.getException() );
693             }
694             else
695             {
696                 slf4jLogger.error( line );
697             }
698         }
699 
700         indent += "  ";
701 
702         for ( ExceptionSummary child : summary.getChildren() )
703         {
704             logSummary( child, references, indent, showErrors );
705         }
706     }
707 
708     private void settings( CliRequest cliRequest )
709         throws Exception
710     {
711         File userSettingsFile;
712 
713         if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_USER_SETTINGS ) )
714         {
715             userSettingsFile = new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_USER_SETTINGS ) );
716             userSettingsFile = resolveFile( userSettingsFile, cliRequest.workingDirectory );
717 
718             if ( !userSettingsFile.isFile() )
719             {
720                 throw new FileNotFoundException( "The specified user settings file does not exist: "
721                     + userSettingsFile );
722             }
723         }
724         else
725         {
726             userSettingsFile = DEFAULT_USER_SETTINGS_FILE;
727         }
728 
729         File globalSettingsFile;
730 
731         if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_GLOBAL_SETTINGS ) )
732         {
733             globalSettingsFile =
734                 new File( cliRequest.commandLine.getOptionValue( CLIManager.ALTERNATE_GLOBAL_SETTINGS ) );
735             globalSettingsFile = resolveFile( globalSettingsFile, cliRequest.workingDirectory );
736 
737             if ( !globalSettingsFile.isFile() )
738             {
739                 throw new FileNotFoundException( "The specified global settings file does not exist: "
740                     + globalSettingsFile );
741             }
742         }
743         else
744         {
745             globalSettingsFile = DEFAULT_GLOBAL_SETTINGS_FILE;
746         }
747 
748         cliRequest.request.setGlobalSettingsFile( globalSettingsFile );
749         cliRequest.request.setUserSettingsFile( userSettingsFile );
750 
751         SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest();
752         settingsRequest.setGlobalSettingsFile( globalSettingsFile );
753         settingsRequest.setUserSettingsFile( userSettingsFile );
754         settingsRequest.setSystemProperties( cliRequest.systemProperties );
755         settingsRequest.setUserProperties( cliRequest.userProperties );
756 
757         eventSpyDispatcher.onEvent( settingsRequest );
758 
759         slf4jLogger.debug( "Reading global settings from "
760             + getSettingsLocation( settingsRequest.getGlobalSettingsSource(), settingsRequest.getGlobalSettingsFile() ) );
761         slf4jLogger.debug( "Reading user settings from "
762             + getSettingsLocation( settingsRequest.getUserSettingsSource(), settingsRequest.getUserSettingsFile() ) );
763 
764         SettingsBuildingResult settingsResult = settingsBuilder.build( settingsRequest );
765 
766         eventSpyDispatcher.onEvent( settingsResult );
767 
768         executionRequestPopulator.populateFromSettings( cliRequest.request, settingsResult.getEffectiveSettings() );
769 
770         if ( !settingsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled() )
771         {
772             slf4jLogger.warn( "" );
773             slf4jLogger.warn( "Some problems were encountered while building the effective settings" );
774 
775             for ( SettingsProblem problem : settingsResult.getProblems() )
776             {
777                 slf4jLogger.warn( problem.getMessage() + " @ " + problem.getLocation() );
778             }
779 
780             slf4jLogger.warn( "" );
781         }
782     }
783 
784     private Object getSettingsLocation( SettingsSource source, File file )
785     {
786         if ( source != null )
787         {
788             return source.getLocation();
789         }
790         return file;
791     }
792 
793     private MavenExecutionRequest populateRequest( CliRequest cliRequest )
794     {
795         MavenExecutionRequest request = cliRequest.request;
796         CommandLine commandLine = cliRequest.commandLine;
797         String workingDirectory = cliRequest.workingDirectory;
798         boolean quiet = cliRequest.quiet;
799         boolean showErrors = cliRequest.showErrors;
800 
801         String[] deprecatedOptions = { "up", "npu", "cpu", "npr" };
802         for ( String deprecatedOption : deprecatedOptions )
803         {
804             if ( commandLine.hasOption( deprecatedOption ) )
805             {
806                 slf4jLogger.warn( "Command line option -" + deprecatedOption
807                     + " is deprecated and will be removed in future Maven versions." );
808             }
809         }
810 
811         // ----------------------------------------------------------------------
812         // Now that we have everything that we need we will fire up plexus and
813         // bring the maven component to life for use.
814         // ----------------------------------------------------------------------
815 
816         if ( commandLine.hasOption( CLIManager.BATCH_MODE ) )
817         {
818             request.setInteractiveMode( false );
819         }
820 
821         boolean noSnapshotUpdates = false;
822         if ( commandLine.hasOption( CLIManager.SUPRESS_SNAPSHOT_UPDATES ) )
823         {
824             noSnapshotUpdates = true;
825         }
826 
827         // ----------------------------------------------------------------------
828         //
829         // ----------------------------------------------------------------------
830 
831         @SuppressWarnings( "unchecked" )
832         List<String> goals = commandLine.getArgList();
833 
834         boolean recursive = true;
835 
836         // this is the default behavior.
837         String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
838 
839         if ( commandLine.hasOption( CLIManager.NON_RECURSIVE ) )
840         {
841             recursive = false;
842         }
843 
844         if ( commandLine.hasOption( CLIManager.FAIL_FAST ) )
845         {
846             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
847         }
848         else if ( commandLine.hasOption( CLIManager.FAIL_AT_END ) )
849         {
850             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_AT_END;
851         }
852         else if ( commandLine.hasOption( CLIManager.FAIL_NEVER ) )
853         {
854             reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_NEVER;
855         }
856 
857         if ( commandLine.hasOption( CLIManager.OFFLINE ) )
858         {
859             request.setOffline( true );
860         }
861 
862         boolean updateSnapshots = false;
863 
864         if ( commandLine.hasOption( CLIManager.UPDATE_SNAPSHOTS ) )
865         {
866             updateSnapshots = true;
867         }
868 
869         String globalChecksumPolicy = null;
870 
871         if ( commandLine.hasOption( CLIManager.CHECKSUM_FAILURE_POLICY ) )
872         {
873             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_FAIL;
874         }
875         else if ( commandLine.hasOption( CLIManager.CHECKSUM_WARNING_POLICY ) )
876         {
877             globalChecksumPolicy = MavenExecutionRequest.CHECKSUM_POLICY_WARN;
878         }
879 
880         File baseDirectory = new File( workingDirectory, "" ).getAbsoluteFile();
881 
882         // ----------------------------------------------------------------------
883         // Profile Activation
884         // ----------------------------------------------------------------------
885 
886         List<String> activeProfiles = new ArrayList<String>();
887 
888         List<String> inactiveProfiles = new ArrayList<String>();
889 
890         if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) )
891         {
892             String[] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES );
893             if ( profileOptionValues != null )
894             {
895                 for ( String profileOptionValue : profileOptionValues )
896                 {
897                     StringTokenizer profileTokens = new StringTokenizer( profileOptionValue, "," );
898 
899                     while ( profileTokens.hasMoreTokens() )
900                     {
901                         String profileAction = profileTokens.nextToken().trim();
902 
903                         if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) )
904                         {
905                             inactiveProfiles.add( profileAction.substring( 1 ) );
906                         }
907                         else if ( profileAction.startsWith( "+" ) )
908                         {
909                             activeProfiles.add( profileAction.substring( 1 ) );
910                         }
911                         else
912                         {
913                             activeProfiles.add( profileAction );
914                         }
915                     }
916                 }
917             }
918         }
919 
920         TransferListener transferListener;
921 
922         if ( quiet )
923         {
924             transferListener = new QuietMavenTransferListener();
925         }        
926         else if ( request.isInteractiveMode() && !cliRequest.commandLine.hasOption( CLIManager.LOG_FILE ) )
927         {
928             //
929             // If we're logging to a file then we don't want the console transfer listener as it will spew
930             // download progress all over the place
931             //
932             transferListener = getConsoleTransferListener();
933         }
934         else
935         {
936             transferListener = getBatchTransferListener();
937         }
938 
939         ExecutionListener executionListener = new ExecutionEventLogger();
940         executionListener = eventSpyDispatcher.chainListener( executionListener );
941 
942         String alternatePomFile = null;
943         if ( commandLine.hasOption( CLIManager.ALTERNATE_POM_FILE ) )
944         {
945             alternatePomFile = commandLine.getOptionValue( CLIManager.ALTERNATE_POM_FILE );
946         }
947 
948         File userToolchainsFile;
949         if ( commandLine.hasOption( CLIManager.ALTERNATE_USER_TOOLCHAINS ) )
950         {
951             userToolchainsFile = new File( commandLine.getOptionValue( CLIManager.ALTERNATE_USER_TOOLCHAINS ) );
952             userToolchainsFile = resolveFile( userToolchainsFile, workingDirectory );
953         }
954         else
955         {
956             userToolchainsFile = MavenCli.DEFAULT_USER_TOOLCHAINS_FILE;
957         }
958 
959         request.setBaseDirectory( baseDirectory ).setGoals( goals )
960             .setSystemProperties( cliRequest.systemProperties )
961             .setUserProperties( cliRequest.userProperties )
962             .setReactorFailureBehavior( reactorFailureBehaviour ) // default: fail fast
963             .setRecursive( recursive ) // default: true
964             .setShowErrors( showErrors ) // default: false
965             .addActiveProfiles( activeProfiles ) // optional
966             .addInactiveProfiles( inactiveProfiles ) // optional
967             .setExecutionListener( executionListener )
968             .setTransferListener( transferListener ) // default: batch mode which goes along with interactive
969             .setUpdateSnapshots( updateSnapshots ) // default: false
970             .setNoSnapshotUpdates( noSnapshotUpdates ) // default: false
971             .setGlobalChecksumPolicy( globalChecksumPolicy ) // default: warn
972             .setUserToolchainsFile( userToolchainsFile );
973 
974         if ( alternatePomFile != null )
975         {
976             File pom = resolveFile( new File( alternatePomFile ), workingDirectory );
977             if ( pom.isDirectory() )
978             {
979                 pom = new File( pom, "pom.xml" );
980             }
981 
982             request.setPom( pom );
983         }
984         else
985         {
986             File pom = modelProcessor.locatePom( baseDirectory );
987 
988             if ( pom.isFile() )
989             {
990                 request.setPom( pom );
991             }
992         }
993 
994         if ( ( request.getPom() != null ) && ( request.getPom().getParentFile() != null ) )
995         {
996             request.setBaseDirectory( request.getPom().getParentFile() );
997         }
998 
999         if ( commandLine.hasOption( CLIManager.RESUME_FROM ) )
1000         {
1001             request.setResumeFrom( commandLine.getOptionValue( CLIManager.RESUME_FROM ) );
1002         }
1003 
1004         if ( commandLine.hasOption( CLIManager.PROJECT_LIST ) )
1005         {
1006             String[] projectOptionValues = commandLine.getOptionValues( CLIManager.PROJECT_LIST );
1007             
1008             List<String> inclProjects = new ArrayList<String>();
1009             List<String> exclProjects = new ArrayList<String>();
1010             
1011             if ( projectOptionValues != null )
1012             {
1013                 for ( String projectOptionValue : projectOptionValues )
1014                 {
1015                     StringTokenizer projectTokens = new StringTokenizer( projectOptionValue, "," );
1016 
1017                     while ( projectTokens.hasMoreTokens() )
1018                     {
1019                         String projectAction = projectTokens.nextToken().trim();
1020 
1021                         if ( projectAction.startsWith( "-" ) || projectAction.startsWith( "!" ) )
1022                         {
1023                             exclProjects.add( projectAction.substring( 1 ) );
1024                         }
1025                         else if ( projectAction.startsWith( "+" ) )
1026                         {
1027                             inclProjects.add( projectAction.substring( 1 ) );
1028                         }
1029                         else
1030                         {
1031                             inclProjects.add( projectAction );
1032                         }
1033                     }
1034                 }
1035             }
1036             
1037             request.setSelectedProjects( inclProjects );
1038             request.setExcludedProjects( exclProjects );
1039         }
1040 
1041         if ( commandLine.hasOption( CLIManager.ALSO_MAKE )
1042                         && !commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) )
1043         {
1044             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM );
1045         }
1046         else if ( !commandLine.hasOption( CLIManager.ALSO_MAKE )
1047                         && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) )
1048         {
1049             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM );
1050         }
1051         else if ( commandLine.hasOption( CLIManager.ALSO_MAKE )
1052                         && commandLine.hasOption( CLIManager.ALSO_MAKE_DEPENDENTS ) )
1053         {
1054             request.setMakeBehavior( MavenExecutionRequest.REACTOR_MAKE_BOTH );
1055         }
1056 
1057         String localRepoProperty = request.getUserProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
1058 
1059         if ( localRepoProperty == null )
1060         {
1061             localRepoProperty = request.getSystemProperties().getProperty( MavenCli.LOCAL_REPO_PROPERTY );
1062         }
1063 
1064         if ( localRepoProperty != null )
1065         {
1066             request.setLocalRepositoryPath( localRepoProperty );
1067         }
1068         
1069 
1070         request.setCacheNotFound( true );
1071         request.setCacheTransferError( false );
1072 
1073         // 
1074         // Builder, concurrency and parallelism
1075         //
1076         // We preserve the existing methods for builder selection which is to look for various inputs in the threading
1077         // configuration. We don't have an easy way to allow a pluggable builder to provide its own configuration parameters
1078         // but this is sufficient for now. Ultimately we want components like Builders to provide a way to extend the command
1079         // line to accept its own configuration parameters. 
1080         //
1081         final String threadConfiguration = commandLine.hasOption( CLIManager.THREADS )
1082             ? commandLine.getOptionValue( CLIManager.THREADS )
1083             : request.getSystemProperties().getProperty(
1084                 MavenCli.THREADS_DEPRECATED ); // TODO: Remove this setting. Note that the int-tests use it
1085         
1086         if ( threadConfiguration != null )
1087         {
1088             //
1089             // Default to the standard multithreaded builder
1090             //
1091             request.setBuilderId( "multithreaded" );
1092 
1093             if ( threadConfiguration.contains( "C" ) )
1094             {
1095                 request.setDegreeOfConcurrency( calculateDegreeOfConcurrencyWithCoreMultiplier( threadConfiguration ) );
1096             }
1097             else
1098             {
1099                 request.setDegreeOfConcurrency( Integer.valueOf( threadConfiguration ) );
1100             }
1101         }
1102               
1103         //
1104         // Allow the builder to be overriden by the user if requested. The builders are now pluggable.
1105         //
1106         if ( commandLine.hasOption( CLIManager.BUILDER ) )
1107         {          
1108             request.setBuilderId( commandLine.getOptionValue( CLIManager.BUILDER ) );
1109         }        
1110         
1111         return request;
1112     }
1113 
1114     int calculateDegreeOfConcurrencyWithCoreMultiplier( String threadConfiguration )
1115     {
1116         return (int) ( Float.valueOf( threadConfiguration.replace( "C", "" ) ) * Runtime.getRuntime().availableProcessors() );
1117     }
1118     
1119     static File resolveFile( File file, String workingDirectory )
1120     {
1121         if ( file == null )
1122         {
1123             return null;
1124         }
1125         else if ( file.isAbsolute() )
1126         {
1127             return file;
1128         }
1129         else if ( file.getPath().startsWith( File.separator ) )
1130         {
1131             // drive-relative Windows path
1132             return file.getAbsoluteFile();
1133         }
1134         else
1135         {
1136             return new File( workingDirectory, file.getPath() ).getAbsoluteFile();
1137         }
1138     }
1139 
1140     // ----------------------------------------------------------------------
1141     // System properties handling
1142     // ----------------------------------------------------------------------
1143 
1144     static void populateProperties( CommandLine commandLine, Properties systemProperties, Properties userProperties )
1145     {
1146         EnvironmentUtils.addEnvVars( systemProperties );
1147 
1148         // ----------------------------------------------------------------------
1149         // Options that are set on the command line become system properties
1150         // and therefore are set in the session properties. System properties
1151         // are most dominant.
1152         // ----------------------------------------------------------------------
1153 
1154         if ( commandLine.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) )
1155         {
1156             String[] defStrs = commandLine.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY );
1157 
1158             if ( defStrs != null )
1159             {
1160                 for ( String defStr : defStrs )
1161                 {
1162                     setCliProperty( defStr, userProperties );
1163                 }
1164             }
1165         }
1166 
1167         SystemProperties.addSystemProperties( systemProperties );
1168 
1169         // ----------------------------------------------------------------------
1170         // Properties containing info about the currently running version of Maven
1171         // These override any corresponding properties set on the command line
1172         // ----------------------------------------------------------------------
1173 
1174         Properties buildProperties = CLIReportingUtils.getBuildProperties();
1175 
1176         String mavenVersion = buildProperties.getProperty( CLIReportingUtils.BUILD_VERSION_PROPERTY );
1177         systemProperties.setProperty( "maven.version", mavenVersion );
1178 
1179         String mavenBuildVersion = CLIReportingUtils.createMavenVersionString( buildProperties );
1180         systemProperties.setProperty( "maven.build.version", mavenBuildVersion );
1181     }
1182 
1183     private static void setCliProperty( String property, Properties properties )
1184     {
1185         String name;
1186 
1187         String value;
1188 
1189         int i = property.indexOf( "=" );
1190 
1191         if ( i <= 0 )
1192         {
1193             name = property.trim();
1194 
1195             value = "true";
1196         }
1197         else
1198         {
1199             name = property.substring( 0, i ).trim();
1200 
1201             value = property.substring( i + 1 );
1202         }
1203 
1204         properties.setProperty( name, value );
1205 
1206         // ----------------------------------------------------------------------
1207         // I'm leaving the setting of system properties here as not to break
1208         // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
1209         // ----------------------------------------------------------------------
1210 
1211         System.setProperty( name, value );
1212     }
1213 
1214     static class CliRequest
1215     {
1216         String[] args;
1217         CommandLine commandLine;
1218         ClassWorld classWorld;
1219         String workingDirectory;
1220         boolean debug;
1221         boolean quiet;
1222         boolean showErrors = true;
1223         Properties userProperties = new Properties();
1224         Properties systemProperties = new Properties();
1225         MavenExecutionRequest request;
1226 
1227         CliRequest( String[] args, ClassWorld classWorld )
1228         {
1229             this.args = args;
1230             this.classWorld = classWorld;
1231             this.request = new DefaultMavenExecutionRequest();
1232         }
1233     }
1234 
1235     static class ExitException
1236         extends Exception
1237     {
1238 
1239         public int exitCode;
1240 
1241         public ExitException( int exitCode )
1242         {
1243             this.exitCode = exitCode;
1244         }
1245 
1246     }
1247     
1248     //
1249     // Customizations available via the CLI
1250     //
1251     
1252     protected TransferListener getConsoleTransferListener() 
1253     {
1254         return new ConsoleMavenTransferListener( System.out );
1255     }
1256     
1257     protected TransferListener getBatchTransferListener()
1258     {
1259         return new Slf4jMavenTransferListener();
1260     }
1261     
1262     protected void customizeContainer( PlexusContainer container )
1263     {
1264     }
1265 
1266     protected ModelProcessor createModelProcessor( PlexusContainer container )
1267         throws ComponentLookupException
1268     {
1269         return container.lookup( ModelProcessor.class );
1270     }        
1271 }