Google

Aug 29, 2013

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: , ,

2 Comments:

Anonymous Anonymous said...

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.

6:22 AM, October 18, 2013  
Blogger Unknown said...

Thanks. Will try that.

11:55 AM, November 02, 2013  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home