package org.apache.maven.cli; /* ==================================================================== * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==================================================================== */ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.ParseException; import org.apache.commons.jelly.JellyException; import org.apache.commons.jelly.XMLOutput; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.maven.MavenConstants; import org.apache.maven.MavenException; import org.apache.maven.MavenSession; import org.apache.maven.MavenUtils; import org.apache.maven.UnknownGoalException; import org.apache.maven.jelly.MavenJellyContext; import org.apache.maven.project.Project; import org.apache.maven.verifier.ChecksumVerificationException; import org.apache.maven.verifier.RepoConfigException; import org.apache.maven.verifier.UnsatisfiedDependencyException; import org.apache.maven.werkz.NoActionDefinitionException; import org.apache.maven.werkz.NoSuchGoalException; import org.apache.maven.werkz.UnattainableGoalException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; import java.text.BreakIterator; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; /** * The CLI wrapper for controlling MavenSession processes which are * encapsulated in the MavenSession bean. * * @author Jason van Zyl * * @version $Id: App.java 126378 2005-01-25 12:35:53Z brett $ * * @todo Separate the computation of the available goals from the display * of the goals. The goal computation logic needs to be moved * out of this class and be placed in MavenSession.java proper. * @todo All logging needs to be done via commons-logging. No * System.err.* and Jelly needs to be taught to take a * logger. In an attempt to isolate everything in MavenSession.java. */ public class App { // ------------------------------------------------------------ // C O N S T A N T S // ------------------------------------------------------------ /** Convenience constant for new line character. */ private static final String LS = System.getProperty( "line.separator", "\n" ); /** Default file name for an XML-based POM. */ public static final String POM_FILE_NAME = "project.xml"; /** Default wrap indent. */ private static final int WRAP_INDENT = 35; /** Default console width - for formatting output. */ private static final int CONSOLE_WIDTH = 80; /** return code for ok processing */ private static final int RC_OK = 0; /** return code from command prompt when a bad argument is passed */ private static final int RC_BAD_ARG = 10; /** return code from command prompt when initialization fails */ private static final int RC_INIT_ERROR = 20; /** return code from command prompt when a goal isn't found */ private static final int RC_NO_GOAL = 30; /** return code for bad repository configuration */ private static final int RC_BAD_REPO = 40; /** return code for a goal with no actions */ private static final int RC_EMPTY_GOAL = 50; /** return code for a goal that failed */ private static final int RC_GOAL_FAILED = 60; /** return code for a goal failed from jelly exception being thrown */ private static final int RC_JELLY_FAILED = 70; /** return code for a failure due to Jelly issues */ private static final int RC_BAD_JELLY = 80; /** return code for a failure due to anything else */ private static final int RC_OTHER_FAILURE = 90; /** return code for a failure due to failed dependency */ private static final int RC_FAILED_DEPENDENCY = 100; // O P T I O N S /** Console banner option. */ private static final String CONSOLE_BANNER = "b"; /** Display stack trace option. */ private static final String DISPLAY_STACKTRACE = "e"; /** Find POM option. */ private static final String FIND_POM_DESCRIPTOR = "f"; /** Display goals options. */ private static final String DISPLAY_GOALS = "g"; /** Display help option. */ private static final String DISPLAY_HELP = "h"; /** Display plugin help option. */ private static final String DISPLAY_PLUGIN_HELP = "P"; /** Display project help option. */ private static final String DISPLAY_USAGE = "u"; /** Display info option. */ private static final String DISPLAY_INFO = "i"; /** Display version option. */ private static final String DISPLAY_VERSION = "v"; /** Work offline option. */ private static final String WORK_OFFLINE = "o"; /** Set POM descriptor option. */ private static final String SET_POM_DESCRIPTOR = "p"; /** System property option. */ private static final String SET_SYSTEM_PROPERTY = "D"; /** Emacs output option. */ private static final String EMACS_OUTPUT = "E"; /** Quiet option. */ private static final String QUIET = "q"; /** Debug option. */ private static final String DEBUG = "X"; /** Working dir option. */ private static final String WORKING_DIR = "d"; /** Links properties */ private static final Properties LINKS_PROPERTIES = new Properties(); // ------------------------------------------------------------ // C L A S S M E M B E R S // ------------------------------------------------------------ // ------------------------------------------------------------ // I N S T A N C E M E M B E R S // ------------------------------------------------------------ /** CLI Parser */ private CommandLine commandLine; /** Jelly's underlying writer. */ private Writer writer; /** MavenSession Jelly rootContext. */ private MavenJellyContext rootContext; /** the session to run builds */ private MavenSession mavenSession; /** logger for output */ private Log log = LogFactory.getLog( App.class ); static { URL url = App.class.getResource("/links.properties"); if (url == null) { throw new RuntimeException("Configuration error: can not find the resource links.properties"); } try { LINKS_PROPERTIES.load(url.openStream()); } catch (IOException e) { e.printStackTrace(); } } /** Constructor. */ public App() { } // ---------------------------------------------------------------------- // A C C E S S O R S // ---------------------------------------------------------------------- /** * Set Jelly rootContext. * * @param rootContext The mavenSession jelly rootContext. */ public void setRootContext( MavenJellyContext rootContext ) { this.rootContext = rootContext; } /** * Retrieve the Jelly rootContext. * * @return The Jelly rootContext. */ public MavenJellyContext getRootContext() { return rootContext; } /** * Set the cli parser. * * @param commandLine The command line parser. */ protected void setCli( CommandLine commandLine ) { this.commandLine = commandLine; } /** * Get the CLI parser. * * @return CommandLine The command line parser. */ protected CommandLine getCli() { return this.commandLine; } // ---------------------------------------------------------------------- // I N S T A N C E M E T H O D S // ---------------------------------------------------------------------- /** Perform initialization. * * @param args The command-line arguments. * * @throws ParseException If there is an error parsing the * command-line. * @throws IOException If there is an error while * reading the project descriptor. * @throws MalformedURLException If any of the the URLs denoting * the local or remote repositories is malformed. */ public void initialize( String[] args ) throws ParseException, MalformedURLException, IOException { setCli( CLIManager.parse( args ) ); initializeSystemProperties(); initializeRootContext(); initializeMavenSession(); } /** * Get the project descriptor file. * * @return The project descriptor file. * @throws IOException when the project.xml parent can't be resolved */ private File getDescriptorFile() throws IOException { File descriptorFile = null; String descriptorName = null; // Determine what the name of the XML POM descriptor is. If it is // specified then we will what the user requests, otherwise we will // default to using 'project.xml'. if ( getCli().hasOption( SET_POM_DESCRIPTOR ) ) { descriptorName = getCli().getOptionValue( SET_POM_DESCRIPTOR ); } else { descriptorName = POM_FILE_NAME; } if ( getCli().hasOption( FIND_POM_DESCRIPTOR ) ) { descriptorFile = find( descriptorName ); if ( descriptorFile == null ) { descriptorFile = new File( descriptorName ); } } else { descriptorFile = new File(descriptorName); } if ( getCli().hasOption( SET_POM_DESCRIPTOR ) && !descriptorFile.exists() ) { throw new FileNotFoundException( "Project file '" + descriptorName + "' not found" ); } descriptorFile = descriptorFile.getAbsoluteFile(); if ( !getCli().hasOption( WORKING_DIR ) ) { System.setProperty("user.dir", descriptorFile.getParentFile().getCanonicalPath()); } return descriptorFile; } /** * Initialize the IO streams. * * @throws IOException on error creating XML output and handling System.err * and out */ protected void initializeRootContext() throws IOException { this.writer = new OutputStreamWriter( System.out ); XMLOutput output = XMLOutput.createXMLOutput( writer, false ); if ( getCli().hasOption( WORKING_DIR ) ) { String workingDir = getCli().getOptionValue(WORKING_DIR); File dir = new File(workingDir); if (!dir.isAbsolute()) { workingDir = dir.getAbsolutePath(); } System.setProperty( "user.dir", workingDir ); } // We will assume here that there might not be a project.xml file present // and we will create the root context from the directory gleaned from // the user.dir system property. File basedir = new File( System.getProperty( "user.dir" ) ); MavenJellyContext c = MavenUtils.createContext( basedir ); setRootContext( c ); c.setXMLOutput( output ); // This is just a start at this mode - there'll still be some output if ( getCli().hasOption( QUIET ) ) { // A little bit of log4j specifics needed here Logger.getLogger( "org.apache.maven" ).setLevel( Level.ERROR ); } // -X takes precedence over -q if ( getCli().hasOption( DEBUG ) ) { getRootContext().setDebugOn( Boolean.TRUE ); // A little bit of log4j specifics needed here Logger.getLogger( "org.apache.maven" ).setLevel( Level.DEBUG ); } else { getRootContext().setDebugOn( Boolean.FALSE ); } if ( getCli().hasOption( EMACS_OUTPUT ) ) { getRootContext().setEmacsModeOn( Boolean.TRUE ); } else { getRootContext().setEmacsModeOn( Boolean.FALSE ); } if ( getCli().hasOption( WORK_OFFLINE ) ) { System.setProperty( MavenConstants.ONLINE, "false" ); } } /** * Initialize the mavenSession bean. * @throws IOException when the descriptor file parent can't be resolved */ private void initializeMavenSession() throws IOException { // Even though the rootProject contains the rootContext we set both in // the even that there is no rootProject and the user is requesting goals // to be attained that do not directly relate to a project. A goaly that // may, say, generate the skeletal maven application build. mavenSession = new MavenSession(); mavenSession.setRootContext( getRootContext() ); // note: setRootDescriptor file is a static method MavenSession.setRootDescriptorFile( getDescriptorFile() ); } /** * Setup any system properties that have been specified on * the CLI. */ public void initializeSystemProperties() { if ( getCli().hasOption( SET_SYSTEM_PROPERTY ) ) { String[] defStrs = getCli().getOptionValues( SET_SYSTEM_PROPERTY ); for ( int i = 0; i < defStrs.length; ++i ) { setCliProperty( defStrs[i] ); } } } /** * Set a property based upon a commandline name=value string. * * @param defStr The name=value string. */ private void setCliProperty( String defStr ) { String name = null; String value = null; int equalLoc = defStr.indexOf( "=" ); if ( equalLoc <= 0 ) { name = defStr.trim(); value = "true"; } else { name = defStr.substring( 0, equalLoc ).trim(); value = defStr.substring( equalLoc + 1 ).trim(); } System.setProperty( name, value ); } /** * Perform main operations in a non-static method. * * @param args Arguments passed in from main(). * @param fullStart Date the mavenSession process was started. */ public void doMain( String[] args, Date fullStart ) { initializeMain(args); displayHelp(); displayVersion(); int returnCode = RC_OK; if ( !getCli().hasOption( CONSOLE_BANNER ) ) { printConsoleMavenHeader(); } boolean failed = false; try { mavenSession.initialize(); displayInfo(); displayProjectHelp(); displayPluginHelp(); if ( getCli().hasOption( DISPLAY_GOALS ) ) { displayGoals(); exit( returnCode ); } else { mavenSession.attainGoals( mavenSession.getRootProject(), getCli().getArgList() ); } } catch ( UnsatisfiedDependencyException e ) { failed = true; System.err.println( e.getLocalizedMessage() ); returnCode = RC_FAILED_DEPENDENCY; } catch ( ChecksumVerificationException e ) { failed = true; System.err.println( e.getLocalizedMessage() ); returnCode = RC_FAILED_DEPENDENCY; } catch ( UnknownGoalException e ) { failed = true; System.err.println( MavenUtils.getMessage( "build.failed" ) ); System.err.println( MavenUtils.getMessage( "build.unknownGoalException" , e.getGoalName() ) ); returnCode = RC_NO_GOAL; } catch ( NoSuchGoalException e ) { failed = true; System.err.println( MavenUtils.getMessage( "build.failed" ) ); System.err.println( e.getMessage() ); System.err.println( MavenUtils.getMessage( "build.noSuchGoalException") ); returnCode = RC_NO_GOAL; } catch ( RepoConfigException e ) { failed = true; System.err.println( e.getLocalizedMessage() ); returnCode = RC_BAD_REPO; } catch ( NoActionDefinitionException e ) { failed = true; System.err.println( MavenUtils.getMessage( "build.internalError" ) ); System.err.println( MavenUtils.getMessage( "build.noActionDefinitionException", e.getGoal().getName() ) ); System.err.println( "" ); returnCode = RC_EMPTY_GOAL; if ( getCli().hasOption( DISPLAY_STACKTRACE ) ) { e.printStackTrace(); } displayBugReportHelp(); return; } catch ( UnattainableGoalException e ) { failed = true; returnCode = handleUnattainableGoalException( e ); } catch ( JellyException e ) { returnCode = RC_BAD_JELLY; failed = true; String fileName = e.getFileName(); String elementName = e.getElementName(); int lineNumber = e.getLineNumber(); int column = e.getColumnNumber(); String msg = e.getReason(); System.err.println( MavenUtils.getMessage( "build.jellyException.file", fileName ) ); System.err.println( MavenUtils.getMessage( "build.jellyException.element", elementName ) ); System.err.println( MavenUtils.getMessage( "build.jellyException.line", new Integer( lineNumber ) ) ); System.err.println( MavenUtils.getMessage( "build.jellyException.column", new Integer( column ) ) ); System.err.println( msg ); if ( getCli().hasOption( DISPLAY_STACKTRACE ) ) { e.printStackTrace(); } displayBugReportHelp(); } catch ( Throwable t ) { returnCode = RC_OTHER_FAILURE; failed = true; t.printStackTrace(); displayBugReportHelp(); } if ( !failed ) { log.info( MavenUtils.getMessage( "build.successful" ) ); } final long mb = 1024 * 1024; System.gc(); Runtime r = Runtime.getRuntime(); log.debug( MavenUtils.getMessage( "build.final.memory" ) + ((r.totalMemory() - r.freeMemory()) / mb) + "M/" + (r.totalMemory() / mb) + "M"); Date fullStop = new Date(); DateFormat defaultDate = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG); long fullDiff = fullStop.getTime() - fullStart.getTime(); log.info( MavenUtils.getMessage( "build.total.time" ) + formatTime( fullDiff ) ); log.info( MavenUtils.getMessage( "build.finished.time" ) + defaultDate.format( fullStop ) ); log.info( "" ); exit( returnCode ); } /** * Intialize main and exit if failures occur * @param args command line args */ private void initializeMain(String[] args) { int returnCode = RC_OK; try { initialize( args ); } catch ( ParseException e ) { log.info( e.getLocalizedMessage() ); CLIManager.displayHelp(); returnCode = RC_BAD_ARG; } catch ( IOException e ) { log.info( e.getLocalizedMessage() ); returnCode = RC_INIT_ERROR; } catch ( Exception e ) { e.printStackTrace(); returnCode = RC_INIT_ERROR; } if ( returnCode != RC_OK ) { log.info( "" ); exit( returnCode ); } } /** * Display the command line help if the option is present, then exit */ private void displayHelp() { if ( getCli().hasOption( DISPLAY_HELP ) ) { CLIManager.displayHelp(); log.info( "" ); exit( RC_OK ); } } /** * Display the plugin help if the option is present, then exit */ private void displayPluginHelp() throws MavenException { if ( getCli().hasOption( DISPLAY_PLUGIN_HELP ) ) { String plugin = getCli().getOptionValue( DISPLAY_PLUGIN_HELP ); displayGoals( true, plugin ); if ( plugin != null ) { Project project = mavenSession.getPluginProjectFromGoal( plugin ); if ( project != null && project.getDescription() != null ) { log.info( wrapConsoleMessage( project.getDescription(), 0, CONSOLE_WIDTH ) ); } } exit( RC_OK ); } } /** * Display information on how to file a bug report if an unknown error occurs. */ private void displayBugReportHelp() { StringBuffer sb = new StringBuffer(); sb.append( MavenUtils.getMessage( "displayBugReportHelp.line1" ) ).append('\n'); sb.append( MavenUtils.getMessage( "displayBugReportHelp.line2" ) ).append('\n'); sb.append( MavenUtils.getMessage( "displayBugReportHelp.line3", LINKS_PROPERTIES.get("faqUrl") ) ).append('\n'); sb.append( MavenUtils.getMessage( "displayBugReportHelp.line4" ) ).append('\n'); sb.append( MavenUtils.getMessage( "displayBugReportHelp.line5", LINKS_PROPERTIES.get("usersMailingListUrl") ) ).append('\n'); if ((mavenSession != null) && (mavenSession.getRootProject() != null)) { sb.append( MavenUtils.getMessage( "displayBugReportHelp.line6", mavenSession.getRootProject().getIssueTrackingUrl() ) ).append('\n'); } else { sb.append( MavenUtils.getMessage( "displayBugReportHelp.line6", "???issueTrackingUrl???" ) ).append('\n'); } sb.append( MavenUtils.getMessage( "displayBugReportHelp.line7" ) ); log.info( "" ); log.info( sb.toString() ); log.info( "" ); } /** * Display the project help if the option is present, then exit */ private void displayProjectHelp() throws MavenException { if ( getCli().hasOption( DISPLAY_USAGE ) ) { Set goals = mavenSession.getProjectGoals( mavenSession.getRootProject() ); String title = MavenUtils.getMessage( "displayProjectHelp.title" ); log.info( title ); log.info( format( "", title.length(), '=' ) ); displayGoals( false, null, goals ); String msg = mavenSession.getRootProject().getDescription(); if ( msg != null ) { log.info( wrapConsoleMessage( msg, 0, CONSOLE_WIDTH ) ); } exit( RC_OK ); } } /** * Display the command line info if the option is present, then exit */ private void displayInfo() { if ( getCli().hasOption( DISPLAY_INFO ) ) { CLIManager.displayInfo(); log.info( "" ); log.info( MavenUtils.getMessage( "displayInfo.info1" ) ); for ( Iterator i = mavenSession.getPluginList().iterator(); i.hasNext(); ) { log.info( " " + i.next() ); } Properties buildProperties = new Properties(); try { buildProperties.load( new FileInputStream( System.getProperty( "user.home" ) + "/build.properties" ) ); } catch ( IOException e ) { log.error( MavenUtils.getMessage( "displayInfo.error" ) + e.getMessage() ); } log.info( MavenUtils.getMessage( "displayInfo.info2" ) + buildProperties ); exit( RC_OK ); } } /** * Display the Maven version info if the option is present, then exit */ private void displayVersion() { if ( getCli().hasOption( DISPLAY_VERSION ) ) { printConsoleMavenHeader(); exit( RC_OK ); } } /** Handle an UnattainableGoalException. * * @param e The exception. * * @return The return code. */ private int handleUnattainableGoalException( UnattainableGoalException e ) { int returnCode = RC_GOAL_FAILED; System.err.println( "" ); System.err.println( MavenUtils.getMessage( "build.failed" ) ); String msg = null; String fileName = null; String elementName = null; int lineNumber = -1; int column = -1; Throwable rootCause = e.getRootCause(); if ( rootCause != null ) { if ( rootCause instanceof JellyException ) { returnCode = RC_JELLY_FAILED; JellyException jellyEx = (JellyException) rootCause; fileName = jellyEx.getFileName(); elementName = jellyEx.getElementName(); lineNumber = jellyEx.getLineNumber(); column = jellyEx.getColumnNumber(); rootCause = jellyEx.getCause(); if ( rootCause == null ) { msg = jellyEx.getReason(); } else { if ( getCli().hasOption( DISPLAY_STACKTRACE ) ) { rootCause.printStackTrace(); } msg = rootCause.getLocalizedMessage(); if ( msg == null ) { msg = rootCause.getClass().getName(); } } System.err.println( MavenUtils.getMessage( "build.jellyException.file", fileName ) ); System.err.println( MavenUtils.getMessage( "build.jellyException.element", elementName ) ); System.err.println( MavenUtils.getMessage( "build.jellyException.line", new Integer( lineNumber ) ) ); System.err.println( MavenUtils.getMessage( "build.jellyException.column", new Integer( column ) ) ); } else { if ( getCli().hasOption( DISPLAY_STACKTRACE ) ) { rootCause.printStackTrace(); } msg = rootCause.getLocalizedMessage(); if ( msg == null ) { msg = rootCause.getClass().getName(); } } } else { msg = e.getLocalizedMessage(); } System.err.println( msg ); if ( getCli().hasOption( DEBUG ) ) { e.printStackTrace(); } return returnCode; } /** * From the CWD search through the directory hierarchy for * an XML-based POM. * * @param filename The filename to find. * @return The found file. */ private File find( String filename ) { // An empty string should resolve to the current directory (user.dir) return find( new File( "" ), filename ); } /** * From the CWD search through the directory hierarchy for * an XML-based POM. * * @param start The starting directory for the POM search. * @param suffix The suffix for the file to be searched for. * @return The found project.xml file. */ private File find( File start, String suffix ) { if ( start == null ) { return null; } File dir = start.getAbsoluteFile(); File file = new File( dir, suffix ); return file.exists() ? file : find( dir.getParentFile(), suffix ); } /** * Prints the MavenSession header. */ protected void printConsoleMavenHeader() { Properties p = new Properties(); InputStream is = getClass().getResourceAsStream( "/driver.properties" ); try { p.load( is ); } catch ( IOException e ) { log.error( MavenUtils.getMessage( "printConsoleMavenHeader.error" ) + e ); } finally { try { is.close(); } catch ( IOException e ) {} } log.info( " __ __" ); log.info( "| \\/ |__ _Apache__ ___" ); log.info( "| |\\/| / _` \\ V / -_) ' \\ ~ intelligent projects ~" ); log.info( "|_| |_\\__,_|\\_/\\___|_||_| v. " + p.getProperty( "maven.application.version" ) ); log.info( "" ); } /** * Display helpful information regarding * all documented goals. */ protected void displayGoals() { displayGoals( false, null ); } /** * Display helpful information regarding all documented goals. * @param pluginOnly show information for the given plugin only * @param plugin plugin to show info for */ protected void displayGoals( boolean pluginOnly, String plugin ) { String title = MavenUtils.getMessage( "displayGoals.title" ); if ( pluginOnly ) { title = ( plugin == null ? MavenUtils.getMessage( "displayGoals.title.pluginOnly.null" ) : MavenUtils.getMessage( "displayGoals.title.pluginOnly.notNull" ) + plugin ); } log.info( title ); log.info( format( "", title.length(), '=' ) ); Set goals = mavenSession.getAllGoalNames(); displayGoals( pluginOnly, plugin, goals ); } private void displayGoals( boolean pluginOnly, String plugin, Set goals ) { Map map = new HashMap(); for ( Iterator i = goals.iterator(); i.hasNext(); ) { String goal = ( String ) i.next(); String pluginName = ""; int index = goal.indexOf( ':' ); if ( index >= 0 ) { pluginName = goal.substring( 0, index ); } List l = ( List ) map.get( pluginName ); if ( l == null ) { l = new ArrayList(); map.put( pluginName, l ); } l.add( goal.substring( index + 1 ) ); } List goalList = ( List ) map.get( "" ); if ( goalList != null ) { for ( Iterator i = goalList.iterator(); i.hasNext(); ) { String goal = ( String ) i.next(); if ( map.containsKey( goal ) ) { i.remove(); List pluginGoals = ( List ) map.get( goal ); if ( pluginGoals == null ) { pluginGoals = new ArrayList(); map.put( goal, pluginGoals ); } // don't add the empty goal as we always attempt to display the default } } } List keys = new ArrayList( map.keySet() ); Collections.sort( keys ); List undocumentedGoals = new ArrayList(); for ( Iterator i = keys.iterator(); i.hasNext(); ) { String pluginName = ( String ) i.next(); if ( pluginOnly ) { if ( plugin == null ) { // only show default goal displayDefaultGoal( pluginName, mavenSession.getGoalDescription( pluginName ), false ); continue; } else if ( !pluginName.equals( plugin ) ) { continue; } } displayDefaultGoal( pluginName, mavenSession.getGoalDescription( pluginName ), true ); List l = ( List ) map.get( pluginName ); Collections.sort( l ); for ( Iterator j = l.iterator(); j.hasNext(); ) { String goalName = ( String ) j.next(); String fullGoalName = pluginName.length() == 0 ? goalName : pluginName + ":" + goalName; String goalDescription = mavenSession.getGoalDescription( fullGoalName ); if ( goalDescription != null ) { displayGoal( goalName, goalDescription ); } else { undocumentedGoals.add( fullGoalName ); } } } if ( undocumentedGoals.isEmpty() == false ) { displayGoalsWithoutDescriptions( undocumentedGoals ); } log.info( "" ); } private void displayGoal( String goalName, String goalDescription ) { if ( "".equals( goalName ) ) { goalName = "( NO GOAL )"; } String msgPrefix = format( " " + goalName + " ", WRAP_INDENT, '.' ) + " "; log.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) ); } private void displayDefaultGoal( String goalName, String goalDescription, boolean newLine ) { if ( "".equals( goalName ) ) { return; } String msgPrefix = format( "[" + goalName + "]", WRAP_INDENT, ' ' ) + " "; if ( goalDescription == null ) { goalDescription = "( NO DEFAULT GOAL )"; } if ( newLine ) { msgPrefix = LS + msgPrefix; } log.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) ); } /** Display goals without descriptions. * * @param list List of undocument goal names. */ private void displayGoalsWithoutDescriptions( List list ) { log.info( "" ); log.info( MavenUtils.getMessage( "displayGoalsWithoutDescriptions.info" ) ); log.info( "" ); for ( Iterator i = list.iterator(); i.hasNext();) { String goalName = ( String ) i.next(); log.info( " " + goalName ); } } /** Produce a formatted/padded string. * * @param orig The string to format. * @param width The width of the resulting formatted string. * @param pad The trailing pad character. * * @return The formatted string, or the original string * if the length is already >= width. */ protected String format( String orig, int width, char pad ) { if ( orig.length() >= width ) { return orig; } StringBuffer buf = new StringBuffer().append( orig ); int diff = width - orig.length(); for ( int i = 0; i < diff; ++i ) { buf.append( pad ); } return buf.toString(); } /** * Nicely wraps a message for console output, using a word BreakIterator * instance to determine wrapping breaks. * * @param msg the string message for the console * @param wrapIndent the number of characters to indent all lines * after the first one * @param lineWidth the console width that determines where to wrap * @return the message wrapped for the console */ protected String wrapConsoleMessage( String msg, int wrapIndent, int lineWidth ) { if ( msg.indexOf( '\n' ) < 0 && msg.indexOf( '\r' ) < 0 ) { return wrapConsoleLine( msg, wrapIndent, lineWidth ); } StringBuffer buf = new StringBuffer(); StringTokenizer tok = new StringTokenizer( msg, "\n\r" ); while ( tok.hasMoreTokens() ) { String token = tok.nextToken().trim(); if ( token.length() > 0 ) { buf.append( wrapConsoleLine( token, wrapIndent, lineWidth ) ); buf.append( LS ); } } return buf.toString(); } private String wrapConsoleLine( String msg, int wrapIndent, int lineWidth ) { int offset = lineWidth - wrapIndent; if ( msg.length() <= offset ) { return msg; } BreakIterator bIter = BreakIterator.getWordInstance(); StringBuffer buf = new StringBuffer(); String pad = " "; int currentPos = 0; bIter.setText( msg ); while ( offset < bIter.getText().getEndIndex() ) { if ( Character.isWhitespace( bIter.getText().first() ) ) { // remove leading whitespace and continue msg = msg.substring( 1 ); bIter.setText( msg ); continue; } // get the last boundary before the specified offset currentPos = bIter.preceding( offset ); // append from the start to currentPos buf.append( msg.substring( 0, currentPos ) ); // start next line buf.append( LS ); //pad with spaces to create indent for ( int i = 0; i != wrapIndent && i < lineWidth; i++ ) { buf.append( pad ); } // set the text of the break iterator to be the rest // of the string not already appended msg = msg.substring( currentPos ); //reset the text for another go bIter.setText( msg ); } // remove leading whitespace and continue while ( Character.isWhitespace( msg.charAt( 0 ) ) ) { msg = msg.substring( 1 ); } buf.append( msg ); return buf.toString(); } /** * Format a time string. * * @param ms Duration in ms. * @return String The formatted time string. */ protected static String formatTime( long ms ) { long secs = ms / 1000; long min = secs / 60; secs = secs % 60; if ( min > 0 ) { return min + MavenUtils.getMessage( "formatTime.minutes" ) + secs + MavenUtils.getMessage( "formatTime.seconds" ); } else { return secs + MavenUtils.getMessage( "formatTime.seconds" ); } } /** * Main CLI entry point for MavenSession. * * @param args CLI arguments. */ public static void main( String[] args ) { Date start = new Date(); App app = new App(); app.doMain( args, start ); } /** * To allow subclasses stop the app from exiting * @param status the value to exit with */ protected void exit( int status ) { System.exit( status ); } }