2009-03-23 05:06:22 +08:00
package org.broadinstitute.sting.utils.cmdLine ;
import org.apache.log4j.* ;
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-06-26 03:47:29 +08:00
import java.util.Enumeration ;
2009-08-23 08:56:02 +08:00
import java.util.Collections ;
import java.util.Collection ;
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
* /
2009-08-21 22:40:57 +08:00
private static Logger logger = Logger . getRootLogger ( ) ;
2009-03-23 05:06:22 +08:00
/ * *
* 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-09-01 12:21:58 +08:00
protected String logging_level = "WARN" ;
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 )
2009-09-05 03:13:37 +08:00
public Boolean help = false ;
2009-05-08 03:08:19 +08:00
2009-03-23 05:06:22 +08:00
/ * *
* our logging output patterns
* /
2009-06-26 03:47:29 +08:00
private static String patternString = "%-5p %d{HH:mm:ss,SSS} %C{1} - %m %n" ;
2009-03-23 05:06:22 +08:00
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
/ * *
2009-07-22 06:23:28 +08:00
* Allows a given application to return a brief description of itself .
* @return An ApplicationDetails object describing the current application . Should not be null .
2009-05-08 03:08:19 +08:00
* /
2009-07-22 06:23:28 +08:00
protected ApplicationDetails getApplicationDetails ( ) {
return new ApplicationDetails ( ApplicationDetails . createDefaultHeader ( getClass ( ) ) ,
ApplicationDetails . createDefaultRunningInstructions ( getClass ( ) ) ) ;
2009-07-14 05:56:41 +08:00
}
2009-08-23 08:56:02 +08:00
/ * *
* Subclasses of CommandLinePrograms can provide their own types of command - line arguments .
* @return A collection of type descriptors generating implementation - dependent placeholders .
* /
protected Collection < ArgumentTypeDescriptor > getArgumentTypeDescriptors ( ) {
return Collections . emptyList ( ) ;
}
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 ( ) ;
2009-07-10 07:59:53 +08:00
static {
// setup a basic log configuration
BasicConfigurator . configure ( ) ;
}
2009-09-05 03:13:37 +08:00
public static int result = 0 ;
2009-03-23 05:06:22 +08:00
/ * *
* 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 our log layout
PatternLayout layout = new PatternLayout ( ) ;
// setup the parser
2009-07-30 00:11:45 +08:00
ParsingEngine parser = clp . parser = new ParsingEngine ( clp ) ;
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 ) ;
}
2009-06-26 03:47:29 +08:00
// now set the layout of all the loggers to our layout
Enumeration < Appender > en = logger . getAllAppenders ( ) ;
for ( ; en . hasMoreElements ( ) ; ) {
Appender app = en . nextElement ( ) ;
app . setLayout ( layout ) ;
}
2009-03-23 05:06:22 +08:00
// 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
2009-09-05 03:13:37 +08:00
CommandLineProgram . result = clp . execute ( ) ;
2009-03-23 05:06:22 +08:00
// return the result
2009-09-05 03:13:37 +08:00
//System.exit(result); // todo -- is this safe -- why exit here? I want to run the GATK like normal
2009-03-23 05:06:22 +08:00
}
2009-05-20 07:26:17 +08:00
catch ( ArgumentException e ) {
2009-07-22 06:23:28 +08:00
clp . parser . printHelp ( clp . getApplicationDetails ( ) ) ;
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 ( "-------------------------------------------------------" ) ;
2009-07-22 06:23:28 +08:00
for ( String headerLine : clp . getApplicationDetails ( ) . applicationHeader )
2009-07-14 05:56:41 +08:00
logger . info ( headerLine ) ;
2009-03-23 05:06:22 +08:00
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-20 07:26:17 +08:00
private void setupLoggerLevel ( ) {
2009-08-21 22:40:57 +08:00
Level par = Level . WARN ;
2009-09-01 12:21:58 +08:00
if ( logging_level . toUpperCase ( ) . equals ( "DEBUG" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . DEBUG ;
}
2009-09-01 12:21:58 +08:00
else if ( logging_level . toUpperCase ( ) . equals ( "ERROR" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . ERROR ;
}
2009-09-01 12:21:58 +08:00
else if ( logging_level . toUpperCase ( ) . equals ( "FATAL" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . FATAL ;
}
2009-09-01 12:21:58 +08:00
else if ( logging_level . toUpperCase ( ) . equals ( "INFO" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . INFO ;
}
2009-09-01 12:21:58 +08:00
else if ( logging_level . toUpperCase ( ) . equals ( "WARN" ) ) {
2009-03-23 05:06:22 +08:00
par = Level . WARN ;
}
2009-09-01 12:21:58 +08:00
else if ( logging_level . toUpperCase ( ) . 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-20 07:26:17 +08:00
throw new ArgumentException ( "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" ) ;
2009-07-14 05:56:41 +08:00
System . out . printf ( "An error has occurred. Please check your command line arguments for any typos or inconsistencies.%n%n" ) ;
System . out . printf ( "For assistance, please email us at gsadevelopers@broad.mit.edu, or review our documentation at http://www.broadinstitute.org/gsa/wiki.%n" ) ;
2009-05-11 10:07:20 +08:00
}
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 ) {
2009-07-22 06:23:28 +08:00
parser . printHelp ( clp . getApplicationDetails ( ) ) ;
2009-05-18 08:42:00 +08:00
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
}