Google

Jun 5, 2014

Java AOP with aopalliance -- method interceptors and custom annotations

Spring Interview Questions and Answers Q1 - Q14 are FAQs

Q1 - Q4 Overview & DIP Q5 - Q8 DI & IoC Q9 - Q10 Bean Scopes Q11 Packages Q12 Principle OCP Q14 AOP and interceptors
Q15 - Q16 Hibernate & Transaction Manager Q17 - Q20 Hibernate & JNDI Q21 - Q22 read properties Q23 - Q24 JMS & JNDI Q25 JDBC Q26 Spring MVC Q27 - Spring MVC Resolvers

Step 1: What is AOP Alliance?

AOP Alliance intends to facilitate and standardize the use of AOP to enhance existing middle ware environments (such as JEE), or development environements (e.g. Eclipse, NetBeans). The AOP Alliance also aims to ensure interoperability between Java/JEE AOP implementations to build a larger AOP community.

AOP Alliance is a set of interfaces that multiple frameworks implement, including both Guice and Spring.

  <groupId>aopalliance</groupId>
  <artifactId>aopalliance</artifactId>
  <version>1.0</version>
  

Q. Why use AOP Alliance in Spring?

Spring AOP can be used together with AOP Alliance MethodInterceptors. AOP Alliance compliant interceptors foster interoperability with other AOP frameworks such as Google Guice. Spring can be used with AspectJ as well, which has annotation syntax that is concise and expressive.

Step 2: Define a custom annotation so taht a method that is annotated with this custom annotation can be retried based on supplied values like attempts,interval, etc.

package com.interceptor.retry;

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;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Retry {

 int attempts() default 3;
 int interval() default 3000;
 boolean nonNullReturn() default true;
 Class<? extends Throwable> exception() default RetryException.class;
 
    //static inner class 
 static class RetryException extends Throwable {
   private static final long serialVersionUID = 1L; // for serialization
   private RetryException() {}

 }  
}


Step 3: Java method interceptor using the aop alliance jar to implement retry logic as a cross cutting concern. Mthods annotated with @Retry will be retried after waiting for the supplied interval.

package com.interceptor.retry;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class RetryMethodInterceptor implements MethodInterceptor {

 @Override
 public Object invoke (MethodInvocation invocation) throws Throwable {
     //
  Method annotatedMethod = invocation.getMethod();
  Method objectMethod = invocation.getThis().getClass()
                                            .getMethod(annotatedMethod.getName(), 
                                             annotatedMethod.getParameterTypes());
  Retry retry = objectMethod.getAnnotation(Retry.class);

  Throwable throwable = null;
  Object result = null;
  int attempt = 0;

  String method = String.format("%s.%s", invocation.getMethod().getDeclaringClass()
                                                               .getName(), 
                                                                invocation.getMethod().getName());
  System.out.println("Invoking {}" + method);

  do {
   attempt++;
   try {
    result = invocation.proceed();
    if (result == null && retry.nonNullReturn()) {
     throw new RuntimeException(String.format("Non-null return expected for %s", method));
    }
    System.out.println("Completed {}" + method);
    return result;
   } catch (Throwable retryThrowable) {
    throwable = retryThrowable;
    try {
     Thread.sleep(retry.interval());
    } catch (InterruptedException e) {
     System.out.println("Retry wait interrupted");
    }
   }
  } while (attempt < retry.attempts());

  //if reached here, all retires have failed
  System.out.println(String.format("Failed invoking %s", method));
  throw retry.exception().getConstructor(Throwable.class).newInstance(throwable);
 }
}


The above example is frequently used in enterprise applications to solve issues relating to

1. Deadlock retry.
2. Service retry.

Labels: ,

Apr 17, 2014

Understanding Java custom annotation with a practical example

In real life Java applications, you need to provide service retries on failures. For example, retry 3 times at the interval of 3 seconds, etc. Here is a custom Java annotation example.

Step 1: Define the Retry run-time annotation to be applied to the declared fields.


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface Retry {

 int attempts() default 3;
 int delayInSeconds() default 1; // default 1 second

}


Step 2:  Use the above custom run-time annotation in the PrinterServiceUtil, so that it will retry the service (e.g. DummyService) that it wraps.

public class PrintServiceTest {

 @Retry(attempts = 3, delayInSeconds = 3) //retry 3 times @ 3 seconds interval
 private PrintServiceUtil printService;

 public static void main(String[] args) {

  PrintServiceUtil printService = new PrintServiceUtil();
  printService.print("Hello");
 }
}


Step 3PrinterServiceUtil  class that uses the annotation to decide, if retry is required, and if required, what retry count and retry values to use by extracting them from the annotation.

