/*
* This file or a portion of this file is licensed under the terms of
* the Globus Toolkit Public License, found in file GTPL, or at
* http://www.globus.org/toolkit/download/license.html. This notice must
* appear in redistributions of this file, with or without modification.
*
* Redistributions of this Software, with or without modification, must
* reproduce the GTPL in: (1) the Software, or (2) the Documentation or
* some other similar material which is provided with the Software (if
* any).
*
* Copyright 1999-2004 University of Chicago and The University of
* Southern California. All rights reserved.
*/
package edu.isi.pegasus.planner.client;
import org.griphyn.vdl.toolkit.*;
import java.io.*;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import edu.isi.pegasus.common.util.Version;
import edu.isi.pegasus.common.util.Currently;
import edu.isi.pegasus.planner.parser.InvocationParser;
import org.griphyn.vdl.dbschema.*;
import org.griphyn.vdl.util.Logging;
import org.griphyn.vdl.util.ChimeraProperties;
import org.griphyn.vdl.directive.*;
import gnu.getopt.*;
/**
* This class gets the exit code of a job from invocation record.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*
* @see org.griphyn.vdl.parser.InvocationParser
* @see org.griphyn.vdl.dbschema.DatabaseSchema
*/
public class ExitCode extends Toolkit
{
/**
* Just a string to denote the short usage.
*/
public static final String m_usage1 =
"[-d dbprefix | -n | -N] [-e] [-f] [-i] [-v] [-l tag -m ISO] file [..]";
/**
* ctor: Constructs a new instance object with the given application name.
*/
public ExitCode( String appName )
{
super(appName);
}
/**
* Implements printing the usage string onto stdout.
*/
public void showUsage()
{
String linefeed = System.getProperty( "line.separator", "\r\n" );
System.out.println(
"$Id$" + linefeed +
"VDS version " + Version.instance().toString() + linefeed );
System.out.println( "Usage: " + this.m_application + ' ' + m_usage1 );
System.out.println( linefeed +
"Generic options: " + linefeed +
" -d|--dbase dbx associates the dbname with the database, unused." + linefeed +
" -V|--version print version information and exit." + linefeed +
" -v|--verbose verbose mode, switches on property vds.log.app on stdout." + linefeed +
" -i|--ignore ignore exit code for failure, just add to database." + linefeed +
" -n|--noadd extract the exit code, don\'t add to any database." + linefeed +
" -N|--nofail extract and add, but do not fail on db errors." + linefeed +
" -e|--emptyfail takes empty files to mean failure instead of ok." + linefeed +
" -f|--fail stops parsing on the first error found instead of going on." + linefeed +
" -l|--label tag attaches the tag string (32 char max) to all records." + linefeed +
" -m|--mtime ISO uses the ISO 8601 timestamp as WF mtime for all records." + linefeed +
" Options -l and -m must be used in conjunction!" + linefeed +
linefeed +
"The following exit codes are returned (except in -i mode):" + linefeed +
" 0 remote application ran to conclusion with exit code zero." + linefeed +
" 1 remote application concluded with a non-zero exit code." + linefeed +
" 2 kickstart failed to start the remote application." + linefeed +
" 3 remote application died on a signal, check database." + linefeed +
" 4 remote application was suspended, should not happen." + linefeed +
" 5 invocation record has an invalid state, unable to parse." + linefeed +
" 6 illegal state, inform vds-support@griphyn.org." + linefeed +
" 7 illegal state, stumbled over an exception, try --verbose for details" + linefeed +
" 8 multiple 0..5 failures during parsing of multiple records" + linefeed );
}
/**
* Creates a set of options.
*/
protected LongOpt[] generateValidOptions()
{
LongOpt[] lo = new LongOpt[11];
lo[0] = new LongOpt( "help", LongOpt.NO_ARGUMENT, null, 'h' );
lo[1] = new LongOpt( "dbase", LongOpt.REQUIRED_ARGUMENT, null, 'd' );
lo[2] = new LongOpt( "version", LongOpt.NO_ARGUMENT, null, 'V' );
lo[3] = new LongOpt( "verbose", LongOpt.NO_ARGUMENT, null, 'v' );
lo[4] = new LongOpt( "ignore", LongOpt.NO_ARGUMENT, null, 'i' );
lo[5] = new LongOpt( "noadd", LongOpt.NO_ARGUMENT, null, 'n' );
lo[6] = new LongOpt( "nofail", LongOpt.NO_ARGUMENT, null, 'N' );
lo[7] = new LongOpt( "emptyfail", LongOpt.NO_ARGUMENT, null, 'e' );
lo[8] = new LongOpt( "fail", LongOpt.NO_ARGUMENT, null, 'f' );
lo[9] = new LongOpt( "label", LongOpt.REQUIRED_ARGUMENT, null, 'l' );
lo[10] = new LongOpt( "mtime", LongOpt.REQUIRED_ARGUMENT, null, 'm' );
return lo;
}
public void showThreads()
{
ThreadGroup tg = Thread.currentThread().getThreadGroup();
while ( tg.getParent() != null ) tg = tg.getParent();
tg.list();
}
public static void main(String[] args)
{
int result = 0;
int verbose = 0;
ExitCode me = null;
boolean noDBase = false;
boolean ignoreDBFail = false;
boolean failOver = true;
boolean emptyFail = false;
boolean earlyFail = false;
ParseKickstart pks = null;
String wf_label = null;
Date wf_mtime = null;
int wf_flag = 0;
try {
me = new ExitCode("exitcode");
if ( args.length == 0 ) {
me.showUsage();
return ;
}
// get the commandline options
Getopt opts = new Getopt( me.m_application, args,
"d:hefil:m:nNvV",
me.generateValidOptions() );
opts.setOpterr(false);
int option = 0;
while ( (option = opts.getopt()) != -1 ) {
switch ( option ) {
case 'V':
System.out.println( "$Id$" );
System.out.println( "VDS version " + Version.instance().toString() );
return;
case 'd':
// currently inactive option
opts.getOptarg();
break;
case 'e':
emptyFail = true;
break;
case 'f':
earlyFail = true;
break;
case 'i':
failOver = false;
break;
case 'l':
if ( (wf_label = opts.getOptarg()) != null ) {
if ( wf_label.length() > 0 ) wf_flag |= 1;
else wf_label = null;
}
break;
case 'm':
if ( (wf_mtime = Currently.parse( opts.getOptarg() )) != null )
wf_flag |= 2;
break;
case 'n':
noDBase = true;
break;
case 'N':
ignoreDBFail = true;
break;
case 'v':
verbose = me.increaseVerbosity();
break;
case '?':
System.out.println( "Invalid option '" + (char) opts.getOptopt() + "'" );
default:
case 'h':
me.showUsage();
return;
}
}
// print usage information
String arg0 = null;
if ( opts.getOptind() >= args.length ) {
System.out.println( "missing necessary file argument" );
me.showUsage();
return ;
}
// check for -m and -l
if ( wf_flag != 0 && wf_flag != 3 ) {
me.m_logger.log( "default", 0,
"Warning: Options -m and -l should be used together!" );
}
if ( wf_label != null && wf_label.length() > 32 ) {
wf_label = wf_label.substring( 0, 32 );
me.m_logger.log( "default", 0, "Warning: Truncating workflow label to \"" +
wf_label + "\"" );
}
ChimeraProperties props = ChimeraProperties.instance();
DatabaseSchema dbschema = null;
String ptcSchemaName = props.getPTCSchemaName();
if ( ptcSchemaName == null ) noDBase = true;
if ( ! noDBase ) {
try {
Connect connect = new Connect();
dbschema = connect.connectDatabase(ptcSchemaName);
} catch ( Exception e ) {
if ( ignoreDBFail ) {
// if dbase errors are not fatal, just record the fact
String cls = e.getClass().getName();
String msg = e.getMessage();
if ( msg == null ) {
Throwable t = e.getCause();
if ( t != null ) {
cls = t.getClass().getName();
msg = t.getMessage();
}
}
me.m_logger.log( "default", 0, "While connecting to dbase: " +
cls + ": " + msg + ", ignoring" );
dbschema = null;
} else {
// re-throw, if dbase errors are fatal (default)
throw e;
}
}
// check for invocation record support
if ( dbschema == null || ! (dbschema instanceof PTC) ) {
me.m_logger.log( "default", 0,
"Your database cannot store invocation records" +
", assuming -n mode" );
noDBase = true;
}
}
// instantiate parser
pks = new ParseKickstart( dbschema, emptyFail );
pks.setNoDBase( noDBase );
pks.setIgnoreDBFail( ignoreDBFail );
pks.setWorkflowLabel( wf_label ); // null ok
pks.setWorkflowTimestamp( wf_mtime ); // null ok
dbschema = null; // decrease reference counter
// for all files specified
for ( int i=opts.getOptind(); i < args.length; ++i ) {
List l = pks.parseFile( args[i] );
// determine result code
if ( failOver ) {
for ( Iterator j=l.iterator(); j.hasNext(); ) {
int status = ((Integer) j.next()).intValue();
me.m_logger.log( "app", 1, "exit status = " + status );
if ( status != 0 ) result = ( result==0 ? status : 8 );
}
}
if ( result != 0 && earlyFail ) break;
} // for
} catch ( FriendlyNudge fn ) {
me.m_logger.log( "default", 0, fn.getMessage() );
if ( failOver ) result = fn.getResult();
} catch ( Exception e ) {
String cls = e.getClass().getName();
String msg = e.getMessage();
if ( msg == null ) {
// another try
Throwable t = e.getCause();
if ( t != null ) {
msg = t.getMessage();
cls = t.getClass().getName();
}
}
if ( verbose > 0 ) e.printStackTrace();
System.err.println(cls + ": " + msg);
result = 7;
} finally {
try {
if ( pks != null ) pks.close();
} catch ( Exception e ) {
me.m_logger.log( "default", 0, "ERROR: " + e.getMessage() );
}
}
// Java will return with 0 unless exit is used. Unfortunately, using
// System.exit sometimes has some unwanted side-effects on d'tors,
// thus avoid using it unless strictly necessary.
// me.showThreads();
if ( result != 0 ) System.exit(result);
}
}