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