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