package org.squirrelframework.foundation.fsm.impl;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.squirrelframework.foundation.fsm.Action;
import org.squirrelframework.foundation.fsm.StateMachine;
import org.squirrelframework.foundation.fsm.annotation.AsyncExecute;
import org.squirrelframework.foundation.fsm.annotation.ExecuteWhen;
import org.squirrelframework.foundation.fsm.annotation.LogExecTime;
import org.squirrelframework.foundation.util.ReflectUtils;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
public class MethodCallActionImpl<T extends StateMachine<T, S, E, C>, S, E, C> implements Action<T, S, E, C> {
final static Logger logger = LoggerFactory.getLogger(MethodCallActionImpl.class);
private final Method method;
private boolean logExecTime;
private final String executeWhenExpr;
private final String methodDesc;
private final ExecutionContext executionContext;
private final int weight;
private final boolean isAsync;
private final long timeout;
MethodCallActionImpl(Method method, int weight, ExecutionContext executionContext) {
Preconditions.checkNotNull(method, "Method of the action cannot be null.");
this.method = method;
this.weight = weight;
this.executionContext = executionContext;
AsyncExecute asyncAnnotation = method.getAnnotation(AsyncExecute.class);
this.isAsync = asyncAnnotation!=null;
this.timeout = asyncAnnotation!=null ? asyncAnnotation.timeout() : -1;
logExecTime = ReflectUtils.isAnnotatedWith(method, LogExecTime.class);
if(!logExecTime) {
logExecTime = method.getDeclaringClass().getAnnotation(LogExecTime.class) != null;
}
ExecuteWhen executeWhen = method.getAnnotation(ExecuteWhen.class);
if(executeWhen!=null) {
executeWhenExpr = executeWhen.value();
executionContext.getScriptManager().compile(executeWhenExpr);
} else {
executeWhenExpr = null;
}
methodDesc = ReflectUtils.logMethod(method);
}
@Override
public void execute(final S from, final S to,
final E event, final C context, final T stateMachine) {
invokeMethod(from, to, event, context, stateMachine);
}
private void invokeMethod(S from, S to, E event, C context, T stateMachine) {
if(executeWhenExpr!=null) {
Map<String, Object> variables = new HashMap<String, Object>();
// variables.put("from", from);
// variables.put("to", to);
// variables.put("event", event);
variables.put("context", context);
// variables.put("stateMachine", stateMachine);
boolean isAllowed = executionContext.getScriptManager().evalBoolean(executeWhenExpr, variables);
if(!isAllowed) return;
}
Object[] paramValues = Lists.newArrayList(from, to, event, context).
subList(0, executionContext.getMethodCallParamTypes().length).toArray();
if(logExecTime && logger.isDebugEnabled()) {
Stopwatch sw = Stopwatch.createStarted();
ReflectUtils.invoke(method, stateMachine, paramValues);
logger.debug("Execute Method \""+methodDesc+"\" tooks "+sw+".");
} else {
ReflectUtils.invoke(method, stateMachine, paramValues);
}
}
@Override
public String name() {
return method.getName();
}
@Override
public int hashCode() {
return method.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if(obj == null)
return false;
if(obj instanceof MethodCallActionProxyImpl && obj.equals(this))
return true;
if (getClass() != obj.getClass() ||
!method.equals(MethodCallActionImpl.class.cast(obj).method))
return false;
return true;
}
@Override
public int weight() {
return weight;
}
@Override
final public String toString() {
return "method#"+method.getName()+":"+weight;
}
@Override
public boolean isAsync() {
return isAsync;
}
@Override
public long timeout() {
return timeout;
}
}