package com.lambdaworks.redis.commands.transactional;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.api.sync.RedisCommands;
import com.lambdaworks.redis.internal.AbstractInvocationHandler;
/**
* Invocation handler for testing purposes that wraps each call into a transaction.
*
* @param <K>
* @param <V>
*/
public class TxSyncInvocationHandler<K, V> extends AbstractInvocationHandler {
private final Object api;
private final Method multi;
private final Method discard;
private final Method exec;
private final Method ping;
public TxSyncInvocationHandler(Object api) throws Exception {
this.api = api;
this.multi = api.getClass().getMethod("multi");
this.exec = api.getClass().getMethod("exec");
this.discard = api.getClass().getMethod("discard");
this.ping = api.getClass().getMethod("ping");
}
@Override
@SuppressWarnings("unchecked")
protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (method.getName().equals("exec") || method.getName().equals("multi")) {
throw new IllegalStateException("Cannot execute transaction commands over this transactional wrapper");
}
Method targetMethod = api.getClass().getMethod(method.getName(), method.getParameterTypes());
if (!method.getName().equals("close")) {
multi.invoke(api);
ping.invoke(api);
targetMethod.invoke(api, args);
Object result = exec.invoke(api);
if (result == null || !(result instanceof List<?>)) {
return result;
}
List<?> list = (List<?>) result;
if (list.size() > 1) {
result = list.get(1);
if (result instanceof Exception) {
throw (Exception) result;
}
return result;
}
return null;
}
return targetMethod.invoke(api, args);
} catch (InvocationTargetException e) {
try {
discard.invoke(api);
} catch (Exception e1) {
}
throw e.getTargetException();
}
}
/**
* Create a transactional wrapper proxy for {@link RedisCommands}.
*
* @param connection the connection
* @return the wrapper proxy.
*/
@SuppressWarnings("unchecked")
public static <K, V> RedisCommands<K, V> sync(StatefulRedisConnection<K, V> connection) {
try {
TxSyncInvocationHandler<K, V> handler = new TxSyncInvocationHandler<>(connection.sync());
return (RedisCommands<K, V>) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
new Class<?>[] { RedisCommands.class }, handler);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}