2009-03-23 05:06:22 +08:00
package org.broadinstitute.sting.utils.cmdLine ;
import org.apache.log4j.* ;
2009-05-08 03:08:19 +08:00
import org.broadinstitute.sting.utils.JVMUtils ;
import org.broadinstitute.sting.utils.StingException ;
2009-03-23 05:06:22 +08:00
import java.io.IOException ;
import java.text.DateFormat ;
import java.text.SimpleDateFormat ;
2009-05-07 02:16:11 +08:00
import java.util.EnumSet ;
2009-03-23 05:06:22 +08:00
/ * *
* User : aaron
* Date : Mar 19 , 2009
* Time : 3 : 54 : 56 PM
* < p / >
* The Broad Institute
* SOFTWARE COPYRIGHT NOTICE AGREEMENT
* This software and its documentation are copyright 2009 by the
* Broad Institute / Massachusetts Institute of Technology . All rights are reserved .
* < p / >
* This software is supplied without any warranty or guaranteed support whatsoever . Neither
* the Broad Institute nor MIT can be responsible for its use , misuse , or functionality .
* < p / >
* < p / >
* This class is our implementation of the command line parser , similar to Pickard ' s . We instead
* support GNU style command line arguements , and use this class to setup the global parser .
* /
public abstract class CommandLineProgram {
/ * *
2009-05-07 02:16:11 +08:00
* The command - line program and the arguments it returned .
2009-03-23 05:06:22 +08:00
* /
2009-05-07 02:16:11 +08:00
private ParsingEngine parser = null ;
2009-03-23 05:06:22 +08:00
/ * *
* our log , which we want to capture anything from org . broadinstitute . sting
* /
private static Logger logger = Logger . getRootLogger ( ) ; // .getLogger(CommandLineProgram.class);
/ * *
* the default log level
* /
2009-05-06 07:57:48 +08:00
@Argument ( fullName = "logging_level" ,
shortName = "l" ,
doc = "Set the minimum level of logging, i.e. setting INFO get's you INFO up to FATAL, setting ERROR gets you ERROR and FATAL level logging. (DEBUG, INFO, WARN, ERROR, FATAL, OFF). " ,
required = false )
2009-05-07 03:38:05 +08:00
protected String logging_level = "ERROR" ;
2009-03-23 05:06:22 +08:00
/ * *
* where to send the output of our logger
* /
2009-05-06 07:57:48 +08:00
@Argument ( fullName = "log_to_file" ,
shortName = "log" ,
doc = "Set the logging location" ,
required = false )
2009-05-07 03:38:05 +08:00
protected String toFile = null ;
2009-03-23 05:06:22 +08:00
/ * *
* do we want to silence the command line output
* /
2009-05-06 07:57:48 +08:00
@Argument ( fullName = "quiet_output_mode" ,
shortName = "quiet" ,
doc = "Set the logging to quiet mode, no output to stdout" ,
required = false )
2009-05-07 03:38:05 +08:00
protected Boolean quietMode = false ;
2009-03-23 05:06:22 +08:00
/ * *
* do we want to generate debugging information with the logs
* /
2009-05-06 07:57:48 +08:00
@Argument ( fullName = "debug_mode" ,
shortName = "debug" ,
doc = "Set the logging file string to include a lot of debugging information (SLOW!)" ,
required = false )
2009-05-07 03:38:05 +08:00
protected Boolean debugMode = false ;
2009-03-23 05:06:22 +08:00
2009-05-08 03:08:19 +08:00
/ * *
* this is used to indicate if they ' ve asked for help
* /
@Argument ( fullName = "help" , shortName = "h" , doc = "Generate this help message" , required = false )
public Boolean help = false ;
2009-03-23 05:06:22 +08:00
/ * *
* our logging output patterns
* /
private static String patternString = "%p %m %n" ;
private static String debugPatternString = "%n[level] %p%n[date]\t\t %d{dd MMM yyyy HH:mm:ss,SSS} %n[class]\t\t %C %n[location]\t %l %n[line number]\t %L %n[message]\t %m %n" ;
2009-05-08 03:08:19 +08:00
/ * *
* Retrieves text from the application regarding what parameters to put on the
* JVM command line to run this application .
* @return helpful instructions on the class / jar to run .
* /
protected String getRunningInstructions ( ) {
// Default implementation to find a command line that makes sense.
// If the user is running from a jar, return '-jar <jarname>'; otherwise
// return the full class name.
String runningInstructions = null ;
try {
runningInstructions = JVMUtils . getLocationFor ( getClass ( ) ) . getName ( ) ;
}
catch ( IOException ex ) {
throw new StingException ( "Unable to determine running instructions" , ex ) ;
}
if ( runningInstructions . endsWith ( ".jar" ) )
runningInstructions = String . format ( "-jar %s" , runningInstructions ) ;
else
runningInstructions = getClass ( ) . getName ( ) ;
return runningInstructions ;
}
2009-03-25 08:12:00 +08:00
/ * *
* Will this application want to vary its argument list dynamically ?
* If so , parse the command - line options and then prompt the subclass to return
* a list of argument providers .
* @return Whether the application should vary command - line arguments dynamically .
* /
protected boolean canAddArgumentsDynamically ( ) { return false ; }
/ * *
* Provide a list of object to inspect , looking for additional command - line arguments .
* @return A list of objects to inspect .
* /
2009-03-27 04:45:27 +08:00
protected Class [ ] getArgumentSources ( ) { return new Class [ ] { } ; }
/ * *
* Name this argument source . Provides the ( full ) class name as a default .
* @param source The argument source .
* @return a name for the argument source .
* /
protected String getArgumentSourceName ( Class source ) { return source . toString ( ) ; }
2009-03-25 08:12:00 +08:00
2009-03-23 05:06:22 +08:00
/ * *
* this is the function that the inheriting class can expect to have called
* when all the argument processing is done
*
* @return the return code to exit the program with
* /
protected abstract int execute ( ) ;
/ * *
* This function is called to start processing the command line , and kick
* off the execute message of the program .
*
* @param clp the command line program to execute
* @param args the command line arguments passed in
* /
public static void start ( CommandLineProgram clp , String [ ] args ) {
try {
// setup a basic log configuration
BasicConfigurator . configure ( ) ;
// setup our log layout
PatternLayout layout = new PatternLayout ( ) ;
// setup the parser
2009-05-07 02:16:11 +08:00
ParsingEngine parser = clp . parser = new ParsingEngine ( ) ;
2009-05-07 03:38:05 +08:00
parser . addArgumentSource ( clp . getClass ( ) ) ;
2009-03-23 05:06:22 +08:00
// process the args
2009-03-25 08:12:00 +08:00
if ( clp . canAddArgumentsDynamically ( ) ) {
// if the command-line program can toss in extra args, fetch them and reparse the arguments.
2009-05-07 03:38:05 +08:00
parser . parse ( args ) ;
2009-05-18 08:42:00 +08:00
// Allow invalid and missing required arguments to pass this validation step.
// - InvalidArgument in case these arguments are specified by plugins.
// - MissingRequiredArgument in case the user requested help. Handle that later, once we've
// determined the full complement of arguments.
parser . validate ( EnumSet . of ( ParsingEngine . ValidationType . MissingRequiredArgument ,
ParsingEngine . ValidationType . InvalidArgument ) ) ;
2009-05-07 03:38:05 +08:00
parser . loadArgumentsIntoObject ( clp ) ;
2009-03-27 04:45:27 +08:00
Class [ ] argumentSources = clp . getArgumentSources ( ) ;
2009-05-07 03:38:05 +08:00
for ( Class argumentSource : argumentSources )
2009-05-07 05:16:01 +08:00
parser . addArgumentSource ( clp . getArgumentSourceName ( argumentSource ) , argumentSource ) ;
2009-05-07 03:38:05 +08:00
parser . parse ( args ) ;
2009-05-18 08:42:00 +08:00
if ( isHelpPresent ( clp , parser ) )
printHelpAndExit ( clp , parser ) ;
2009-05-07 03:38:05 +08:00
parser . validate ( ) ;
2009-03-25 08:12:00 +08:00
}
else {
2009-05-07 03:38:05 +08:00
parser . parse ( args ) ;
2009-05-18 08:42:00 +08:00
if ( isHelpPresent ( clp , parser ) )
printHelpAndExit ( clp , parser ) ;
2009-05-07 03:38:05 +08:00
parser . validate ( ) ;
2009-05-07 21:37:19 +08:00
parser . loadArgumentsIntoObject ( clp ) ;
2009-03-25 08:12:00 +08:00
}
2009-03-23 05:06:22 +08:00
// if we're in debug mode, set the mode up
if ( clp . debugMode ) {
//logger.info("Setting debug");
layout . setConversionPattern ( debugPatternString ) ;
} else {
//logger.info("not Setting debug");
layout . setConversionPattern ( patternString ) ;
}
// if they set the mode to quiet
if ( clp . quietMode ) {
// the only appender we should have is stdout, the following meathod is
// deprecated, but the standard remove all appenders doesn't seem to work
// TODO: find the right function
//Category root = Category.getRoot();
//root.removeAllAppenders();
//logger.removeAllAppenders();
}
// if they specify a log location, output our data there
if ( clp . toFile ! = null ) {
FileAppender appender = null ;
try {
appender = new FileAppender ( layout , clp . toFile , false ) ;
logger . addAppender ( appender ) ;
} catch ( IOException e ) {
throw new RuntimeException ( "Unable to re-route log output to " + clp . toFile + " make sure the destination exists" ) ;
}
}
// regardless of what happens next, generate the header information
generateHeaderInformation ( clp , args ) ;
// set the default logger level
clp . setupLoggerLevel ( ) ;
// call the execute
int result = clp . execute ( ) ;
// return the result
System . exit ( result ) ;
}
2009-05-07 02:37:51 +08:00
catch ( ParseException e ) {
2009-05-08 03:08:19 +08:00
clp . parser . printHelp ( clp . getRunningInstructions ( ) ) ;
2009-05-08 03:31:32 +08:00
// Rethrow the exception to exit with an error.
throw e ;
2009-03-25 08:12:00 +08:00
}
2009-03-23 05:06:22 +08:00
catch ( Exception e ) {
// we catch all exceptions here. if it makes it to this level, we're in trouble. Let's bail!
// TODO: what if the logger is the exception? hmm...
logger . fatal ( "Exception caught by base Command Line Program, with message: " + e . getMessage ( ) ) ;
logger . fatal ( "with cause: " + e . getCause ( ) ) ;
e . printStackTrace ( ) ;
2009-03-25 08:12:00 +08:00
throw new RuntimeException ( e ) ;
2009-03-23 05:06:22 +08:00
}
}
2009-03-27 04:45:27 +08:00
/ * *
* Find fields in the object obj that look like command - line arguments , and put command - line
* arguments into them .
* @param obj Object to inspect for command line arguments .
* /
public void loadArgumentsIntoObject ( Object obj ) {
2009-05-07 03:38:05 +08:00
parser . loadArgumentsIntoObject ( obj ) ;
2009-03-27 04:45:27 +08:00
}
2009-05-11 10:07:20 +08:00
/ * *
* a manual way to load argument providing objects into the program
* @param clp the command line program
* @param cls the class to load the arguments off of
* /
public void loadAdditionalSource ( CommandLineProgram clp , Class cls ) {
parser . addArgumentSource ( clp . getArgumentSourceName ( cls ) , cls ) ;
}
2009-03-23 05:06:22 +08:00
/ * *
* generateHeaderInformation
* < p / >
*
* Generate a standard header for the logger
* @param clp the command line program to execute
* @param args the command line arguments passed in
*
* * /
protected static void generateHeaderInformation ( CommandLineProgram clp , String [ ] args ) {
DateFormat dateFormat = new SimpleDateFormat ( "yyyy/MM/dd HH:mm:ss" ) ;
java . util . Date date = new java . util . Date ( ) ;
logger . info ( "-------------------------------------------------------" ) ;
logger . info ( "Program Name: " + clp . getClass ( ) . getName ( ) ) ;
String output = "" ;
for ( String str : args ) {
output = output + str + " " ;
}
logger . info ( "Program Args: " + output ) ;
logger . info ( "Time/Date: " + dateFormat . format ( date ) ) ;
logger . info ( "-------------------------------------------------------" ) ;
}
/ * *
* this function checks the logger level passed in on the command line , taking the lowest
* level that was provided .
* /
2009-05-07 02:37:51 +08:00
private void setupLoggerLevel ( ) throws ParseException {
2009-03-23 05:06:22 +08:00
Level par = Level . ERROR ;
if ( logging_level . equals ( "DEBUG" ) ) {
par = Level . DEBUG ;
}
2009-03-27 12:29:27 +08:00
else if ( logging_level . equals ( "ERROR" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . ERROR ;
}
2009-03-27 12:29:27 +08:00
else if ( logging_level . equals ( "FATAL" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . FATAL ;
}
2009-03-27 12:29:27 +08:00
else if ( logging_level . equals ( "INFO" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . INFO ;
}
2009-03-27 12:29:27 +08:00
else if ( logging_level . equals ( "WARN" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . WARN ;
}
2009-03-27 12:29:27 +08:00
else if ( logging_level . equals ( "OFF" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . OFF ;
}
2009-03-27 12:29:27 +08:00
else {
// we don't understand the logging level, let's get out of here
2009-05-07 02:37:51 +08:00
throw new ParseException ( "Unable to match: " + logging_level + " to a logging level, make sure it's a valid level (INFO, DEBUG, ERROR, FATAL, OFF)" ) ;
2009-03-27 12:29:27 +08:00
}
2009-03-23 05:06:22 +08:00
logger . setLevel ( par ) ;
}
2009-05-11 10:07:20 +08:00
/ * *
* a function used to indicate an error occured in the command line tool
*
* @param msg
* /
private static void printExitSystemMsg ( final String msg ) {
System . out . printf ( "------------------------------------------------------------------------------------------%n" ) ;
System . out . printf ( "An error has occurred%n" ) ;
System . out . printf ( "Check your command line arguments for any typos or inconsistencies.%n" ) ;
System . out . printf ( "If you think it's because of a bug or a feature in GATK that should work, please report this to gsadevelopers@broad.mit.edu%n" ) ;
System . out . printf ( "%n" ) ;
System . out . printf ( "%s%n" , msg ) ;
}
2009-05-18 08:42:00 +08:00
/ * *
* Do a cursory search for the given argument .
* @param clp Instance of the command - line program .
* @param parser Parser
* @return True if help is present ; false otherwise .
* /
private static boolean isHelpPresent ( CommandLineProgram clp , ParsingEngine parser ) {
return parser . isArgumentPresent ( "help" ) ;
}
/ * *
* Print help and exit .
* @param clp Instance of the command - line program .
* @param parser True if help is present ; false otherwise .
* /
private static void printHelpAndExit ( CommandLineProgram clp , ParsingEngine parser ) {
parser . printHelp ( clp . getRunningInstructions ( ) ) ;
System . exit ( 0 ) ;
}
2009-05-11 10:07:20 +08:00
/ * *
* used to indicate an error occured
* @param msg the message to display
* /
public static void exitSystemWithError ( final String msg ) {
printExitSystemMsg ( msg ) ;
System . exit ( 1 ) ;
}
/ * *
* used to indicate an error occured
* @param msg the message
* @param e the error
* /
public static void exitSystemWithError ( final String msg , Exception e ) {
e . printStackTrace ( ) ;
printExitSystemMsg ( msg ) ;
System . exit ( 1 ) ;
}
/ * *
* used to indicate an error occured
* @param e the exception occured
* /
public static void exitSystemWithError ( Exception e ) {
exitSystemWithError ( e . getMessage ( ) , e ) ;
}
2009-03-23 05:06:22 +08:00
}