Google

Feb 6, 2012

Spring and Hibernate Interview Questions and Answers: AOP, interceptors, and deadlock retry

Before this, please refer to Hibernate Interview Questions and Answers: with annotations and Spring framework  , as the examples below are extension of this blog.

Q. How would you go about implementing a dead lock retry service using Spring and hibernate?
A.
  • Define an annotation to annotate the methods that needs deadlock retry service. E.g. DeadlockRetry.java
  • Define the interceptor that gets wired up via AOP to perform the retry functionality by invoking the annotated method. E.g. DeadlockRetryMethodInterceptor
  • Wire up the annotation and deadlock retry classes via Spring config. E.g. transactionContext.xml
  • Finally, annotate the method that needs perform the retry service. E.g. EmployeeServiceImpl --> saveEmployee (....)

Define a custom annotation class DeadlockRetry.java.

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 21;
    int tryIntervalMillis() default 100;
    
}


Define the DeadlockRetryMethodInterceptor for performing retries. The above annotation will be bound to the following implementation via Spring.

package com.myapp.deadlock;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.exception.LockAcquisitionException;
import org.springframework.dao.DeadlockLoserDataAccessException;


public class DeadlockRetryMethodInterceptor implements MethodInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(DeadlockRetryMethodInterceptor.class);
        
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object obj = invocation.getThis();
        Method method = invocation.getMethod();
        Method targetMethod = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
        DeadlockRetry dlRetryAnnotation = targetMethod.getAnnotation(DeadlockRetry.class);
        int maxTries = dlRetryAnnotation.maxTries();
        int tryIntervalMillis = dlRetryAnnotation.tryIntervalMillis();
        for (int i = 0; i < maxTries; i++) {
            try {
                LOGGER.info("Attempting to invoke " + invocation.getMethod().getName());
                Object result = invocation.proceed();    // retry
                LOGGER.info("Completed invocation of " + invocation.getMethod().getName());
                return result;
            } catch (Throwable e) {
                Throwable cause = e;
    
      //... put the logic to identify DeadlockLoserDataAccessException or LockAcquisitionException
      //...in the cause. If the execption is not due to deadlock, throw an exception 
    
            if (tryIntervalMillis > 0) {
              try {
                  Thread.sleep(tryIntervalMillis);
              } catch (InterruptedException ie) { 
                LOGGER.warn("Deadlock retry thread interrupted", ie);
              }
            }
    
        }
  
        //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 DeadlockLoserDataAccessException("Created by DeadlockRetryMethodInterceptor", null));
    }    
}


Wire up the annotation and the interceptor via Spring config transactionContext.xml. Only the snippet is shown.

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- Deadlock Retry AOP             -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="deadlockRetryPointcut" class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
            <constructor-arg><null/></constructor-arg>
            <constructor-arg value="com.myapp.deadlock.DeadlockRetry" />
    </bean>
 
    <bean id="deadlockRetryMethodInterceptor" class="com.myapp.deadlock.DeadlockRetryMethodInterceptor" />
 
    <bean id="deadlockRetryPointcutAdvisor"
        class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <constructor-arg ref="deadlockRetryPointcut" />
            <constructor-arg ref="deadlockRetryMethodInterceptor" />        
    </bean>

 
Finally, annotate the method that needs to be retried in the event of dead lock issues.

package com.myapp.service;


import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
//....other imports

public class EmployeeServiceImpl implements EmployeeService {
    
    private final EmployeeTableRepository employeeRepository;
    private PlatformTransactionManager transactionManager;
   
    public EmployeeServiceImpl(EmployeeTableRepository employeeRepository, PlatformTransactionManager transactionManager) {
        this.employeeRepository = employeeRepository;
        this.transactionManager = transactionManager;
    }


    @DeadlockRetry
    public Employee saveEmployee(Employeee employee) throws RepositoryException {
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(new DefaultTransactionDefinition(
                        TransactionDefinition.PROPAGATION_REQUIRED));
        try {
            employee = this.employeeRepository.saveEmployee(employee);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RepositoryException(e);
        } finally {
            if (!transactionStatus.isCompleted()) {
                transactionManager.commit(transactionStatus);
            }
        }
        return employee;
    }
 
 
 //....other methods omitted for brevity

} 

The above example gives a real life example using a custom annotation and AOP (i.e. Aspect Oriented Programming).

Note: Also refer to Spring Interview Questions and Answers for explanation on interceptors.

Q. What are the pros and cons between Spring AOP and AspectJ AOP?
A. The Spring AOP is simpler as it is achieved via a runtime dynamic proxy class. Unlike AspectJ, it does not have to go through load time weaving or a compiler. The Spring AOP uses the proxy and decorator design patterns.

Since Spring AOP uses proxy based AOP, it can only offer method execution pointcut, whereas the AspectJ  AOP supports all pointcuts.

In some cases, the compile time weaving (e.g. AspectJ AOP) offers better performance.

Labels: ,

3 Comments:

Blogger jaylen watkins said...

Thanks for the post of this question and answers. This is well explained one.

Interview Questions

7:51 PM, February 07, 2012  
Blogger Pankaj said...

Informative tutorial about custom annotation.

3:17 AM, September 30, 2012  
Anonymous ramesh said...

EMPLOYEE

empId
empName
PermAddrId
tempAddrId


ADDR

addrId
addr1
addr2
Street
State
City

Use Hibernate,DAO layer to fetch/update/delete employee details based on empID as input

plz get the result,am troubling in this code

5:36 PM, November 23, 2013  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home