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-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 ;
private ArgumentMatches parameters = 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-03-23 05:06:22 +08:00
public String logging_level = "ERROR" ;
/ * *
* 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-03-23 05:06:22 +08:00
public String toFile = null ;
/ * *
* 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-03-23 05:06:22 +08:00
public Boolean quietMode = false ;
/ * *
* 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-03-23 05:06:22 +08:00
public Boolean debugMode = false ;
/ * *
* 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-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 is used to indicate if they ' ve asked for help
* /
2009-05-06 07:57:48 +08:00
@Argument ( fullName = "help" , shortName = "h" , doc = "Generate this help message" , required = false )
2009-03-23 05:06:22 +08:00
public Boolean help = false ;
/ * *
* 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 ( ) ;
parser . addArgumentSources ( 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 02:16:11 +08:00
clp . parameters = parser . parse ( args ) ;
parser . validate ( clp . parameters , EnumSet . of ( ParsingEngine . ValidationType . InvalidArgument ) ) ;
parser . loadArgumentsIntoObject ( clp , clp . parameters ) ;
2009-03-27 04:45:27 +08:00
Class [ ] argumentSources = clp . getArgumentSources ( ) ;
2009-05-07 02:16:11 +08:00
parser . addArgumentSources ( argumentSources ) ;
clp . parameters = parser . parse ( args ) ;
parser . validate ( clp . parameters ) ;
2009-03-25 08:12:00 +08:00
}
else {
2009-05-07 02:16:11 +08:00
clp . parameters = parser . parse ( args ) ;
parser . validate ( clp . parameters ) ;
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();
}
// they asked for help, give it to them
if ( clp . help ) {
2009-05-07 02:16:11 +08:00
parser . printHelp ( ) ;
2009-03-23 05:06:22 +08:00
System . exit ( 1 ) ;
}
// 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-03-25 08:12:00 +08:00
catch ( org . apache . commons . cli . ParseException e ) {
logger . fatal ( "Unable to pass command line arguments: " + e . getMessage ( ) ) ;
2009-05-07 02:16:11 +08:00
clp . parser . printHelp ( ) ;
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 02:16:11 +08:00
parser . loadArgumentsIntoObject ( obj , parameters ) ;
2009-03-27 04:45:27 +08:00
}
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-03-27 12:29:27 +08:00
private void setupLoggerLevel ( ) throws org . apache . commons . cli . 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
throw new org . apache . commons . cli . ParseException ( "Unable to match: " + logging_level + " to a logging level, make sure it's a valid level (INFO, DEBUG, ERROR, FATAL, OFF)" ) ;
}
2009-03-23 05:06:22 +08:00
logger . setLevel ( par ) ;
}
}