import java.lang.reflect.Field;

/**
 * A bit convoluted method to demonstrate custom annotation to keep it simple without
 * AOP and Dependency injection frameworks, which are more suited.
 * 
 */

public class PrintServiceUtil {

 private DummyService service;

 public void print(String message) {

  //if PrintServiceUtil invoking this is annotated with retry
  //use dynamic proxy design pattern to retry if the service is down
  Field[] fields = getCallerClassName().getDeclaredFields();
  boolean serviceCreated = false;

  for (Field field : fields) {
   Retry annotation = field.getAnnotation(Retry.class);
   if (annotation != null && field.getType() == PrintServiceUtil.class) {
    service = (DummyService) RetryProxy.newInstance(new DummyServiceImpl(), annotation.attempts(),
      annotation.delayInSeconds());
    serviceCreated = true;
   }
  }

  //if not annotated, retry is not required
  if (!serviceCreated) {
   service = new DummyServiceImpl();
  }

  service.execute(message); //execute the service
 }

 
 
 /**
  * gets the calling class from the stack trace
  * @return
  */
 private Class getCallerClassName() {
  String callerClassName = null;

  try {
   callerClassName = new Exception().getStackTrace()[2].getClassName();
   return Class.forName(callerClassName);
  } catch (Exception ex) {}

  throw new RuntimeException("Error getting caller class");
 }
}


Step 4: The DummyService interface and implementations are similar to the dynamic proxy tutorial -- retry example.

public interface DummyService {
     abstract void execute(String message);
}


public class DummyServiceImpl implements DummyService {
 
 private int count = 0 ;
 
 public void execute(String message) {
  count++;
  if(count % 3 == 0){
   System.out.println (message);
  }
  else {
   throw new RuntimeException("Service Cannot be accessed ..... ");
  }
 }

}



Step 5: The dynamic proxy class that performs the actual retry.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

public class RetryProxy<T> implements InvocationHandler {

 final T delegate; // underlying object
 int retryCount;
 long delay;

 //create a proxy
 public static Object newInstance(Object obj, int retryCount, long delay) {
  return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
    obj.getClass().getInterfaces(), new RetryProxy(obj, retryCount, delay));
 }

 private RetryProxy(T underlying, int retryCount, long delay) {
  this.delegate = underlying;
  this.retryCount = retryCount;
  this.delay = delay;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  int retries = 0;
  boolean completed = false;
  Object ret = null;

  while (!completed) {
   try {
    ret = method.invoke(delegate, args);
    completed = true;
   } catch (Exception e) {
    retries++;
    if (retries > retryCount) {
     completed = true;
     throw e;
    }
    
    TimeUnit.SECONDS.sleep(delay);
    System.out.println("Retrying the service. Retry count " + retries);
   }
  }

  return ret;

 }
}


Output:

Retrying the service. Retry count 1
Retrying the service. Retry count 2
Hello

Change the annotation properties and test it.

Labels:

Jul 26, 2013

Creating Java custom annotations with Spring aspectj AOP

There are situations where you want to retry a particular method that fails. For example, retry submitting a message to an messaging queue 3 times, retry external service calls, etc. AOP (Aspect Oriented Programming) is well suited for this as this is a cross cutting concern. In this tutorial, I use aspectj, spring-aop, and Java annotation. Here are the steps.

Step 1:  The pom.xml file to outline the dependency jar files.

<!-- Spring -->
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-core</artifactId>
 <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>3.1.2.RELEASE</version>

</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aop</artifactId>
 <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context-support</artifactId>
 <version>3.1.2.RELEASE</version>
</dependency>

<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>2.2</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
 <artifactId>spring-aspects</artifactId>
 <version>3.1.2.RELEASE</version>
</dependency>


<!-- Aspectj -->
<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.10</version>
</dependency>


Step 2:Define the annotation -- RetryOnFailure.

package com.mycompany.app9;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(
{
    ElementType.METHOD, ElementType.TYPE
})
public @interface RetryOnFailure
{
    int attempts() default 3;
    
    int delay() default 1000; //default 1 second
}

Step 3: Define the aspect point cut implementation RetryOnFailureAspect.

package com.mycompany.app9;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.NestedRuntimeException;

@Aspect
public class RetryOnFailureAspect
{
    public static final String RETRY_LIMIT_EXCEEDED_MSG = "Retry limit exceeded.";
    
