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