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 org.apache.commons.cli.CommandLine;
23  import org.apache.commons.cli.ParseException;
24  import org.apache.maven.Maven;
25  import org.apache.maven.SettingsConfigurationException;
26  import org.apache.maven.artifact.manager.WagonManager;
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
29  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
30  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
31  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
32  import org.apache.maven.execution.DefaultMavenExecutionRequest;
33  import org.apache.maven.execution.MavenExecutionRequest;
34  import org.apache.maven.execution.ReactorManager;
35  import org.apache.maven.monitor.event.DefaultEventDispatcher;
36  import org.apache.maven.monitor.event.DefaultEventMonitor;
37  import org.apache.maven.monitor.event.EventDispatcher;
38  import org.apache.maven.plugin.Mojo;
39  import org.apache.maven.profiles.DefaultProfileManager;
40  import org.apache.maven.profiles.ProfileManager;
41  import org.apache.maven.reactor.MavenExecutionException;
42  import org.apache.maven.settings.MavenSettingsBuilder;
43  import org.apache.maven.settings.RuntimeInfo;
44  import org.apache.maven.settings.Settings;
45  import org.codehaus.classworlds.ClassWorld;
46  import org.codehaus.plexus.PlexusContainerException;
47  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
48  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
49  import org.codehaus.plexus.embed.Embedder;
50  import org.codehaus.plexus.logging.Logger;
51  import org.codehaus.plexus.logging.LoggerManager;
52  import org.codehaus.plexus.util.IOUtil;
53  import org.codehaus.plexus.util.Os;
54  import org.codehaus.plexus.util.cli.CommandLineUtils;
55  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
56  
57  import java.io.File;
58  import java.io.IOException;
59  import java.io.InputStream;
60  import java.text.SimpleDateFormat;
61  import java.util.Date;
62  import java.util.Iterator;
63  import java.util.Locale;
64  import java.util.Properties;
65  import java.util.StringTokenizer;
66  import java.util.Map.Entry;
67  
68  /**
69   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
70   * @version $Id: MavenCli.java 744996 2009-02-17 12:23:30Z bentmann $
71   * @noinspection UseOfSystemOutOrSystemErr,ACCESS_STATIC_VIA_INSTANCE
72   */
73  public class MavenCli
74  {
75      /** @deprecated use {@link Os#OS_NAME} */
76      public static final String OS_NAME = Os.OS_NAME;
77  
78      /** @deprecated use {@link Os#OS_ARCH} */
79      public static final String OS_ARCH = Os.OS_ARCH;
80  
81      /** @deprecated use {@link Os#OS_VERSION} */
82      public static final String OS_VERSION = Os.OS_VERSION;
83  
84      private static Embedder embedder;
85  
86      public static void main( String[] args )
87      {
88          ClassWorld classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
89  
90          int result = main( args, classWorld );
91  
92          System.exit( result );
93      }
94  
95      /**
96       * @noinspection ConfusingMainMethod
97       */
98      public static int main( String[] args, ClassWorld classWorld )
99      {
100         // ----------------------------------------------------------------------
101         // Setup the command line parser
102         // ----------------------------------------------------------------------
103 
104         CLIManager cliManager = new CLIManager();
105 
106         CommandLine commandLine;
107         try
108         {
109             commandLine = cliManager.parse( args );
110         }
111         catch ( ParseException e )
112         {
113             System.err.println( "Unable to parse command line options: " + e.getMessage() );
114             cliManager.displayHelp();
115             return 1;
116         }
117 
118         boolean debug = commandLine.hasOption( CLIManager.DEBUG );
119 
120         boolean showErrors = debug || commandLine.hasOption( CLIManager.ERRORS );
121 
122         if ( showErrors )
123         {
124             System.out.println( "+ Error stacktraces are turned on." );
125         }
126 
127         // ----------------------------------------------------------------------
128         // Process particular command line options
129         // ----------------------------------------------------------------------
130 
131         if ( commandLine.hasOption( CLIManager.HELP ) )
132         {
133             cliManager.displayHelp();
134             return 0;
135         }
136 
137         if ( commandLine.hasOption( CLIManager.VERSION ) )
138         {
139             showVersion();
140 
141             return 0;
142         }
143         else if ( debug )
144         {
145             showVersion();
146         }
147 
148         EventDispatcher eventDispatcher = new DefaultEventDispatcher();
149 
150         // Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
151         // Windows paths.
152         String mavenHome = System.getProperty( "maven.home" );
153         if ( mavenHome != null )
154         {
155             System.setProperty( "maven.home", new File( mavenHome ).getAbsolutePath() );
156         }
157 
158         // ----------------------------------------------------------------------
159         // Now that we have everything that we need we will fire up plexus and
160         // bring the maven component to life for use.
161         // ----------------------------------------------------------------------
162 
163         embedder = new Embedder();
164 
165         try
166         {
167             embedder.start( classWorld );
168         }
169         catch ( PlexusContainerException e )
170         {
171             showFatalError( "Unable to start the embedded plexus container", e, showErrors );
172 
173             return 1;
174         }
175 
176         // ----------------------------------------------------------------------
177         // The execution properties need to be created before the settings
178         // are constructed.
179         // ----------------------------------------------------------------------
180 
181         Properties executionProperties = new Properties();
182         Properties userProperties = new Properties();
183         populateProperties( commandLine, executionProperties, userProperties );
184 
185         Settings settings;
186 
187         try
188         {
189             settings = buildSettings( commandLine );
190         }
191         catch ( SettingsConfigurationException e )
192         {
193             showError( "Error reading settings.xml: " + e.getMessage(), e, showErrors );
194 
195             return 1;
196         }
197         catch ( ComponentLookupException e )
198         {
199             showFatalError( "Unable to read settings.xml", e, showErrors );
200 
201             return 1;
202         }
203 
204         Maven maven = null;
205 
206         MavenExecutionRequest request = null;
207 
208         LoggerManager loggerManager = null;
209 
210         try
211         {
212             // logger must be created first
213             loggerManager = (LoggerManager) embedder.lookup( LoggerManager.ROLE );
214 
215             if ( debug )
216             {
217                 loggerManager.setThreshold( Logger.LEVEL_DEBUG );
218             }
219             else if ( commandLine.hasOption( CLIManager.QUIET ) )
220             {
221                 // TODO: we need to do some more work here. Some plugins use sys out or log errors at info level.
222                 // Ideally, we could use Warn across the board
223                 loggerManager.setThreshold( Logger.LEVEL_ERROR );
224                 // TODO:Additionally, we can't change the mojo level because the component key includes the version and it isn't known ahead of time. This seems worth changing.
225             }
226 
227             ProfileManager profileManager = new DefaultProfileManager( embedder.getContainer(), executionProperties );
228 
229             if ( commandLine.hasOption( CLIManager.ACTIVATE_PROFILES ) )
230             {
231                 String [] profileOptionValues = commandLine.getOptionValues( CLIManager.ACTIVATE_PROFILES );
232 
233                 if ( profileOptionValues != null )
234                 {
235                     for ( int i=0; i < profileOptionValues.length; ++i )
236                     {
237                         StringTokenizer profileTokens = new StringTokenizer( profileOptionValues[i], "," );
238 
239                         while ( profileTokens.hasMoreTokens() )
240                         {
241                             String profileAction = profileTokens.nextToken().trim();
242 
243                             if ( profileAction.startsWith( "-" ) || profileAction.startsWith( "!" ) )
244                             {
245                                 profileManager.explicitlyDeactivate( profileAction.substring( 1 ) );
246                             }
247                             else if ( profileAction.startsWith( "+" ) )
248                             {
249                                 profileManager.explicitlyActivate( profileAction.substring( 1 ) );
250                             }
251                             else
252                             {
253                                 profileManager.explicitlyActivate( profileAction );
254                             }
255                         }
256                     }
257                 }
258             }
259 
260             request = createRequest( commandLine, settings, eventDispatcher, loggerManager, profileManager,
261                                      executionProperties, userProperties, showErrors );
262 
263             setProjectFileOptions( commandLine, request );
264 
265             maven = createMavenInstance( settings.isInteractiveMode() );
266         }
267         catch ( ComponentLookupException e )
268         {
269             showFatalError( "Unable to configure the Maven application", e, showErrors );
270 
271             return 1;
272         }
273         finally
274         {
275             if ( loggerManager != null )
276             {
277                 try
278                 {
279                     embedder.release( loggerManager );
280                 }
281                 catch ( ComponentLifecycleException e )
282                 {
283                     showFatalError( "Error releasing logging manager", e, showErrors );
284                 }
285             }
286         }
287 
288         try
289         {
290             maven.execute( request );
291         }
292         catch ( MavenExecutionException e )
293         {
294             return 1;
295         }
296 
297         return 0;
298     }
299 
300     private static Settings buildSettings( CommandLine commandLine )
301         throws ComponentLookupException, SettingsConfigurationException
302     {
303         String userSettingsPath = null;
304 
305         if ( commandLine.hasOption( CLIManager.ALTERNATE_USER_SETTINGS ) )
306         {
307             userSettingsPath = commandLine.getOptionValue( CLIManager.ALTERNATE_USER_SETTINGS );
308         }
309 
310         Settings settings = null;
311 
312         MavenSettingsBuilder settingsBuilder = (MavenSettingsBuilder) embedder.lookup( MavenSettingsBuilder.ROLE );
313 
314         try
315         {
316             if ( userSettingsPath != null )
317             {
318                 File userSettingsFile = new File( userSettingsPath );
319 
320                 if ( userSettingsFile.exists() && !userSettingsFile.isDirectory() )
321                 {
322                     settings = settingsBuilder.buildSettings( userSettingsFile );
323                 }
324                 else
325                 {
326                     System.out.println( "WARNING: Alternate user settings file: " + userSettingsPath +
327                         " is invalid. Using default path." );
328                 }
329             }
330 
331             if ( settings == null )
332             {
333                 settings = settingsBuilder.buildSettings();
334             }
335         }
336         catch ( IOException e )
337         {
338             throw new SettingsConfigurationException( "Error reading settings file", e );
339         }
340         catch ( XmlPullParserException e )
341         {
342             throw new SettingsConfigurationException( e.getMessage(), e.getDetail(), e.getLineNumber(),
343                                                       e.getColumnNumber() );
344         }
345 
346         // why aren't these part of the runtime info? jvz.
347 
348         if ( commandLine.hasOption( CLIManager.BATCH_MODE ) )
349         {
350             settings.setInteractiveMode( false );
351         }
352 
353         if ( commandLine.hasOption( CLIManager.SUPPRESS_PLUGIN_REGISTRY ) )
354         {
355             settings.setUsePluginRegistry( false );
356         }
357 
358         // Create settings runtime info
359 
360         settings.setRuntimeInfo( createRuntimeInfo( commandLine, settings ) );
361 
362         return settings;
363     }
364 
365     private static RuntimeInfo createRuntimeInfo( CommandLine commandLine, Settings settings )
366     {
367         RuntimeInfo runtimeInfo = new RuntimeInfo( settings );
368 
369         if ( commandLine.hasOption( CLIManager.FORCE_PLUGIN_UPDATES ) ||
370             commandLine.hasOption( CLIManager.FORCE_PLUGIN_UPDATES2 ) )
371         {
372             runtimeInfo.setPluginUpdateOverride( Boolean.TRUE );
373         }
374         else if ( commandLine.hasOption( CLIManager.SUPPRESS_PLUGIN_UPDATES ) )
375         {
376             runtimeInfo.setPluginUpdateOverride( Boolean.FALSE );
377         }
378 
379         return runtimeInfo;
380     }
381 
382 
383     private static void showFatalError( String message, Exception e, boolean show )
384     {
385         System.err.println( "FATAL ERROR: " + message );
386         if ( show )
387         {
388             System.err.println( "Error stacktrace:" );
389 
390             e.printStackTrace();
391         }
392         else
393         {
394             System.err.println( "For more information, run with the -e flag" );
395         }
396     }
397 
398     private static void showError( String message, Exception e, boolean show )
399     {
400         System.err.println( message );
401         if ( show )
402         {
403             System.err.println( "Error stacktrace:" );
404 
405             e.printStackTrace();
406         }
407     }
408 
409     private static MavenExecutionRequest createRequest( CommandLine commandLine, Settings settings,
410                                                         EventDispatcher eventDispatcher, LoggerManager loggerManager,
411                                                         ProfileManager profileManager, Properties executionProperties,
412                                                         Properties userProperties, boolean showErrors )
413         throws ComponentLookupException
414     {
415         MavenExecutionRequest request;
416 
417         ArtifactRepository localRepository = createLocalRepository( embedder, settings, commandLine );
418 
419         File userDir = new File( System.getProperty( "user.dir" ) );
420 
421         request = new DefaultMavenExecutionRequest( localRepository, settings, eventDispatcher,
422                                                     commandLine.getArgList(), userDir.getPath(), profileManager,
423                                                     executionProperties, userProperties, showErrors );
424 
425         // TODO [BP]: do we set one per mojo? where to do it?
426         Logger logger = loggerManager.getLoggerForComponent( Mojo.ROLE );
427 
428         if ( logger != null )
429         {
430             request.addEventMonitor( new DefaultEventMonitor( logger ) );
431         }
432 
433         if ( commandLine.hasOption( CLIManager.NON_RECURSIVE ) )
434         {
435             request.setRecursive( false );
436         }
437 
438         if ( commandLine.hasOption( CLIManager.FAIL_FAST ) )
439         {
440             request.setFailureBehavior( ReactorManager.FAIL_FAST );
441         }
442         else if ( commandLine.hasOption( CLIManager.FAIL_AT_END ) )
443         {
444             request.setFailureBehavior( ReactorManager.FAIL_AT_END );
445         }
446         else if ( commandLine.hasOption( CLIManager.FAIL_NEVER ) )
447         {
448             request.setFailureBehavior( ReactorManager.FAIL_NEVER );
449         }
450 
451         return request;
452     }
453 
454     private static void setProjectFileOptions( CommandLine commandLine, MavenExecutionRequest request )
455     {
456         if ( commandLine.hasOption( CLIManager.REACTOR ) )
457         {
458             request.setReactorActive( true );
459         }
460         else if ( commandLine.hasOption( CLIManager.ALTERNATE_POM_FILE ) )
461         {
462             request.setPomFile( commandLine.getOptionValue( CLIManager.ALTERNATE_POM_FILE ) );
463         }
464     }
465 
466     private static Maven createMavenInstance( boolean interactive )
467         throws ComponentLookupException
468     {
469         // TODO [BP]: doing this here as it is CLI specific, though it doesn't feel like the right place (likewise logger).
470         WagonManager wagonManager = (WagonManager) embedder.lookup( WagonManager.ROLE );
471         if ( interactive )
472         {
473             wagonManager.setDownloadMonitor( new ConsoleDownloadMonitor() );
474         }
475         else
476         {
477             wagonManager.setDownloadMonitor( new BatchModeDownloadMonitor() );
478         }
479 
480         wagonManager.setInteractive( interactive );
481 
482         return (Maven) embedder.lookup( Maven.ROLE );
483     }
484 
485     private static ArtifactRepository createLocalRepository( Embedder embedder, Settings settings,
486                                                              CommandLine commandLine )
487         throws ComponentLookupException
488     {
489         // TODO: release
490         // TODO: something in plexus to show all active hooks?
491         ArtifactRepositoryLayout repositoryLayout =
492             (ArtifactRepositoryLayout) embedder.lookup( ArtifactRepositoryLayout.ROLE, "default" );
493 
494         ArtifactRepositoryFactory artifactRepositoryFactory =
495             (ArtifactRepositoryFactory) embedder.lookup( ArtifactRepositoryFactory.ROLE );
496 
497         String url = settings.getLocalRepository();
498 
499         if ( !url.startsWith( "file:" ) )
500         {
501             url = "file://" + url;
502         }
503 
504         ArtifactRepository localRepository = new DefaultArtifactRepository( "local", url, repositoryLayout );
505 
506         boolean snapshotPolicySet = false;
507 
508         if ( commandLine.hasOption( CLIManager.OFFLINE ) )
509         {
510             settings.setOffline( true );
511 
512             snapshotPolicySet = true;
513         }
514 
515         if ( !snapshotPolicySet && commandLine.hasOption( CLIManager.UPDATE_SNAPSHOTS ) )
516         {
517             artifactRepositoryFactory.setGlobalUpdatePolicy( ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS );
518         }
519 
520         if ( commandLine.hasOption( CLIManager.CHECKSUM_FAILURE_POLICY ) )
521         {
522             System.out.println( "+ Enabling strict checksum verification on all artifact downloads." );
523 
524             artifactRepositoryFactory.setGlobalChecksumPolicy( ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL );
525         }
526         else if ( commandLine.hasOption( CLIManager.CHECKSUM_WARNING_POLICY ) )
527         {
528             System.out.println( "+ Disabling strict checksum verification on all artifact downloads." );
529 
530             artifactRepositoryFactory.setGlobalChecksumPolicy( ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
531         }
532 
533         return localRepository;
534     }
535 
536     static Properties getBuildProperties()
537     {
538         Properties properties = new Properties();
539         InputStream resourceAsStream = null;
540         try
541         {
542             resourceAsStream = MavenCli.class.getClassLoader().getResourceAsStream( "org/apache/maven/messages/build.properties" );
543 
544             if ( resourceAsStream != null )
545             {
546                 properties.load( resourceAsStream );
547             }
548         }
549         catch ( IOException e )
550         {
551             System.err.println( "Unable determine version from JAR file: " + e.getMessage() );
552         }
553         finally
554         {
555             IOUtil.close( resourceAsStream );
556         }
557 
558         return properties;
559     }
560 
561     private static void showVersion()
562     {
563         Properties properties = getBuildProperties();
564 
565         String timestamp = reduce( properties.getProperty( "timestamp" ) );
566         String version = reduce( properties.getProperty( "version" ) );
567         String rev = reduce( properties.getProperty( "buildNumber" ) );
568 
569         String msg = "Apache Maven ";
570         msg += ( version != null ? version : "<version unknown>" );
571         if ( rev != null || timestamp != null )
572         {
573             msg += " (";
574             msg += ( rev != null ? "r" + rev : "" );
575             if ( timestamp != null )
576             {
577                 SimpleDateFormat fmt = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ssZ" );
578                 String ts = fmt.format( new Date( Long.valueOf( timestamp ).longValue() ) );
579                 msg += ( rev != null ? "; " : "" ) + ts;
580             }
581             msg += ")";
582         }
583 
584         System.out.println( msg );
585 
586         System.out.println( "Java version: " + System.getProperty( "java.version", "<unknown java version>" ) );
587 
588         System.out.println( "Java home: " + System.getProperty( "java.home", "<unknown java home>" ) );
589 
590         System.out.println( "Default locale: " + Locale.getDefault() + ", platform encoding: "
591                             + System.getProperty( "file.encoding", "<unknown encoding>" ) );
592 
593         System.out.println( "OS name: \"" + Os.OS_NAME + "\" version: \"" + Os.OS_VERSION +
594                             "\" arch: \"" + Os.OS_ARCH + "\" Family: \"" + Os.OS_FAMILY + "\"" );
595     }
596 
597     private static String reduce( String s )
598     {
599         return ( s != null ? ( s.startsWith( "${" ) && s.endsWith( "}" ) ? null : s ) : null );
600     }
601 
602     // ----------------------------------------------------------------------
603     // System properties handling
604     // ----------------------------------------------------------------------
605 
606     static void populateProperties( CommandLine commandLine, Properties executionProperties, Properties userProperties )
607     {
608         // add the env vars to the property set, with the "env." prefix
609         // XXX support for env vars should probably be removed from the ModelInterpolator
610         try
611         {
612             Properties envVars = CommandLineUtils.getSystemEnvVars();
613             Iterator i = envVars.entrySet().iterator();
614             while ( i.hasNext() )
615             {
616                 Entry e = (Entry) i.next();
617                 executionProperties.setProperty( "env." + e.getKey().toString(), e.getValue().toString() );
618             }
619         }
620         catch ( IOException e )
621         {
622             System.err.println( "Error getting environment vars for profile activation: " + e );
623         }
624 
625         // ----------------------------------------------------------------------
626         // Options that are set on the command line become system properties
627         // and therefore are set in the session properties. System properties
628         // are most dominant.
629         // ----------------------------------------------------------------------
630 
631         if ( commandLine.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) )
632         {
633             String[] defStrs = commandLine.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY );
634 
635             if ( defStrs != null )
636             {
637                 for ( int i = 0; i < defStrs.length; ++i )
638                 {
639                     setCliProperty( defStrs[i], userProperties );
640                 }
641             }
642 
643             executionProperties.putAll( userProperties );
644         }
645 
646         executionProperties.putAll( System.getProperties() );
647     }
648 
649     private static void setCliProperty( String property, Properties requestProperties )
650     {
651         String name;
652 
653         String value;
654 
655         int i = property.indexOf( "=" );
656 
657         if ( i <= 0 )
658         {
659             name = property.trim();
660 
661             value = "true";
662         }
663         else
664         {
665             name = property.substring( 0, i ).trim();
666 
667             value = property.substring( i + 1 ).trim();
668         }
669 
670         requestProperties.setProperty( name, value );
671 
672         // ----------------------------------------------------------------------
673         // I'm leaving the setting of system properties here as not to break
674         // the SystemPropertyProfileActivator. This won't harm embedding. jvz.
675         // ----------------------------------------------------------------------
676 
677         System.setProperty( name, value );
678     }
679 }