    @Around("execution(* *(..)) && @annotation(retry)")
    public Object retry(ProceedingJoinPoint pjp, RetryOnFailure retry) throws Throwable
    {
        
        Object returnValue = null;
        for (int attemptCount = 1; attemptCount <= (1 + retry.attempts()); attemptCount++)
        {
            try
            {
                returnValue = pjp.proceed();
            }
            catch (Exception ex)
            {
                handleRetryException(pjp, ex, attemptCount, retry);
            }
        }
        return returnValue;
    }
    
    private void handleRetryException(ProceedingJoinPoint pjp, Throwable ex,
            int attemptCount, RetryOnFailure retry) throws Throwable
    {
        
        if (ex instanceof NestedRuntimeException)
        {
            ex = ((NestedRuntimeException) ex).getMostSpecificCause();
        }
        
        if (attemptCount == 1 + retry.attempts())
        {
            throw new RuntimeException(RETRY_LIMIT_EXCEEDED_MSG, ex);
        }
        else
        {  
            System.out.println(String
                    .format("%s: Attempt %d of %d failed with exception '%s'. Will retry immediately. %s",
                            pjp.getSignature(), attemptCount,
                            retry.attempts(),
                            ex.getClass().getCanonicalName(),
                            ex.getMessage()));
        }
    }
}

Step 4: Now wire up aspectj and the annotation via Spring xml files.

Firstly, wire up aop via spring-aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

 <!-- Enable the @AspectJ support -->
 <aop:aspectj-autoproxy />

 <bean id="retryOnFailureAspect" class="com.mycompany.app9.RetryOnFailureAspect" />

</beans>

Next, the application context xml file springApplicationContext.xml


<?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:context="http://www.springframework.org/schema/context"
 xmlns:p="http://www.springframework.org/schema/p"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

 <!-- Import your AspectJ config -->
 <import resource="classpath:spring-aop.xml" />

 <!-- Scan you spring services for annotations -->
 <context:component-scan base-package="com.mycompany.app9" />

</beans>


Step 5: Finally, the test class that uses this annotation. Forcefully throw an exception to see if the method is retried.

package com.mycompany.app9;

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

@Component
public class RetryOnFailureTest
{
    
    @RetryOnFailure(attempts = 3, delay = 2000)
    public void testRetry()
    {
        System.out.println("Entered ....................");
        throw new RuntimeException("Forcing an exception");
    }
    
    public static void main(String[] args)
    {
        final ApplicationContext context = new ClassPathXmlApplicationContext("springApplicationContext.xml");
        // Get me my spring managed bean
        final RetryOnFailureTest retryFailureTest = context.getBean(RetryOnFailureTest.class);
        retryFailureTest.testRetry();
    }
}

Step 6: The output will be


Jul 24, 2013 6:41:24 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5e3974: startup date [Wed Jul 24 18:41:24 EST 2013]; root of context hierarchy
Jul 24, 2013 6:41:24 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [springApplicationContext.xml]
Jul 24, 2013 6:41:24 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-aop.xml]
Jul 24, 2013 6:41:24 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1dc423f: defining beans [org.springframework.aop.config.internalAutoProxyCreator,retryOnFailureAspect,retryOnFailureTest,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
Entered ....................
void com.mycompany.app9.RetryOnFailureTest.testRetry(): Attempt 1 of 3 failed with exception 'java.lang.RuntimeException'. Will retry immediately. Forcing an exception
Entered ....................
void com.mycompany.app9.RetryOnFailureTest.testRetry(): Attempt 2 of 3 failed with exception 'java.lang.RuntimeException'. Will retry immediately. Forcing an exception
Entered ....................
void com.mycompany.app9.RetryOnFailureTest.testRetry(): Attempt 3 of 3 failed with exception 'java.lang.RuntimeException'. Will retry immediately. Forcing an exception
Entered ....................
Exception in thread "main" java.lang.RuntimeException: Retry limit exceeded.
 at com.mycompany.app9.RetryOnFailureAspect.handleRetryException(RetryOnFailureAspect.java:43)
 at com.mycompany.app9.RetryOnFailureAspect.retry(RetryOnFailureAspect.java:26)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
 at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
 at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
 at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
 at com.mycompany.app9.RetryOnFailureTest$$EnhancerByCGLIB$$4086c6b6.testRetry()
 at com.mycompany.app9.RetryOnFailureTest.main(RetryOnFailureTest.java:23)
Caused by: java.lang.RuntimeException: Forcing an exception
 at com.mycompany.app9.RetryOnFailureTest.testRetry(RetryOnFailureTest.java:15)
 at com.mycompany.app9.RetryOnFailureTest$$FastClassByCGLIB$$e37240e1.invoke()
 at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
 at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
 at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
 at com.mycompany.app9.RetryOnFailureAspect.retry(RetryOnFailureAspect.java:22)
 ... 13 more


Labels: ,