Deadlock Retry with JDK Dynamic Proxy
Here are the key steps in writing a dead lock retry service with JDK Dynamic Proxy.
Step 1: Define the custom deadlock retry annotation.
package com.myapp.deadlock;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface DeadlockRetry
{
int maxTries() default 10;
int tryIntervalMillis() default 1000;
}
Step 2: Define the JDK dynamic proxy class.
package com.myapp.deadlock;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DeadlockRetryHandler implements InvocationHandler
{
private static final Logger LOG = LoggerFactory.getLogger(DeadlockRetryHandler.class);
private Object target;
public DeadlockRetryHandler(Object target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Annotation[] annotations = method.getAnnotations();
DeadlockRetry deadlockRetry = (DeadlockRetry) annotations[0];
final Integer maxTries = deadlockRetry.maxTries();
long tryIntervalMillis = deadlockRetry.tryIntervalMillis();
int count = 0;
do
{
try
{
count++;
LOG.info("Attempting to invoke method " + method.getName() + " on " + target.getClass() + " count "
+ count);
Object result = method.invoke(target, args); // retry
LOG.info("Completed invocation of method " + method.getName() + " on " + target.getClass());
return result;
}
catch (Throwable e)
{
if (!DeadlockUtil.isDeadLock(e))
{
throw new RuntimeException(e);
}
if (tryIntervalMillis > 0)
{
try
{
Thread.sleep(tryIntervalMillis);
}
catch (InterruptedException ie)
{
LOG.warn("Deadlock retry thread interrupted", ie);
}
}
}
}
while (count <= maxTries);
//gets here only when all attempts have failed
throw new RuntimeException("DeadlockRetryMethodInterceptor failed to successfully execute target "
+ " due to deadlock in all retry attempts",
new DeadlockDataAccessException("Created by DeadlockRetryMethodInterceptor", null));
}
}
Step 3: The utility class used by the dynamic proxy to determine if the exception indicates deadlock.
package com.myapp.deadlock;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.dao.CannotAcquireLockException;
public final class DeadlockUtil
{
public static final String DEADLOCK_MSG = "encountered a deadlock situation. Please re-run your command.";
static boolean isDeadLock(Throwable throwable)
{
boolean isDeadLock = false;
Throwable[] causes = ExceptionUtils.getThrowables(throwable);
for (Throwable cause : causes)
{
if (cause instanceof CannotAcquireLockException || (cause.getMessage() != null
&& (cause.getMessage().contains("LockAcquisitionException") || cause.getMessage().contains(
DEADLOCK_MSG))))
{
isDeadLock = true;
return isDeadLock;
}
}
return isDeadLock;
}
}
Step 4: Define the target object interface with the annotation.
package com.myapp.engine;
import com.myapp.DeadlockRetry;
public interface AccountServicePersistenceDelegate
{
@DeadlockRetry(maxTries = 10, tryIntervalMillis = 5000)
abstract Account getAccount(String accountNumber);
}
Step 5: Define the target object implementaion.
package com.myapp.engine;
import com.myapp.dao.AccountDAO;
...
import javax.annotation.Resource;
import org.springframework.stereotype.Repository;
@Repository
public class AccountServicePersistenceDelegateImpl implements AccountServicePersistenceDelegate
{
@Resource(name = "accountDao")
private AccountDAO accountDAO;
public Account getStatementOfNetAsset(String accountNumber)
{
Account account = accountDAO.getAccount(String accountNumber);
return account;
}
}
Step 6: Invoke the target via the proxy.
...
@Component("accountService")
@ThreadSafe
public class AccountServiceImpl implements AccountService
{
@Resource
private AccountServicePersistenceDelegate asServicePersistenceDelegate;
private AccountServicePersistenceDelegate proxyAsPersistenceDelegate;
@PostConstruct
public void init()
{
this.proxyAsPersistenceDelegate = (AccountServicePersistenceDelegate) Proxy
.newProxyInstance(AccountServicePersistenceDelegate.class.getClassLoader(), new Class<?>[]
{AccountServicePersistenceDelegate.class},
new DeadlockRetryHandler(asServicePersistenceDelegate));
}
public void processAccount(String accountNumber) {
//...
Account account = proxyAsPersistenceDelegate.getAccount(accountNumber); // invokes the target via the proxy
//............
}
}
Other design patterns - real life examples
- Sharing and reusing objects with the flyweight design pattern in Java
- When to use a builder design pattern? real life tips
- Proxy design pattern implementing thread safe wrappers
- Java I/O -- the Decorator and proxy design pattern interview questions and answers
- Java design pattern interview questions and answers: strategy and factory pattern
- Sharing and reusing objects with the flyweight design pattern in Java
- Observer design pattern for publish/Subscribe model
Labels: design pattern, Multi-threading
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home