cn.org.atool.fluentmachine.persistence.ContextTestSaver Maven / Gradle / Ivy
package cn.org.atool.fluentmachine.persistence;
import cn.org.atool.fluentmachine.context.Context;
import cn.org.atool.fluentmachine.context.FireContext;
import cn.org.atool.fluentmachine.exception.LockException;
import cn.org.atool.fluentmachine.saver.ContextSaver;
import cn.org.atool.fluentmachine.state.IName;
import lombok.Getter;
import lombok.Setter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 上下文保存测试
*
* @author darui.wu
*/
public class ContextTestSaver implements ContextSaver {
@Getter
@Setter
private long lockDuration = 100L;
private volatile Context context = null;
/**
* 状态机运行路径
*/
@Getter
private final List paths = new ArrayList<>();
@Override
public boolean isExistContext(String machineId, String tradeNo) {
return context != null;
}
private String lockBy = null;
public void cleanup() {
context = null;
}
@Override
public void saveContext(Context ctx, FireContext fire, boolean isNew) {
synchronized (getLock(ctx.getMachineId(), ctx.getTradeNo())) {
this.context = ContextHelper.copy(ctx);
this.paths.add(fire.toString());
}
}
@Override
public boolean lock(Context ctx, String lockVersion, Object event) {
synchronized (getLock(ctx.getMachineId(), ctx.getTradeNo())) {
if (this.context == null) {
return true;
}
if (this.canLock(ctx)) {
this.context.setLockVersion(lockVersion);
this.context.setLockExpireSecond(new Date().getTime() + this.getLockDuration());
this.lockBy = IName.name(event) + ":" + ctx.getStates();
return true;
} else {
throw new LockException("The context[machineId=%s, tradeNo=%s] is locked and not expired, lock by:%s",
ctx.getMachineId(), ctx.getTradeNo(), this.lockBy);
}
}
}
@Override
public boolean unlock(Context ctx) {
synchronized (getLock(ctx.getMachineId(), ctx.getTradeNo())) {
if (this.context == null) {
return true;
}
if (canLock(ctx)) {
this.context.setLockVersion(null);
this.context.setLockExpireSecond(0L);
return true;
} else {
return false;
}
}
}
/**
* 未上锁或锁相同
*
* @param context
* @return
*/
private boolean canLock(Context context) {
String oldLock = this.context.getLockVersion();
return this.expiredLock() || Objects.equals(oldLock, context.getLockVersion());
}
/**
* 锁过期
*
* @return
*/
private boolean expiredLock() {
String oldLock = this.context.getLockVersion();
if (oldLock == null || oldLock.trim().isEmpty() || Objects.equals(oldLock, "0")) {
return true;
} else {
return this.context.getLockExpireSecond() <= new Date().getTime();
}
}
@Override
public Context loadContext(String machineId, String tradeNo, boolean ignoreLock, Class klass) {
synchronized (getLock(machineId, tradeNo)) {
if (ignoreLock) {
return ContextHelper.copy(context);
} else if (this.expiredLock()) {
return ContextHelper.copy(context);
} else {
throw new LockException("The lock of context[machineId=%s, tradeNo=%s] not expired.", machineId, tradeNo);
}
}
}
/**
* 返回路径描述
*
* @param delimiter 分隔符
* @return
*/
public String getPaths(String delimiter) {
return this.getPaths().stream().collect(Collectors.joining(delimiter));
}
private static Map locks = new HashMap<>();
private synchronized String getLock(String machineId, String tradeNo) {
String lock = machineId + tradeNo;
if (!locks.containsKey(lock)) {
locks.put(lock, lock);
}
return locks.get(lock);
}
}