Deadlock retry with Spring AOP using aspectj tutorial
In the previous post entitled "Deadlock Retry with JDK Dynamic Proxy" we looked at using a JDK proxy. Here we will use Spring AOP.
The required dependency jar files are
<properties>
<spring.version>3.0.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring AOP + AspectJ -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
</dependencies>
Step 1: Define the annotation that can be used.
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 Aspect that performs the deadlock retry logic
package com.myapp.deadlock;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
@Aspect
public class DeadlockRetryAspect implements Ordered
{
private static final Logger LOG = LoggerFactory.getLogger(DeadlockRetryAspect.class);
@Around(value = "@annotation(deadlockRetry)", argNames = "deadlockRetry")
public Object invoke(final ProceedingJoinPoint pjp, final DeadlockRetry deadlockRetry) throws Throwable
{
final Integer maxTries = deadlockRetry.maxTries();
long tryIntervalMillis = deadlockRetry.tryIntervalMillis();
Object target = pjp.getTarget();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
int count = 0;
do
{
try
{
count++;
LOG.info("Attempting to invoke method " + method.getName() + " on " + target.getClass() + " count "
+ count);
Object result = pjp.proceed(); // 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));
}
@Override
public int getOrder()
{
return 99;
}
}
Step 3: Define the util class that checks if an exception is deadlock related.
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: Finally, apply the annotation where required to perform deadlock retry.
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;
@DeadlockRetry(maxTries = 10, tryIntervalMillis = 5000)
public Account getStatementOfNetAsset(String accountNumber)
{
Account account = accountDAO.getAccount(String accountNumber);
return account;
}
}
Step 5: In your spring cofig file you need to have
;?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.myapp"/>
...
</beans>
Labels: AOP, Spring, Spring tutorial

2 Comments:
Thanks for this post! I would just add that you need to register this aspect with Spring. It is easiest to do this by adding @Component annotation to the DeadlockRetryAspect class.
Thanks. Will try that.
Post a Comment
Subscribe to Post Comments [Atom]
<< Home