/**
* PlanStep.java
*
* Provide generic class for plan step implementation. Every plan step should extend
* this class.
*
* To implement a plan step, the new class must implement the following functions:
* - main(): provide main entry to run the step. This is also the legacy way of
* running a plan step.
* - printVersion(): print plan step version
* - printUsage(): print help message
* - parseArgument2(): additional parameters specific to the plan step. Generic plan
* parameters are already handled by PlanStep class.
* - init(): what to do in init stage.
* - running(): what to do in running stage.
* - close(): what to do in closing stage.
*
* @author Wahyu Yoga Pratama (yoga@thatcoolguy.com)
*
* @created Nov 12, 2009
* @version $$
*
* HISTORY:
* - 2009/11/12 Created.
*
* TODO:
*
*/
package tcg.plan;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger; // Perform logging operations
import org.apache.log4j.NDC;
import tcg.common.LoggerManager;
public abstract class PlanStep
{
protected static DateFormat DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
protected static int ERROR_THRESHOLD = 3;
protected String exeId = "";
protected String nodeId = "";
protected int masterPortNo = 0;
protected int agentPortNo = 0;
protected Logger logger = LoggerManager.getLogger(PlanStep.class.toString());
public void execute(String params)
{
String args[] = params.split(" ");
this.execute(args);
}
//Parse child specific arguments
protected abstract boolean parseArgument2(String args[]);
//Perform different operation in different stage of running
protected abstract boolean init();
protected abstract boolean running();
protected abstract boolean close();
//Terminate the step. This will be called when PlanMaster wants to
// abruptly terminate the step
protected abstract void terminate();
protected void execute(String args[])
{
//parse the argument
if (!parseArguments(args))
{
return;
}
//add logging context
NDC.push(exeId + ":" + nodeId + ":" + masterPortNo);
//run any initialization
updateExecutionState(StepExecutionState.INIT);
if (!init())
{
updateExecutionState(StepExecutionState.STEPFAIL);
NDC.pop();
return;
}
//run the actual processing
updateExecutionState(StepExecutionState.RUNNING);
if (!running())
{
updateExecutionState(StepExecutionState.STEPFAIL);
NDC.pop();
return;
}
//end execution
updateExecutionState(StepExecutionState.CLOSE);
close();
//remove logging context
NDC.pop();
}
protected void setLogger(Logger logger)
{
this.logger = logger;
}
//I need to do this so that child class does not need to each print it out
// in its printUsage()
protected void printExpectedArguments()
{
System.out.println(" --plan-execution-id <plan-id> Plan Execution ID");
System.out.println(" --executed-node-id <node-id> Executed Node ID");
System.out.println(" --port-no <port-no> Plan Master Port No");
System.out.println(" --agent-port-no <port-no> Plan Agent Port No");
}
private boolean parseArguments(String args[])
{
CommandLineParser cmdLnParser = new BasicParser();
CommandLine cmdLn = null;
Options optArgs = new Options();
Option cmdLineArg1 =
new Option("exeID", "PLAN Execution ID (required)");
cmdLineArg1.setRequired(true);
cmdLineArg1.setArgs(1);
cmdLineArg1.setArgName("plan-execution-id");
Option cmdLineArg2 =
new Option("nodeID", "Executed Node ID (required)");
cmdLineArg2.setRequired(true);
cmdLineArg2.setArgs(1);
cmdLineArg2.setArgName("executed-node-id");
Option cmdLineArg3 =
new Option("portNo", "PLAN Master listener port no (required)");
cmdLineArg3.setRequired(true);
cmdLineArg3.setArgs(1);
cmdLineArg3.setArgName("port-no");
Option cmdLineArg4 =
new Option("agentPortNo", "PLAN Agent Port No (required)");
cmdLineArg4.setRequired(true);
cmdLineArg4.setArgs(1);
cmdLineArg4.setArgName("agent-port-no");
optArgs.addOption(cmdLineArg1);
optArgs.addOption(cmdLineArg2);
optArgs.addOption(cmdLineArg3);
optArgs.addOption(cmdLineArg4);
//parse the arguments
try
{
cmdLn = cmdLnParser.parse(optArgs, args);
}
catch (ParseException pe)
{
logger.error("Can not parse arguments: " + pe.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "Parameters:", optArgs );
return false;
}
//get the valud
exeId = cmdLn.getOptionValue("exeID");
nodeId = cmdLn.getOptionValue("nodeID");
try
{
masterPortNo = Integer.parseInt(cmdLn.getOptionValue("portNo"));
}
catch (NumberFormatException ne)
{
logger.error("Invalid plan master port no: " + cmdLn.getOptionValue("portNo"));
return false;
}
try
{
agentPortNo = Integer.parseInt(cmdLn.getOptionValue("agent-port-no"));
}
catch (NumberFormatException ne)
{
logger.error("Invalid plan agent port no: "
+ cmdLn.getOptionValue("agent-port-no"));
return false;
}
//pass to child subroutines
return parseArgument2(args);
}
private boolean updateExecutionState(StepExecutionState state)
{
String msg = "";
//build the message
if (state == StepExecutionState.INIT)
{
msg = DATE_FORMAT.format(new java.util.Date()) + ",INIT," + nodeId;
}
else if (state == StepExecutionState.RUNNING)
{
msg = DATE_FORMAT.format(new java.util.Date()) + ",RUNNING," + nodeId;
}
else if (state == StepExecutionState.CLOSE)
{
msg = DATE_FORMAT.format(new java.util.Date()) + ",CLOSE," + nodeId;
}
else
{
msg = DATE_FORMAT.format(new java.util.Date()) + ",STEPFAIL," + nodeId;
}
//plan master is always running locally
InetAddress masterHost = null;
try
{
masterHost = InetAddress.getLocalHost();
}
catch(Exception ex)
{
//this is very unlikely to happen!
logger.error("Can not get localhost address: " + ex.getLocalizedMessage());
return false;
}
//TODO: avoid opening the socket everytime. open once and reuse it if possible.
boolean status = false;
for (int i=0; i<ERROR_THRESHOLD && !status; i++)
{
try
{
// Create the socket
Socket socket = new Socket(masterHost, masterPortNo);
// Send a String
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
out.write(msg);
out.flush();
// Close socket
socket.close();
// Successful
status = true;
}
catch (Exception ex)
{
logger.warn("Can not notify plan master: " + ex.getLocalizedMessage());
}
}
return status;
}
protected enum StepExecutionState
{
INIT,
RUNNING,
CLOSE,
STEPFAIL;
}
}