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

Oct 11, 2012

Spring DI (aka IOC) Tutorial

Thus Spring tutorial is an extension to the Hibernate tutorial where the CourseDao is injected via Spring as opposed to directly instantiating it.

Step 1: As demonstrated in other tutorials, the first step is to have the relevant Spring framework dependency jar files. This is where Maven comes in handy to download and manage dependencies. The pom.xml file in the previous tutorial needs to now include Spring framework related jar files. I have also cleaned up the pom file by defining the version numbers via properties.



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.mytutorial</groupId>
 <artifactId>simple</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>simple</name>
 <url>http://maven.apache.org</url>

    <!-- define the version numbers -->
 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <junit.version>3.8.1</junit.version>
  <hsqldb.version>2.2.8</hsqldb.version>
  <hibernate.version>3.2.6.GA</hibernate.version>
  <hibernate-annotations.version>3.3.1.GA</hibernate-annotations.version>
  <spring.version>3.0.5.RELEASE</spring.version>


 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>${junit.version}</version>
   <scope>test</scope>
  </dependency>

  <!-- jar that contains the HSQLDB driver class -->
  <dependency>
   <groupId>org.hsqldb</groupId>
   <artifactId>hsqldb</artifactId>
   <version>${hsqldb.version}</version>
  </dependency>

  <!-- GA means General Availability -->
  <!-- Hibernate framework JARS -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate</artifactId>
   <version>${hibernate.version}</version>
  </dependency>

  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-annotations</artifactId>
   <version>${hibernate-annotations.version}</version>
  </dependency>


  <!-- Spring 3 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>

 </dependencies>
</project>


If you right-mouse-click on the folder "simple", and then select "Maven --> Update Dependencies" you should see the relevant JARs added as shown below.


Step 2: Next step is to define your beans that you want to inject via a XML configuration file know as the spring application context. This file goes in the src/main/resources folder. Make sure this folder is in the classpath. If it is not, right-mouse-click on the "resources" folder and then select "Build Path --> Use as Source Folder". Here is the Spring configuration file simpleContext.xml.

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
 <bean id="courseDao" class="com.mytutorial.dao.CourseDaoImpl" />
  
</beans>

Step 3: Redefine the HibernateTutorial  class to use Spring.

package com.mytutorial;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mytutorial.dao.CourseDao;

public class HibernateTutorial {
 
 public static void main(String[] args) {
  
  //bootstrap spring via its context file
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "simpleContext.xml");
  
  CourseDao cDao = (CourseDao)context.getBean("courseDao");
  cDao.readAllCourses();
 }

}


As you can see, CourseDao is injected via Spring. That's all to it. In reality, there will be a service layer between the DAO layer (e.g. CourseDao) and the class that makes use of the service (e.g. HibernateTutorial). Let's look at this scenario to understand and appreciate DI (i.e. Dependency Injection) and how Spring does it.

Step 1: Define the service interface and implementation classes CourseService and CourseServiceImpl respectively.

The interface CourseService

package com.mytutorial.service;

public interface CourseService {
    abstract void listAllEnrolments();
}


The implementation  CourseServiceImpl

package com.mytutorial.service;

import com.mytutorial.dao.CourseDao;

public class CourseServiceImpl implements CourseService {
 
 //the implementation is injected via Spring's setter injection
 CourseDao dao;

 public void listAllEnrolments() {
  dao.readAllCourses();
 }

 public CourseDao getDao() {
  return dao;
 }

 //Spring IOC container invokes this method
 public void setDao(CourseDao dao) {
  this.dao = dao;
 }
 
}

Step 2: Modify the HibernateTutorial to inject the CourseService and CourseDao will be injected via the modified Spring configuration shown in Step 3.

package com.mytutorial;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mytutorial.service.CourseService;

public class HibernateTutorial {
 
 public static void main(String[] args) {
  
  //bootstrap spring via its context file
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "simpleContext.xml");
  
  CourseService cService = (CourseService)context.getBean("courseService");
  cService.listAllEnrolments();
 }

}

Step 3: The config file simpleContext.xml.

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
 <bean id="courseDao" class="com.mytutorial.dao.CourseDaoImpl" />
 
 <bean id="courseService" class="com.mytutorial.service.CourseServiceImpl">
      <property name="dao" ref="courseDao" />
 </bean>
  
</beans>





Spring is vast, and I have covered more step by step Spring tutorials at Spring concepts and step by step tutorial style examples.

Labels: