package org.apache.maven.cli;
/* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.log4j.PropertyConfigurator;
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 <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
*
* @version $Id: App.java 530196 2007-04-18 23:04:51Z aheritier $
*
* @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
{
/** Default file name for an XML-based POM. */
public static final String POM_FILE_NAME = "project.xml";
/** Default console width - for formatting output. */
private static final int CONSOLE_WIDTH = 80;
/** Links properties */
private static final Properties LINKS_PROPERTIES = new Properties();
/** logger for output */
private static final Log LOGGER = LogFactory.getLog( App.class );
/** Convenience constant for new line character. */
private static final String LS = System.getProperty( "line.separator", "\n" );
/** Console banner option. */
private static final String OPT_CONSOLE_BANNER = "b";
/** Debug option. */
private static final String OPT_DEBUG = "X";
/** Display goals options. */
private static final String OPT_DISPLAY_GOALS = "g";
/** Display help option. */
private static final String OPT_DISPLAY_HELP = "h";
/** Display info option. */
private static final String OPT_DISPLAY_INFO = "i";
/** Display plugin help option. */
private static final String OPT_DISPLAY_PLUGIN_HELP = "P";
/** Display stack trace option. */
private static final String OPT_DISPLAY_STACKTRACE = "e";
/** Display project help option. */
private static final String OPT_DISPLAY_USAGE = "u";
/** Display version option. */
private static final String OPT_DISPLAY_VERSION = "v";
/** Emacs output option. */
private static final String OPT_EMACS_OUTPUT = "E";
/** Find POM option. */
private static final String OPT_FIND_POM_DESCRIPTOR = "f";
/** Quiet option. */
private static final String OPT_QUIET = "q";
/** Set POM descriptor option. */
private static final String OPT_SET_POM_DESCRIPTOR = "p";
/** System property option. */
private static final String OPT_SET_SYSTEM_PROPERTY = "D";
/** Work offline option. */
private static final String OPT_WORK_OFFLINE = "o";
/** Working dir option. */
private static final String OPT_WORKING_DIR = "d";
/** return code from command prompt when a bad argument is passed */
private static final int RC_BAD_ARG = 10;
/** return code for a failure due to Jelly issues */
private static final int RC_BAD_JELLY = 80;
/** 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 failure due to failed dependency */
private static final int RC_FAILED_DEPENDENCY = 100;
/** return code for a goal that failed */
private static final int RC_GOAL_FAILED = 60;
/** return code from command prompt when initialization fails */
private static final int RC_INIT_ERROR = 20;
/** return code for a goal failed from jelly exception being thrown */
private static final int RC_JELLY_FAILED = 70;
/** return code from command prompt when a goal isn't found */
private static final int RC_NO_GOAL = 30;
/** return code for ok processing */
private static final int RC_OK = 0;
/** return code for a failure due to anything else */
private static final int RC_OTHER_FAILURE = 90;
/** Default wrap indent. */
private static final int WRAP_INDENT = 35;
static
{
URL urlLog4j = App.class.getResource( "/log4j.properties" );
if ( urlLog4j == null )
{
throw new RuntimeException( "Configuration error: can not find the resource log4j.properties" );
}
PropertyConfigurator.configure( urlLog4j );
URL urlLinks = App.class.getResource( "/links.properties" );
if ( urlLinks == null )
{
throw new RuntimeException( "Configuration error: can not find the resource links.properties" );
}
try
{
LINKS_PROPERTIES.load( urlLinks.openStream() );
}
catch ( IOException e )
{
throw new RuntimeException( "Configuration error: Unable to load the resource links.properties", e );
}
}
/**
* 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 );
}
/**
* 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" );
}
}
/** CLI Parser */
private CommandLine commandLine;
/** the session to run builds */
private MavenSession mavenSession;
/** MavenSession Jelly rootContext. */
private MavenJellyContext rootContext;
/** Jelly's underlying writer. */
private Writer writer;
/** Constructor. */
public App()
{
}
/**
* 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( OPT_CONSOLE_BANNER ) )
{
printConsoleMavenHeader();
}
if ( !MavenSession.getRootDescriptorFile().exists() )
{
LOGGER.warn( MavenUtils.getMessage( "build.no.pom.found" ) );
LOGGER.warn( "" );
}
boolean failed = false;
boolean displayStackTrace = getCli().hasOption( OPT_DISPLAY_STACKTRACE ) || getCli().hasOption( OPT_DEBUG );
try
{
mavenSession.initialize();
displayInfo();
displayProjectHelp();
displayPluginHelp();
if ( getCli().hasOption( OPT_DISPLAY_GOALS ) )
{
displayGoals();
exit( returnCode );
}
else
{
mavenSession.attainGoals( mavenSession.getRootProject(), getCli().getArgList() );
}
}
catch ( UnsatisfiedDependencyException e )
{
failed = true;
displayBuildFailed( e, false, true, displayStackTrace );
returnCode = RC_FAILED_DEPENDENCY;
}
catch ( ChecksumVerificationException e )
{
failed = true;
displayBuildFailed( e, false, true, displayStackTrace );
returnCode = RC_FAILED_DEPENDENCY;
}
catch ( UnknownGoalException e )
{
failed = true;
LOGGER.info( MavenUtils.getMessage( "line" ) );
LOGGER.info( MavenUtils.getMessage( "build.unknownGoalException", e.getGoalName() ) );
displayBuildFailed( e, false, false, displayStackTrace );
returnCode = RC_NO_GOAL;
}
catch ( NoSuchGoalException e )
{
failed = true;
displayBuildFailed( e, false, true, displayStackTrace );
returnCode = RC_NO_GOAL;
}
catch ( RepoConfigException e )
{
failed = true;
displayBuildFailed( e, false, true, displayStackTrace );
returnCode = RC_BAD_REPO;
}
catch ( NoActionDefinitionException e )
{
failed = true;
LOGGER.info( MavenUtils.getMessage( "line" ) );
LOGGER.info( MavenUtils.getMessage( "build.internalError" ) );
LOGGER.info( MavenUtils.getMessage( "build.noActionDefinitionException", e.getGoal().getName() ) );
displayBuildFailed( e, true, false, displayStackTrace );
returnCode = RC_EMPTY_GOAL;
}
catch ( UnattainableGoalException e )
{
failed = true;
displayBuildFailed( e, false, true, displayStackTrace );
if ( e.getCause() instanceof JellyException )
{
returnCode = RC_JELLY_FAILED;
}
else
{
returnCode = RC_GOAL_FAILED;
}
}
catch ( JellyException e )
{
failed = true;
displayBuildFailed( e, true, true, displayStackTrace );
returnCode = RC_BAD_JELLY;
}
catch ( MavenException e )
{
failed = true;
displayBuildFailed( e, MavenUtils.MAVEN_UNKNOWN_ERROR.equals( e.getMessage() ), true, displayStackTrace );
returnCode = RC_OTHER_FAILURE;
}
catch ( Throwable t )
{
failed = true;
displayBuildFailed( t, true, true, displayStackTrace );
returnCode = RC_OTHER_FAILURE;
}
if ( !failed )
{
LOGGER.warn( MavenUtils.getMessage( "line" ) );
LOGGER.warn( MavenUtils.getMessage( "build.successful" ) );
}
LOGGER.warn( MavenUtils.getMessage( "line" ) );
Date fullStop = new Date();
DateFormat defaultDate = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.LONG );
long fullDiff = fullStop.getTime() - fullStart.getTime();
LOGGER.warn( MavenUtils.getMessage( "build.total.time", formatTime( fullDiff ) ) );
LOGGER.warn( MavenUtils.getMessage( "build.finished.time", defaultDate.format( fullStop ) ) );
final long mb = 1024 * 1024;
System.gc();
Runtime r = Runtime.getRuntime();
LOGGER.warn( MavenUtils.getMessage( "build.final.memory", ( ( r.totalMemory() - r.freeMemory() ) / mb ) + "M/"
+ ( r.totalMemory() / mb ) + "M" ) );
LOGGER.warn( MavenUtils.getMessage( "line" ) );
exit( returnCode );
}
/**
* Retrieve the Jelly rootContext.
*
* @return The Jelly rootContext.
*/
public MavenJellyContext getRootContext()
{
return rootContext;
}
/**
* 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();
customizeLogging();
}
/**
* Setup any system properties that have been specified on the CLI.
*/
public void initializeSystemProperties()
{
if ( getCli().hasOption( OPT_SET_SYSTEM_PROPERTY ) )
{
String[] defStrs = getCli().getOptionValues( OPT_SET_SYSTEM_PROPERTY );
for ( int i = 0; i < defStrs.length; ++i )
{
setCliProperty( defStrs[i] );
}
}
}
/**
* Set Jelly rootContext.
*
* @param rootContext
* The mavenSession jelly rootContext.
*/
public void setRootContext( MavenJellyContext rootContext )
{
this.rootContext = rootContext;
}
/**
* 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 );
}
LOGGER.info( title );
LOGGER.info( format( "", title.length(), '=' ) );
Set goals = mavenSession.getAllGoalNames();
displayGoals( pluginOnly, plugin, goals );
}
/**
* To allow subclasses stop the app from exiting
*
* @param status
* the value to exit with
*/
protected void exit( int status )
{
System.exit( status );
}
/**
* 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 >= <code>width</code>.
*/
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();
}
/**
* Get the CLI parser.
*
* @return CommandLine The command line parser.
*/
protected CommandLine getCli()
{
return this.commandLine;
}
/**
* 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( OPT_WORKING_DIR ) )
{
String workingDir = getCli().getOptionValue( OPT_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( OPT_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( OPT_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( OPT_EMACS_OUTPUT ) )
{
getRootContext().setEmacsModeOn( Boolean.TRUE );
}
else
{
getRootContext().setEmacsModeOn( Boolean.FALSE );
}
if ( getCli().hasOption( OPT_WORK_OFFLINE ) )
{
System.setProperty( MavenConstants.ONLINE, "false" );
}
}
/**
* Prints the MavenSession header.
*/
protected void printConsoleMavenHeader()
{
Properties p = new Properties();
InputStream is = getClass().getResourceAsStream( "/driver.properties" );
try
{
p.load( is );
}
catch ( IOException e )
{
LOGGER.error( MavenUtils.getMessage( "printConsoleMavenHeader.error" ) + e );
}
finally
{
try
{
is.close();
}
catch ( IOException e )
{
LOGGER.debug( "WARNING: Cannot close stream!", e );
}
}
LOGGER.info( " __ __" );
LOGGER.info( "| \\/ |__ _Apache__ ___" );
LOGGER.info( "| |\\/| / _` \\ V / -_) ' \\ ~ intelligent projects ~" );
LOGGER.info( "|_| |_\\__,_|\\_/\\___|_||_| v. " + p.getProperty( "maven.application.version" ) );
LOGGER.info( "" );
}
/**
* Set the cli parser.
*
* @param commandLine
* The command line parser.
*/
protected void setCli( CommandLine commandLine )
{
this.commandLine = commandLine;
}
/**
* 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();
}
/**
* Customize the log4j configuration from properties (read in system, user or project scope).
*/
private void customizeLogging()
{
Iterator iter = getRootContext().getVariableNames();
Properties log4jProperties = new Properties();
String propertyName;
while ( iter.hasNext() )
{
propertyName = (String) iter.next();
if ( propertyName.startsWith( "log4j" ) )
{
log4jProperties.put( propertyName, getRootContext().getVariable( propertyName ) );
}
}
// Modify the default log4j settings
PropertyConfigurator.configure( log4jProperties );
log4jProperties = null;
iter = null;
}
/**
* Display information on how to file a bug report if an unknown error occurs.
*/
private void displayBugReportHelp()
{
LOGGER.info( MavenUtils.getMessage( "line" ) );
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' );
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line6", LINKS_PROPERTIES.get( "issueTrackingUrl" ) ) ).append(
'\n' );
sb.append( MavenUtils.getMessage( "displayBugReportHelp.line7" ) );
LOGGER.info( "" );
LOGGER.info( sb.toString() );
LOGGER.info( "" );
}
private void displayBuildFailed( Throwable t, boolean displayBugReportHelp, boolean displayErrors,
boolean displayStackTrace )
{
if ( displayBugReportHelp )
displayBugReportHelp();
if ( displayErrors )
displayThrowable( t, displayStackTrace );
LOGGER.warn( MavenUtils.getMessage( "line" ) );
LOGGER.warn( MavenUtils.getMessage( "build.failed" ) );
}
/**
* Display helpful information about the given default goal.
*
* @param goalName
* goal to show info for
* @param goalDescription
* the description of the goal
* @param newLine
* whether to append a newline
*/
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;
}
LOGGER.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
}
/**
* Display helpful information about the given goal.
*
* @param goalName
* goal to show info for
* @param goalDescription
* the description of the goal
*/
private void displayGoal( String goalName, String goalDescription )
{
if ( "".equals( goalName ) )
{
goalName = "( NO GOAL )";
}
String msgPrefix = format( " " + goalName + " ", WRAP_INDENT, '.' ) + " ";
LOGGER.info( msgPrefix + wrapConsoleMessage( goalDescription, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
}
/**
* Display helpful information regarding all documented goals.
*
* @param pluginOnly
* show information for the given plugin only
* @param plugin
* plugin to show info for
* @param goals
* the set of 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 );
}
LOGGER.info( "" );
}
/**
* Display goals without descriptions.
*
* @param list
* List of undocument goal names.
*/
private void displayGoalsWithoutDescriptions( List list )
{
LOGGER.info( "" );
LOGGER.info( MavenUtils.getMessage( "displayGoalsWithoutDescriptions.info" ) );
LOGGER.info( "" );
for ( Iterator i = list.iterator(); i.hasNext(); )
{
String goalName = (String) i.next();
LOGGER.info( " " + goalName );
}
}
/**
* Display the command line help if the option is present, then exit
*/
private void displayHelp()
{
if ( getCli().hasOption( OPT_DISPLAY_HELP ) )
{
CLIManager.displayHelp();
LOGGER.info( "" );
exit( RC_OK );
}
}
/**
* Display the command line info if the option is present, then exit
*/
private void displayInfo()
{
if ( getCli().hasOption( OPT_DISPLAY_INFO ) )
{
CLIManager.displayInfo();
LOGGER.info( "" );
LOGGER.info( MavenUtils.getMessage( "displayInfo.info1" ) );
for ( Iterator i = mavenSession.getPluginList().iterator(); i.hasNext(); )
{
LOGGER.info( " " + i.next() );
}
Properties buildProperties = new Properties();
FileInputStream fis = null;
try
{
fis = new FileInputStream( System.getProperty( "user.home" ) + "/build.properties" );
buildProperties.load( fis );
}
catch ( IOException e )
{
LOGGER.error( MavenUtils.getMessage( "displayInfo.error" ) + e.getMessage() );
}
finally
{
if ( fis != null )
{
try
{
fis.close();
}
catch ( IOException e )
{
LOGGER.debug( "WARNING: Cannot close stream!", e );
}
fis = null;
}
}
LOGGER.info( MavenUtils.getMessage( "displayInfo.info2" ) + buildProperties );
exit( RC_OK );
}
}
/**
* Display the plugin help if the option is present, then exit.
*
* @throws MavenException
* when anything goes wrong
*/
private void displayPluginHelp() throws MavenException
{
if ( getCli().hasOption( OPT_DISPLAY_PLUGIN_HELP ) )
{
String plugin = getCli().getOptionValue( OPT_DISPLAY_PLUGIN_HELP );
displayGoals( true, plugin );
if ( plugin != null )
{
Project project = mavenSession.getPluginProjectFromGoal( plugin );
if ( ( project != null ) && ( project.getDescription() != null ) )
{
LOGGER.info( wrapConsoleMessage( project.getDescription(), 0, CONSOLE_WIDTH ) );
}
}
exit( RC_OK );
}
}
/**
* Display the project help if the option is present, then exit.
*
* @throws MavenException
* when anything goes wrong
*/
private void displayProjectHelp() throws MavenException
{
if ( getCli().hasOption( OPT_DISPLAY_USAGE ) )
{
Set goals = mavenSession.getProjectGoals( mavenSession.getRootProject() );
String title = MavenUtils.getMessage( "displayProjectHelp.title" );
LOGGER.info( title );
LOGGER.info( format( "", title.length(), '=' ) );
LOGGER.info( "" );
Project rootProject = mavenSession.getRootProject();
if ( ( rootProject.getBuild() != null ) && ( rootProject.getBuild().getDefaultGoal() != null ) )
{
String defaultGoal = rootProject.getBuild().getDefaultGoal();
String msg = MavenUtils.getMessage( "displayGoals.defaultGoal" ) + defaultGoal;
LOGGER.info( wrapConsoleMessage( msg, WRAP_INDENT + 1, CONSOLE_WIDTH ) );
}
displayGoals( false, null, goals );
String msg = mavenSession.getRootProject().getDescription();
if ( msg != null )
{
LOGGER.info( wrapConsoleMessage( msg, 0, CONSOLE_WIDTH ) );
}
exit( RC_OK );
}
}
private void displayThrowable( Throwable t, boolean displayStackTrace )
{
LOGGER.info( MavenUtils.getMessage( "line" ) );
if ( displayStackTrace )
LOGGER.info( MavenUtils.getMessage( "build.errors.stack" ) );
Throwable localThrowable = t;
while ( localThrowable != null )
{
if ( localThrowable instanceof JellyException )
{
JellyException jellyEx = (JellyException) localThrowable;
if ( jellyEx.getCause() == null )
{
LOGGER.info( MavenUtils.getMessage( "exception.cause" ) + jellyEx.getReason() );
}
if ( displayStackTrace )
{
LOGGER.info( MavenUtils.getMessage( "build.jellyException.file", jellyEx.getFileName() ) );
LOGGER.info( MavenUtils.getMessage( "build.jellyException.element", jellyEx.getElementName() ) );
LOGGER.info( MavenUtils.getMessage( "build.jellyException.line",
Integer.toString( jellyEx.getLineNumber() ) ) );
LOGGER.info( MavenUtils.getMessage( "build.jellyException.column",
Integer.toString( jellyEx.getColumnNumber() ) ) );
}
}
else if ( localThrowable.getLocalizedMessage() != null )
LOGGER.info( MavenUtils.getMessage( "exception.cause" ) + localThrowable.getLocalizedMessage() );
else
LOGGER.info( MavenUtils.getMessage( "exception.cause" ) + localThrowable.getClass().getName() );
localThrowable = localThrowable.getCause();
}
if ( displayStackTrace )
{
LOGGER.info( "" );
LOGGER.info( MavenUtils.getMessage( "build.stacktrace" ), t );
}
}
/**
* Display the Maven version info if the option is present, then exit
*/
private void displayVersion()
{
if ( getCli().hasOption( OPT_DISPLAY_VERSION ) )
{
printConsoleMavenHeader();
exit( RC_OK );
}
}
/**
* 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 );
}
/**
* 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 );
}
/**
* 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( OPT_SET_POM_DESCRIPTOR ) )
{
descriptorName = getCli().getOptionValue( OPT_SET_POM_DESCRIPTOR );
}
else
{
descriptorName = POM_FILE_NAME;
}
if ( getCli().hasOption( OPT_FIND_POM_DESCRIPTOR ) )
{
descriptorFile = find( descriptorName );
if ( descriptorFile == null )
{
descriptorFile = new File( descriptorName );
}
}
else
{
descriptorFile = new File( descriptorName );
}
if ( getCli().hasOption( OPT_SET_POM_DESCRIPTOR ) && !descriptorFile.exists() )
{
throw new FileNotFoundException( "Project file '" + descriptorName + "' not found" );
}
descriptorFile = descriptorFile.getAbsoluteFile();
if ( !getCli().hasOption( OPT_WORKING_DIR ) )
{
System.setProperty( "user.dir", descriptorFile.getParentFile().getCanonicalPath() );
}
return descriptorFile;
}
/**
* 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 )
{
LOGGER.info( e.getLocalizedMessage() );
CLIManager.displayHelp();
returnCode = RC_BAD_ARG;
}
catch ( IOException e )
{
LOGGER.info( e.getLocalizedMessage() );
returnCode = RC_INIT_ERROR;
}
catch ( Exception e )
{
e.printStackTrace();
returnCode = RC_INIT_ERROR;
}
if ( returnCode != RC_OK )
{
LOGGER.info( "" );
exit( returnCode );
}
}
/**
* 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() );
}
/**
* Set a property based upon a commandline <code>name=value</code> string.
*
* @param defStr
* The <code>name=value</code> 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();
}
if ( name.length() > 0 )
{
System.setProperty( name, value );
}
}
/**
* Reformat a message to display on the console.
*
* @param msg
* the message to display
* @param wrapIndent
* indent
* @param lineWidth
* line width
* @return String
*/
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();
}
}