package org.kisst.gft.action;
//import org.kisst.gft.LogService;
import org.kisst.gft.RetryableException;
import org.kisst.gft.task.Task;
import org.kisst.props4j.Props;
import org.kisst.util.ThreadUtil;
import org.kisst.util.exception.BasicFunctionalException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jamonapi.Monitor;
import com.jamonapi.MonitorFactory;
public class ActionExecutor {
final static Logger logger=LoggerFactory.getLogger(ActionExecutor.class);
private final int maxNrofTries;
private final long retryDelay;
private final boolean retryNonFunctionalExceptions;
public ActionExecutor(Props props) {
maxNrofTries = props.getInt("maxNrofTries", 3);
retryDelay = props.getLong("retryDelay", 30000);
retryNonFunctionalExceptions = props.getBoolean("retryNonFunctionalExceptions", false);
}
private Object tryToExecute(Action a, Task task) {
boolean done=false;
Transaction trans=null;
if (a instanceof Transaction)
trans=(Transaction) a;
try {
if (trans!=null)
trans.prepareTransaction(task);
a.execute(task);
done=true;
return null;
}
finally {
if (trans!=null) {
if (done)
trans.commitTransaction(task);
else
trans.rollbackTransaction(task);
}
}
}
public Object execute(Action a, String name, Task task) {
if (name==null)
name=a.getClass().getSimpleName();
if (logger.isInfoEnabled())
logger.info("action "+name+" started");
boolean done=false;
int nrofTries=0;
while (! done){
Monitor mon1=MonitorFactory.start("action:"+name);
Monitor mon2=null;
String channelName= task.getTaskDefinition().getName();
mon2=MonitorFactory.start("channel:"+channelName+":action:"+name);
try {
task.setCurrentAction(a);
tryToExecute(a, task);
done=true;
}
catch (RuntimeException e) {
if (e instanceof BasicFunctionalException) {
logger.error("action "+name+" had functional error: "+e.getMessage());
throw e;
}
if (! a.safeToRetry()) {
logger.error("action "+name+" (which is not safe to retry) had error: "+e.getMessage());
throw e;
}
if ( (!retryNonFunctionalExceptions) && ! (e instanceof RetryableException)) {
logger.error("action "+name+" had non-functional error: "+e.getMessage());
throw e;
}
if (nrofTries <= maxNrofTries) {
logger.warn("Error during action "+name+", try number "+nrofTries+", will retry after "+retryDelay/1000+" seconds, error was ", e);
nrofTries++;
ThreadUtil.sleep(retryDelay);
}
else {
logger.error("action "+name+" had "+(nrofTries+1)+" tries, last error: "+e.getMessage());
throw e;
}
}
finally {
mon1.stop();
if (mon2!=null) mon2.stop();
}
if (done && logger.isInfoEnabled())
logger.info("action "+name+" succesful");
}
return null;
}
}