1 package org.apache.maven.cli;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import org.apache.commons.cli.CommandLine;
22 import org.apache.commons.cli.ParseException;
23 import org.apache.commons.jelly.JellyException;
24 import org.apache.commons.jelly.XMLOutput;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.log4j.Level;
28 import org.apache.log4j.Logger;
29 import org.apache.log4j.PropertyConfigurator;
30 import org.apache.maven.MavenConstants;
31 import org.apache.maven.MavenException;
32 import org.apache.maven.MavenSession;
33 import org.apache.maven.MavenUtils;
34 import org.apache.maven.UnknownGoalException;
35 import org.apache.maven.jelly.MavenJellyContext;
36 import org.apache.maven.project.Project;
37 import org.apache.maven.verifier.ChecksumVerificationException;
38 import org.apache.maven.verifier.RepoConfigException;
39 import org.apache.maven.verifier.UnsatisfiedDependencyException;
40 import org.apache.maven.werkz.NoActionDefinitionException;
41 import org.apache.maven.werkz.NoSuchGoalException;
42 import org.apache.maven.werkz.UnattainableGoalException;
43
44 import java.io.File;
45 import java.io.FileInputStream;
46 import java.io.FileNotFoundException;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.OutputStreamWriter;
50 import java.io.Writer;
51 import java.net.MalformedURLException;
52 import java.net.URL;
53 import java.text.BreakIterator;
54 import java.text.DateFormat;
55 import java.util.ArrayList;
56 import java.util.Collections;
57 import java.util.Date;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Properties;
63 import java.util.Set;
64 import java.util.StringTokenizer;
65
66 /**
67 * The CLI wrapper for controlling MavenSession processes which are encapsulated in the MavenSession bean.
68 *
69 * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
70 *
71 * @version $Id: App.java 530196 2007-04-18 23:04:51Z aheritier $
72 *
73 * @todo Separate the computation of the available goals from the display of the goals. The goal computation logic needs
74 * to be moved out of this class and be placed in MavenSession.java proper.
75 * @todo All logging needs to be done via commons-logging. No System.err.* and Jelly needs to be taught to take a
76 * logger. In an attempt to isolate everything in MavenSession.java.
77 */
78 public class App
79 {
80 /** Default file name for an XML-based POM. */
81 public static final String POM_FILE_NAME = "project.xml";
82
83 /** Default console width - for formatting output. */
84 private static final int CONSOLE_WIDTH = 80;
85
86 /** Links properties */
87 private static final Properties LINKS_PROPERTIES = new Properties();
88
89 /** logger for output */
90 private static final Log LOGGER = LogFactory.getLog( App.class );
91
92 /** Convenience constant for new line character. */
93 private static final String LS = System.getProperty( "line.separator", "\n" );
94
95 /** Console banner option. */
96 private static final String OPT_CONSOLE_BANNER = "b";
97
98 /** Debug option. */
99 private static final String OPT_DEBUG = "X";
100
101 /** Display goals options. */
102 private static final String OPT_DISPLAY_GOALS = "g";
103
104 /** Display help option. */
105 private static final String OPT_DISPLAY_HELP = "h";
106
107 /** Display info option. */
108 private static final String OPT_DISPLAY_INFO = "i";
109
110 /** Display plugin help option. */
111 private static final String OPT_DISPLAY_PLUGIN_HELP = "P";
112
113 /** Display stack trace option. */
114 private static final String OPT_DISPLAY_STACKTRACE = "e";
115
116 /** Display project help option. */
117 private static final String OPT_DISPLAY_USAGE = "u";
118
119 /** Display version option. */
120 private static final String OPT_DISPLAY_VERSION = "v";
121
122 /** Emacs output option. */
123 private static final String OPT_EMACS_OUTPUT = "E";
124
125 /** Find POM option. */
126 private static final String OPT_FIND_POM_DESCRIPTOR = "f";
127
128 /** Quiet option. */
129 private static final String OPT_QUIET = "q";
130
131 /** Set POM descriptor option. */
132 private static final String OPT_SET_POM_DESCRIPTOR = "p";
133
134 /** System property option. */
135 private static final String OPT_SET_SYSTEM_PROPERTY = "D";
136
137 /** Work offline option. */
138 private static final String OPT_WORK_OFFLINE = "o";
139
140 /** Working dir option. */
141 private static final String OPT_WORKING_DIR = "d";
142
143 /** return code from command prompt when a bad argument is passed */
144 private static final int RC_BAD_ARG = 10;
145
146 /** return code for a failure due to Jelly issues */
147 private static final int RC_BAD_JELLY = 80;
148
149 /** return code for bad repository configuration */
150 private static final int RC_BAD_REPO = 40;
151
152 /** return code for a goal with no actions */
153 private static final int RC_EMPTY_GOAL = 50;
154
155 /** return code for a failure due to failed dependency */
156 private static final int RC_FAILED_DEPENDENCY = 100;
157
158 /** return code for a goal that failed */
159 private static final int RC_GOAL_FAILED = 60;
160
161 /** return code from command prompt when initialization fails */
162 private static final int RC_INIT_ERROR = 20;
163
164 /** return code for a goal failed from jelly exception being thrown */
165 private static final int RC_JELLY_FAILED = 70;
166
167 /** return code from command prompt when a goal isn't found */
168 private static final int RC_NO_GOAL = 30;
169
170 /** return code for ok processing */
171 private static final int RC_OK = 0;
172
173 /** return code for a failure due to anything else */
174 private static final int RC_OTHER_FAILURE = 90;
175
176 /** Default wrap indent. */
177 private static final int WRAP_INDENT = 35;
178
179 static
180 {
181 URL urlLog4j = App.class.getResource( "/log4j.properties" );
182
183 if ( urlLog4j == null )
184 {
185 throw new RuntimeException( "Configuration error: can not find the resource log4j.properties" );
186 }
187
188 PropertyConfigurator.configure( urlLog4j );
189
190 URL urlLinks = App.class.getResource( "/links.properties" );
191
192 if ( urlLinks == null )
193 {
194 throw new RuntimeException( "Configuration error: can not find the resource links.properties" );
195 }
196
197 try
198 {
199 LINKS_PROPERTIES.load( urlLinks.openStream() );
200 }
201 catch ( IOException e )
202 {
203 throw new RuntimeException( "Configuration error: Unable to load the resource links.properties", e );
204 }
205 }
206
207 /**
208 * Main CLI entry point for MavenSession.
209 *
210 * @param args
211 * CLI arguments.
212 */
213 public static void main( String[] args )
214 {
215 Date start = new Date();
216 App app = new App();
217 app.doMain( args, start );
218 }
219
220 /**
221 * Format a time string.
222 *
223 * @param ms
224 * Duration in ms.
225 * @return String The formatted time string.
226 */
227 protected static String formatTime( long ms )
228 {
229 long secs = ms / 1000;
230 long min = secs / 60;
231 secs = secs % 60;
232
233 if ( min > 0 )
234 {
235 return min + MavenUtils.getMessage( "formatTime.minutes" ) + secs
236 + MavenUtils.getMessage( "formatTime.seconds" );
237 }
238 else
239 {
240 return secs + MavenUtils.getMessage( "formatTime.seconds" );
241 }
242 }
243
244 /** CLI Parser */
245 private CommandLine commandLine;
246
247 /** the session to run builds */
248 private MavenSession mavenSession;
249
250 /** MavenSession Jelly rootContext. */
251 private MavenJellyContext rootContext;
252
253 /** Jelly's underlying writer. */
254 private Writer writer;
255
256 /** Constructor. */
257 public App()
258 {
259 }
260
261 /**
262 * Perform main operations in a non-static method.
263 *
264 * @param args
265 * Arguments passed in from main().
266 * @param fullStart
267 * Date the mavenSession process was started.
268 */
269 public void doMain( String[] args, Date fullStart )
270 {
271 initializeMain( args );
272
273 displayHelp();
274 displayVersion();
275
276 int returnCode = RC_OK;
277
278 if ( !getCli().hasOption( OPT_CONSOLE_BANNER ) )
279 {
280 printConsoleMavenHeader();
281 }
282
283 if ( !MavenSession.getRootDescriptorFile().exists() )
284 {
285 LOGGER.warn( MavenUtils.getMessage( "build.no.pom.found" ) );
286 LOGGER.warn( "" );
287 }
288
289 boolean failed = false;
290 boolean displayStackTrace = getCli().hasOption( OPT_DISPLAY_STACKTRACE ) || getCli().hasOption( OPT_DEBUG );
291
292 try
293 {
294 mavenSession.initialize();
295
296 displayInfo();
297 displayProjectHelp();
298 displayPluginHelp();
299
300 if ( getCli().hasOption( OPT_DISPLAY_GOALS ) )
301 {
302 displayGoals();
303 exit( returnCode );
304 }
305 else
306 {
307 mavenSession.attainGoals( mavenSession.getRootProject(), getCli().getArgList() );
308 }
309 }
310 catch ( UnsatisfiedDependencyException e )
311 {
312 failed = true;
313 displayBuildFailed( e, false, true, displayStackTrace );
314 returnCode = RC_FAILED_DEPENDENCY;
315 }
316 catch ( ChecksumVerificationException e )
317 {
318 failed = true;
319 displayBuildFailed( e, false, true, displayStackTrace );
320 returnCode = RC_FAILED_DEPENDENCY;
321 }
322 catch ( UnknownGoalException e )
323 {
324 failed = true;
325 LOGGER.info( MavenUtils.getMessage( "line" ) );
326 LOGGER.info( MavenUtils.getMessage( "build.unknownGoalException", e.getGoalName() ) );
327 displayBuildFailed( e, false, false, displayStackTrace );
328 returnCode = RC_NO_GOAL;
329 }
330 catch ( NoSuchGoalException e )
331 {
332 failed = true;
333 displayBuildFailed( e, false, true, displayStackTrace );
334 returnCode = RC_NO_GOAL;
335 }
336 catch ( RepoConfigException e )
337 {
338 failed = true;
339 displayBuildFailed( e, false, true, displayStackTrace );
340 returnCode = RC_BAD_REPO;
341 }
342 catch ( NoActionDefinitionException e )
343 {
344 failed = true;
345 LOGGER.info( MavenUtils.getMessage( "line" ) );
346 LOGGER.info( MavenUtils.getMessage( "build.internalError" ) );
347 LOGGER.info( MavenUtils.getMessage( "build.noActionDefinitionException", e.getGoal().getName() ) );
348 displayBuildFailed( e, true, false, displayStackTrace );
349 returnCode = RC_EMPTY_GOAL;
350 }
351 catch ( UnattainableGoalException e )
352 {
353 failed = true;
354 displayBuildFailed( e, false, true, displayStackTrace );
355 if ( e.getCause() instanceof JellyException )
356 {
357 returnCode = RC_JELLY_FAILED;
358 }
359 else
360 {
361 returnCode = RC_GOAL_FAILED;
362 }
363 }
364 catch ( JellyException e )
365 {
366 failed = true;
367 displayBuildFailed( e, true, true, displayStackTrace );
368 returnCode = RC_BAD_JELLY;
369 }
370 catch ( MavenException e )
371 {
372 failed = true;
373 displayBuildFailed( e, MavenUtils.MAVEN_UNKNOWN_ERROR.equals( e.getMessage() ), true, displayStackTrace );
374 returnCode = RC_OTHER_FAILURE;
375 }
376 catch ( Throwable t )
377 {
378 failed = true;
379 displayBuildFailed( t, true, true, displayStackTrace );
380 returnCode = RC_OTHER_FAILURE;
381 }
382
383 if ( !failed )
384 {
385 LOGGER.warn( MavenUtils.getMessage( "line" ) );
386 LOGGER.warn( MavenUtils.getMessage( "build.successful" ) );
387 }
388 LOGGER.warn( MavenUtils.getMessage( "line" ) );
389 Date fullStop = new Date();
390 DateFormat defaultDate = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.LONG );
391 long fullDiff = fullStop.getTime() - fullStart.getTime();
392 LOGGER.warn( MavenUtils.getMessage( "build.total.time", formatTime( fullDiff ) ) );
393 LOGGER.warn( MavenUtils.getMessage( "build.finished.time", defaultDate.format( fullStop ) ) );
394 final long mb = 1024 * 1024;
395 System.gc();
396 Runtime r = Runtime.getRuntime();
397 LOGGER.warn( MavenUtils.getMessage( "build.final.memory", ( ( r.totalMemory() - r.freeMemory() ) / mb ) + "M/"
398 + ( r.totalMemory() / mb ) + "M" ) );
399 LOGGER.warn( MavenUtils.getMessage( "line" ) );
400 exit( returnCode );
401 }
402
403 /**
404 * Retrieve the Jelly rootContext.
405 *
406 * @return The Jelly rootContext.
407 */
408 public MavenJellyContext getRootContext()
409 {
410 return rootContext;
411 }
412
413 /**
414 * Perform initialization.
415 *
416 * @param args
417 * The command-line arguments.
418 *
419 * @throws ParseException
420 * If there is an error parsing the command-line.
421 * @throws IOException
422 * If there is an error while reading the project descriptor.
423 * @throws MalformedURLException
424 * If any of the the URLs denoting the local or remote repositories is malformed.
425 */
426 public void initialize( String[] args ) throws ParseException, MalformedURLException, IOException
427 {
428 setCli( CLIManager.parse( args ) );
429
430 initializeSystemProperties();
431 initializeRootContext();
432 initializeMavenSession();
433 customizeLogging();
434 }
435
436 /**
437 * Setup any system properties that have been specified on the CLI.
438 */
439 public void initializeSystemProperties()
440 {
441 if ( getCli().hasOption( OPT_SET_SYSTEM_PROPERTY ) )
442 {
443 String[] defStrs = getCli().getOptionValues( OPT_SET_SYSTEM_PROPERTY );
444
445 for ( int i = 0; i < defStrs.length; ++i )
446 {
447 setCliProperty( defStrs[i] );
448 }
449 }
450 }
451
452 /**
453 * Set Jelly rootContext.
454 *
455 * @param rootContext
456 * The mavenSession jelly rootContext.
457 */
458 public void setRootContext( MavenJellyContext rootContext )
459 {
460 this.rootContext = rootContext;
461 }
462
463 /**
464 * Display helpful information regarding all documented goals.
465 */
466 protected void displayGoals()
467 {
468 displayGoals( false, null );
469 }
470
471 /**
472 * Display helpful information regarding all documented goals.
473 *
474 * @param pluginOnly
475 * show information for the given plugin only
476 * @param plugin
477 * plugin to show info for
478 */
479 protected void displayGoals( boolean pluginOnly, String plugin )
480 {
481 String title = MavenUtils.getMessage( "displayGoals.title" );
482 if ( pluginOnly )
483 {
484 title =
485 ( plugin == null ? MavenUtils.getMessage( "displayGoals.title.pluginOnly.null" )
486 : MavenUtils.getMessage( "displayGoals.title.pluginOnly.notNull" ) + plugin );
487 }
488 LOGGER.info( title );
489 LOGGER.info( format( "", title.length(), '=' ) );
490
491 Set goals = mavenSession.getAllGoalNames();
492 displayGoals( pluginOnly, plugin, goals );
493 }
494
495 /**
496 * To allow subclasses stop the app from exiting
497 *
498 * @param status
499 * the value to exit with
500 */
501 protected void exit( int status )
502 {
503 System.exit( status );
504 }
505
506 /**
507 * Produce a formatted/padded string.
508 *
509 * @param orig
510 * The string to format.
511 * @param width
512 * The width of the resulting formatted string.
513 * @param pad
514 * The trailing pad character.
515 *
516 * @return The formatted string, or the original string if the length is already >= <code>width</code>.
517 */
518 protected String format( String orig, int width, char pad )
519 {
520 if ( orig.length() >= width )
521 {
522 return orig;
523 }
524
525 StringBuffer buf = new StringBuffer().append( orig );
526
527 int diff = width - orig.length();
528
529 for ( int i = 0; i < diff; ++i )
530 {
531 buf.append( pad );
532 }
533
534 return buf.toString();
535 }
536
537 /**
538 * Get the CLI parser.
539 *
540 * @return CommandLine The command line parser.
541 */
542 protected CommandLine getCli()
543 {
544 return this.commandLine;
545 }
546
547 /**
548 * Initialize the IO streams.
549 *
550 * @throws IOException
551 * on error creating XML output and handling System.err and out
552 */
553 protected void initializeRootContext() throws IOException
554 {
555 this.writer = new OutputStreamWriter( System.out );
556 XMLOutput output = XMLOutput.createXMLOutput( writer, false );
557
558 if ( getCli().hasOption( OPT_WORKING_DIR ) )
559 {
560 String workingDir = getCli().getOptionValue( OPT_WORKING_DIR );
561 File dir = new File( workingDir );
562 if ( !dir.isAbsolute() )
563 {
564 workingDir = dir.getAbsolutePath();
565 }
566
567 System.setProperty( "user.dir", workingDir );
568 }
569
570
571
572
573 File basedir = new File( System.getProperty( "user.dir" ) );
574 MavenJellyContext c = MavenUtils.createContext( basedir );
575 setRootContext( c );
576 c.setXMLOutput( output );
577
578
579 if ( getCli().hasOption( OPT_QUIET ) )
580 {
581
582 Logger.getLogger( "org.apache.maven" ).setLevel( Level.ERROR );
583 }
584
585
586 if ( getCli().hasOption( OPT_DEBUG ) )
587 {
588 getRootContext().setDebugOn( Boolean.TRUE );
589
590 Logger.getLogger( "org.apache.maven" ).setLevel( Level.DEBUG );
591 }
592 else
593 {
594 getRootContext().setDebugOn( Boolean.FALSE );
595 }
596
597 if ( getCli().hasOption( OPT_EMACS_OUTPUT ) )
598 {
599 getRootContext().setEmacsModeOn( Boolean.TRUE );
600 }
601 else
602 {
603 getRootContext().setEmacsModeOn( Boolean.FALSE );
604 }
605
606 if ( getCli().hasOption( OPT_WORK_OFFLINE ) )
607 {
608 System.setProperty( MavenConstants.ONLINE, "false" );
609 }
610 }
611
612 /**
613 * Prints the MavenSession header.
614 */
615 protected void printConsoleMavenHeader()
616 {
617 Properties p = new Properties();
618 InputStream is = getClass().getResourceAsStream( "/driver.properties" );
619 try
620 {
621 p.load( is );
622 }
623 catch ( IOException e )
624 {
625 LOGGER.error( MavenUtils.getMessage( "printConsoleMavenHeader.error" ) + e );
626 }
627 finally
628 {
629 try
630 {
631 is.close();
632 }
633 catch ( IOException e )
634 {
635 LOGGER.debug( "WARNING: Cannot close stream!", e );
636 }
637 }
638
639 LOGGER.info( " __ __" );
640 LOGGER.info( "| \\/ |__ _Apache__ ___" );
641 LOGGER.info( "| |\\/| / _` \\ V / -_) ' \\ ~ intelligent projects ~" );
642 LOGGER.info( "|_| |_\\__,_|\\_/\\___|_||_| v. " + p.getProperty( "maven.application.version" ) );
643 LOGGER.info( "" );
644
645 }
646
647 /**
648 * Set the cli parser.
649 *
650 * @param commandLine
651 * The command line parser.
652 */
653 protected void setCli( CommandLine commandLine )
654 {
655 this.commandLine = commandLine;
656 }
657
658 /**
659 * Nicely wraps a message for console output, using a word BreakIterator instance to determine wrapping breaks.
660 *
661 * @param msg
662 * the string message for the console
663 * @param wrapIndent
664 * the number of characters to indent all lines after the first one
665 * @param lineWidth
666 * the console width that determines where to wrap
667 * @return the message wrapped for the console
668 */
669 protected String wrapConsoleMessage( String msg, int wrapIndent, int lineWidth )
670 {
671 if ( ( msg.indexOf( '\n' ) < 0 ) && ( msg.indexOf( '\r' ) < 0 ) )
672 {
673 return wrapConsoleLine( msg, wrapIndent, lineWidth );
674 }
675
676 StringBuffer buf = new StringBuffer();
677 StringTokenizer tok = new StringTokenizer( msg, "\n\r" );
678 while ( tok.hasMoreTokens() )
679 {
680 String token = tok.nextToken().trim();
681 if ( token.length() > 0 )
682 {
683 buf.append( wrapConsoleLine( token, wrapIndent, lineWidth ) );
684 buf.append( LS );
685 }
686 }
687 return buf.toString();
688 }
689
690 /**
691 * Customize the log4j configuration from properties (read in system, user or project scope).
692 */
693 private void customizeLogging()
694 {
695 Iterator iter = getRootContext().getVariableNames();
696 Properties log4jProperties = new Properties();
697 String propertyName;
698 while ( iter.hasNext() )
699 {
700 propertyName = (String) iter.next();
701 if ( propertyName.startsWith( "log4j" ) )
702 {
703 log4jProperties.put( propertyName, getRootContext().getVariable( propertyName ) );
704 }
705 }
706
707 PropertyConfigurator.configure( log4jProperties );
708 log4jProperties = null;
709 iter = null;
710 }
711
712 /**
713 * Display information on how to file a bug report if an unknown error occurs.
714 */
715 private void displayBugReportHelp()
716 {
717 LOGGER.info( MavenUtils.getMessage( "line" ) );
718 StringBuffer sb = new StringBuffer();
719 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line1" ) ).append( '\n' );
720 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line2" ) ).append( '\n' );
721 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line3", LINKS_PROPERTIES.get( "faqUrl" ) ) ).append(
722 '\n' );
723 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line4" ) ).append( '\n' );
724 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line5", LINKS_PROPERTIES.get( "usersMailingListUrl" ) ) ).append(
725 '\n' );
726 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line6", LINKS_PROPERTIES.get( "issueTrackingUrl" ) ) ).append(
727 '\n' );
728 sb.append( MavenUtils.getMessage( "displayBugReportHelp.line7" ) );
729
730 LOGGER.info( "" );
731 LOGGER.info( sb.toString() );
732 LOGGER.info( "" );
733 }
734
735 private void displayBuildFailed( Throwable t, boolean displayBugReportHelp, boolean displayErrors,
736 boolean displayStackTrace )
737 {
738 if ( displayBugReportHelp )
739 displayBugReportHelp();
740 if ( displayErrors )
741 displayThrowable( t, displayStackTrace );
742 LOGGER.warn( MavenUtils.getMessage( "line" ) );
743 LOGGER.warn( MavenUtils.getMessage( "build.failed" ) );
744 }
745
746 /**
747 * Display helpful information about the given default goal.
748 *
749 * @param goalName
750 * goal to show info for
751 * @param goalDescription
752 * the description of the goal
753 * @param newLine
754 * whether to append a newline
755 */
756 private void displayDefaultGoal( String goalName, String goalDescription, boolean newLine )
757 {
758 if ( "".equals( goalName ) )
759 {
760 return;
761 }
762
763 String msgPrefix = format( "[" + goalName + "]", WRAP_INDENT, ' ' ) + " ";
764 if ( goalDescription == null )
765 {
766 goalDescription = "( NO DEFAULT GOAL )";
767 }
768
769 if ( newLine )
770 {
771 msgPrefix = LS + msgPrefix;
772 }
773
774 LOGGER.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
775 }
776
777 /**
778 * Display helpful information about the given goal.
779 *
780 * @param goalName
781 * goal to show info for
782 * @param goalDescription
783 * the description of the goal
784 */
785 private void displayGoal( String goalName, String goalDescription )
786 {
787 if ( "".equals( goalName ) )
788 {
789 goalName = "( NO GOAL )";
790 }
791
792 String msgPrefix = format( " " + goalName + " ", WRAP_INDENT, '.' ) + " ";
793
794 LOGGER.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
795 }
796
797 /**
798 * Display helpful information regarding all documented goals.
799 *
800 * @param pluginOnly
801 * show information for the given plugin only
802 * @param plugin
803 * plugin to show info for
804 * @param goals
805 * the set of goals
806 */
807 private void displayGoals( boolean pluginOnly, String plugin, Set goals )
808 {
809 Map map = new HashMap();
810 for ( Iterator i = goals.iterator(); i.hasNext(); )
811 {
812 String goal = (String) i.next();
813 String pluginName = "";
814 int index = goal.indexOf( ':' );
815 if ( index >= 0 )
816 {
817 pluginName = goal.substring( 0, index );
818 }
819 List l = (List) map.get( pluginName );
820 if ( l == null )
821 {
822 l = new ArrayList();
823 map.put( pluginName, l );
824 }
825 l.add( goal.substring( index + 1 ) );
826 }
827
828 List goalList = (List) map.get( "" );
829 if ( goalList != null )
830 {
831 for ( Iterator i = goalList.iterator(); i.hasNext(); )
832 {
833 String goal = (String) i.next();
834 if ( map.containsKey( goal ) )
835 {
836 i.remove();
837 List pluginGoals = (List) map.get( goal );
838 if ( pluginGoals == null )
839 {
840 pluginGoals = new ArrayList();
841 map.put( goal, pluginGoals );
842 }
843
844 }
845 }
846 }
847
848 List keys = new ArrayList( map.keySet() );
849 Collections.sort( keys );
850
851 List undocumentedGoals = new ArrayList();
852
853 for ( Iterator i = keys.iterator(); i.hasNext(); )
854 {
855 String pluginName = (String) i.next();
856 if ( pluginOnly )
857 {
858 if ( plugin == null )
859 {
860
861 displayDefaultGoal( pluginName, mavenSession.getGoalDescription( pluginName ), false );
862 continue;
863 }
864 else if ( !pluginName.equals( plugin ) )
865 {
866 continue;
867 }
868 }
869
870 displayDefaultGoal( pluginName, mavenSession.getGoalDescription( pluginName ), true );
871 List l = (List) map.get( pluginName );
872 Collections.sort( l );
873 for ( Iterator j = l.iterator(); j.hasNext(); )
874 {
875 String goalName = (String) j.next();
876 String fullGoalName = pluginName.length() == 0 ? goalName : pluginName + ":" + goalName;
877 String goalDescription = mavenSession.getGoalDescription( fullGoalName );
878 if ( goalDescription != null )
879 {
880 displayGoal( goalName, goalDescription );
881 }
882 else
883 {
884 undocumentedGoals.add( fullGoalName );
885 }
886 }
887 }
888
889 if ( undocumentedGoals.isEmpty() == false )
890 {
891 displayGoalsWithoutDescriptions( undocumentedGoals );
892 }
893 LOGGER.info( "" );
894 }
895
896 /**
897 * Display goals without descriptions.
898 *
899 * @param list
900 * List of undocument goal names.
901 */
902 private void displayGoalsWithoutDescriptions( List list )
903 {
904 LOGGER.info( "" );
905 LOGGER.info( MavenUtils.getMessage( "displayGoalsWithoutDescriptions.info" ) );
906 LOGGER.info( "" );
907
908 for ( Iterator i = list.iterator(); i.hasNext(); )
909 {
910 String goalName = (String) i.next();
911
912 LOGGER.info( " " + goalName );
913 }
914 }
915
916 /**
917 * Display the command line help if the option is present, then exit
918 */
919 private void displayHelp()
920 {
921 if ( getCli().hasOption( OPT_DISPLAY_HELP ) )
922 {
923 CLIManager.displayHelp();
924 LOGGER.info( "" );
925 exit( RC_OK );
926 }
927 }
928
929 /**
930 * Display the command line info if the option is present, then exit
931 */
932 private void displayInfo()
933 {
934 if ( getCli().hasOption( OPT_DISPLAY_INFO ) )
935 {
936 CLIManager.displayInfo();
937 LOGGER.info( "" );
938 LOGGER.info( MavenUtils.getMessage( "displayInfo.info1" ) );
939 for ( Iterator i = mavenSession.getPluginList().iterator(); i.hasNext(); )
940 {
941 LOGGER.info( " " + i.next() );
942 }
943
944 Properties buildProperties = new Properties();
945 FileInputStream fis = null;
946 try
947 {
948 fis = new FileInputStream( System.getProperty( "user.home" ) + "/build.properties" );
949 buildProperties.load( fis );
950 }
951 catch ( IOException e )
952 {
953 LOGGER.error( MavenUtils.getMessage( "displayInfo.error" ) + e.getMessage() );
954 }
955 finally
956 {
957 if ( fis != null )
958 {
959 try
960 {
961 fis.close();
962 }
963 catch ( IOException e )
964 {
965 LOGGER.debug( "WARNING: Cannot close stream!", e );
966 }
967 fis = null;
968 }
969 }
970 LOGGER.info( MavenUtils.getMessage( "displayInfo.info2" ) + buildProperties );
971 exit( RC_OK );
972 }
973 }
974
975 /**
976 * Display the plugin help if the option is present, then exit.
977 *
978 * @throws MavenException
979 * when anything goes wrong
980 */
981 private void displayPluginHelp() throws MavenException
982 {
983 if ( getCli().hasOption( OPT_DISPLAY_PLUGIN_HELP ) )
984 {
985 String plugin = getCli().getOptionValue( OPT_DISPLAY_PLUGIN_HELP );
986
987 displayGoals( true, plugin );
988
989 if ( plugin != null )
990 {
991 Project project = mavenSession.getPluginProjectFromGoal( plugin );
992 if ( ( project != null ) && ( project.getDescription() != null ) )
993 {
994 LOGGER.info( wrapConsoleMessage( project.getDescription(), 0, CONSOLE_WIDTH ) );
995 }
996 }
997
998 exit( RC_OK );
999 }
1000 }
1001
1002 /**
1003 * Display the project help if the option is present, then exit.
1004 *
1005 * @throws MavenException
1006 * when anything goes wrong
1007 */
1008 private void displayProjectHelp() throws MavenException
1009 {
1010 if ( getCli().hasOption( OPT_DISPLAY_USAGE ) )
1011 {
1012 Set goals = mavenSession.getProjectGoals( mavenSession.getRootProject() );
1013
1014 String title = MavenUtils.getMessage( "displayProjectHelp.title" );
1015 LOGGER.info( title );
1016 LOGGER.info( format( "", title.length(), '=' ) );
1017 LOGGER.info( "" );
1018
1019 Project rootProject = mavenSession.getRootProject();
1020 if ( ( rootProject.getBuild() != null ) && ( rootProject.getBuild().getDefaultGoal() != null ) )
1021 {
1022 String defaultGoal = rootProject.getBuild().getDefaultGoal();
1023 String msg = MavenUtils.getMessage( "displayGoals.defaultGoal" ) + defaultGoal;
1024 LOGGER.info( wrapConsoleMessage( msg, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
1025 }
1026
1027 displayGoals( false, null, goals );
1028
1029 String msg = mavenSession.getRootProject().getDescription();
1030 if ( msg != null )
1031 {
1032 LOGGER.info( wrapConsoleMessage( msg, 0, CONSOLE_WIDTH ) );
1033 }
1034
1035 exit( RC_OK );
1036 }
1037 }
1038
1039 private void displayThrowable( Throwable t, boolean displayStackTrace )
1040 {
1041 LOGGER.info( MavenUtils.getMessage( "line" ) );
1042 if ( displayStackTrace )
1043 LOGGER.info( MavenUtils.getMessage( "build.errors.stack" ) );
1044 Throwable localThrowable = t;
1045 while ( localThrowable != null )
1046 {
1047 if ( localThrowable instanceof JellyException )
1048 {
1049 JellyException jellyEx = (JellyException) localThrowable;
1050 if ( jellyEx.getCause() == null )
1051 {
1052 LOGGER.info( MavenUtils.getMessage( "exception.cause" ) + jellyEx.getReason() );
1053 }
1054 if ( displayStackTrace )
1055 {
1056 LOGGER.info( MavenUtils.getMessage( "build.jellyException.file", jellyEx.getFileName() ) );
1057 LOGGER.info( MavenUtils.getMessage( "build.jellyException.element", jellyEx.getElementName() ) );
1058 LOGGER.info( MavenUtils.getMessage( "build.jellyException.line",
1059 Integer.toString( jellyEx.getLineNumber() ) ) );
1060 LOGGER.info( MavenUtils.getMessage( "build.jellyException.column",
1061 Integer.toString( jellyEx.getColumnNumber() ) ) );
1062 }
1063 }
1064 else if ( localThrowable.getLocalizedMessage() != null )
1065 LOGGER.info( MavenUtils.getMessage( "exception.cause" ) + localThrowable.getLocalizedMessage() );
1066 else
1067 LOGGER.info( MavenUtils.getMessage( "exception.cause" ) + localThrowable.getClass().getName() );
1068
1069 localThrowable = localThrowable.getCause();
1070 }
1071 if ( displayStackTrace )
1072 {
1073 LOGGER.info( "" );
1074 LOGGER.info( MavenUtils.getMessage( "build.stacktrace" ), t );
1075 }
1076 }
1077
1078 /**
1079 * Display the Maven version info if the option is present, then exit
1080 */
1081 private void displayVersion()
1082 {
1083 if ( getCli().hasOption( OPT_DISPLAY_VERSION ) )
1084 {
1085 printConsoleMavenHeader();
1086 exit( RC_OK );
1087 }
1088 }
1089
1090 /**
1091 * From the CWD search through the directory hierarchy for an XML-based POM.
1092 *
1093 * @param start
1094 * The starting directory for the POM search.
1095 * @param suffix
1096 * The suffix for the file to be searched for.
1097 * @return The found project.xml file.
1098 */
1099 private File find( File start, String suffix )
1100 {
1101 if ( start == null )
1102 {
1103 return null;
1104 }
1105
1106 File dir = start.getAbsoluteFile();
1107 File file = new File( dir, suffix );
1108
1109 return file.exists() ? file : find( dir.getParentFile(), suffix );
1110 }
1111
1112 /**
1113 * From the CWD search through the directory hierarchy for an XML-based POM.
1114 *
1115 * @param filename
1116 * The filename to find.
1117 * @return The found file.
1118 */
1119 private File find( String filename )
1120 {
1121
1122 return find( new File( "" ), filename );
1123 }
1124
1125 /**
1126 * Get the project descriptor file.
1127 *
1128 * @return The project descriptor file.
1129 * @throws IOException
1130 * when the project.xml parent can't be resolved
1131 */
1132 private File getDescriptorFile() throws IOException
1133 {
1134 File descriptorFile = null;
1135 String descriptorName = null;
1136
1137
1138
1139
1140 if ( getCli().hasOption( OPT_SET_POM_DESCRIPTOR ) )
1141 {
1142 descriptorName = getCli().getOptionValue( OPT_SET_POM_DESCRIPTOR );
1143 }
1144 else
1145 {
1146 descriptorName = POM_FILE_NAME;
1147 }
1148
1149 if ( getCli().hasOption( OPT_FIND_POM_DESCRIPTOR ) )
1150 {
1151 descriptorFile = find( descriptorName );
1152
1153 if ( descriptorFile == null )
1154 {
1155 descriptorFile = new File( descriptorName );
1156 }
1157 }
1158 else
1159 {
1160 descriptorFile = new File( descriptorName );
1161 }
1162 if ( getCli().hasOption( OPT_SET_POM_DESCRIPTOR ) && !descriptorFile.exists() )
1163 {
1164 throw new FileNotFoundException( "Project file '" + descriptorName + "' not found" );
1165 }
1166
1167 descriptorFile = descriptorFile.getAbsoluteFile();
1168 if ( !getCli().hasOption( OPT_WORKING_DIR ) )
1169 {
1170 System.setProperty( "user.dir", descriptorFile.getParentFile().getCanonicalPath() );
1171 }
1172
1173 return descriptorFile;
1174 }
1175
1176 /**
1177 * Intialize main and exit if failures occur
1178 *
1179 * @param args
1180 * command line args
1181 */
1182 private void initializeMain( String[] args )
1183 {
1184 int returnCode = RC_OK;
1185
1186 try
1187 {
1188 initialize( args );
1189 }
1190 catch ( ParseException e )
1191 {
1192 LOGGER.info( e.getLocalizedMessage() );
1193 CLIManager.displayHelp();
1194 returnCode = RC_BAD_ARG;
1195 }
1196 catch ( IOException e )
1197 {
1198 LOGGER.info( e.getLocalizedMessage() );
1199 returnCode = RC_INIT_ERROR;
1200 }
1201 catch ( Exception e )
1202 {
1203 e.printStackTrace();
1204 returnCode = RC_INIT_ERROR;
1205 }
1206
1207 if ( returnCode != RC_OK )
1208 {
1209 LOGGER.info( "" );
1210 exit( returnCode );
1211 }
1212
1213 }
1214
1215 /**
1216 * Initialize the mavenSession bean.
1217 *
1218 * @throws IOException
1219 * when the descriptor file parent can't be resolved
1220 */
1221 private void initializeMavenSession() throws IOException
1222 {
1223
1224
1225
1226
1227
1228 mavenSession = new MavenSession();
1229 mavenSession.setRootContext( getRootContext() );
1230
1231 MavenSession.setRootDescriptorFile( getDescriptorFile() );
1232 }
1233
1234 /**
1235 * Set a property based upon a commandline <code>name=value</code> string.
1236 *
1237 * @param defStr
1238 * The <code>name=value</code> string.
1239 */
1240 private void setCliProperty( String defStr )
1241 {
1242 String name = null;
1243 String value = null;
1244 int equalLoc = defStr.indexOf( "=" );
1245
1246 if ( equalLoc <= 0 )
1247 {
1248 name = defStr.trim();
1249 value = "true";
1250 }
1251 else
1252 {
1253 name = defStr.substring( 0, equalLoc ).trim();
1254 value = defStr.substring( equalLoc + 1 ).trim();
1255 }
1256
1257 if ( name.length() > 0 )
1258 {
1259 System.setProperty( name, value );
1260 }
1261 }
1262
1263 /**
1264 * Reformat a message to display on the console.
1265 *
1266 * @param msg
1267 * the message to display
1268 * @param wrapIndent
1269 * indent
1270 * @param lineWidth
1271 * line width
1272 * @return String
1273 */
1274 private String wrapConsoleLine( String msg, int wrapIndent, int lineWidth )
1275 {
1276 int offset = lineWidth - wrapIndent;
1277 if ( msg.length() <= offset )
1278 {
1279 return msg;
1280 }
1281
1282 BreakIterator bIter = BreakIterator.getWordInstance();
1283 StringBuffer buf = new StringBuffer();
1284 String pad = " ";
1285 int currentPos = 0;
1286 bIter.setText( msg );
1287
1288 while ( offset < bIter.getText().getEndIndex() )
1289 {
1290 if ( Character.isWhitespace( bIter.getText().first() ) )
1291 {
1292
1293 msg = msg.substring( 1 );
1294 bIter.setText( msg );
1295 continue;
1296 }
1297
1298
1299 currentPos = bIter.preceding( offset );
1300
1301 buf.append( msg.substring( 0, currentPos ) );
1302
1303
1304 buf.append( LS );
1305
1306
1307 for ( int i = 0; ( i != wrapIndent ) && ( i < lineWidth ); i++ )
1308 {
1309 buf.append( pad );
1310 }
1311
1312
1313
1314 msg = msg.substring( currentPos );
1315
1316
1317 bIter.setText( msg );
1318 }
1319
1320
1321 while ( Character.isWhitespace( msg.charAt( 0 ) ) )
1322 {
1323 msg = msg.substring( 1 );
1324 }
1325 buf.append( msg );
1326 return buf.toString();
1327 }
1328 }