Analyze application performance with CDI Interceptors

Using interceptors for analyzing performance issues

Do you know statements in your code like this?

  ...
  long start = System.currentTimeMillis();
  //some computing...
  long end = System.currentTimeMillis();
  long duration = end - start;
  ...

Analyzing how long the execution of a method takes is a cross-cutting concern. Cross-cutting concerns like transactions, caching or measuring latencies are aspects of a program that cannot be cleanly decomposed from the rest of the system. So they result in either code duplication or significant dependencies between systems.
CDI Interceptors are perfect for encapsulating such concerns. You can use the interceptor concept to implement such cross-cutting concerns in an isolated and reusable manner.

For my example I use Weld, the reference implementation of CDI. CDI stands for Context and Dependency Injection. It helps you to improve the structure of your code and defines a powerful set of services like dependency injection, contextual lifecycles, events, decorators and interceptors.

Here comes my toy example. execute() is annotated with @Measured. This annotations intercepts all calls to methods annotated with this annotation of every CDI Bean that is managed by the container.

package de.claudioaltamura.labs.cdi.interceptor;

import java.util.Random;
import java.util.concurrent.TimeUnit;

public class Service {
	
	private Random random = new Random();

	@Measured
	public long execute() throws InterruptedException {
		
		//long computation
		TimeUnit.SECONDS.sleep(randomTime());
		
		return 0;
	}

	private long randomTime() {
	    return random.nextInt(10) + 1;
	}

}

The annotation itself is very simple. @InterceptorBinding specifies that the annotation is an interceptor binding type. Interceptor bindings associate interceptors with target beans.

package de.claudioaltamura.labs.cdi.interceptor;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

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

import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Measured {
}

Now I show you the interceptor. MeasureTimeInterceptor finally gets all calls and just logs the execution time of your method.

package de.claudioaltamura.labs.cdi.interceptor;

import java.util.concurrent.TimeUnit;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Measured
@Interceptor
public class MeasureTimeInterceptor {
	
	@AroundInvoke
	private Object intercept(InvocationContext ic) throws Exception {
		long start = System.nanoTime();
		Object obj = ic.proceed();
		long end = System.nanoTime();
		long durationInMs = TimeUnit.NANOSECONDS.toMillis(end - start);

		Logger log = LoggerFactory.getLogger(ic.getMethod().getDeclaringClass());
		log.info(ic.getMethod().getName() + " - " + durationInMs + " ms"); 
		return obj;
	}

}

Last step is the CDI configuration (bean.xml).

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all">

	<interceptors>
    	<class>de.claudioaltamura.labs.cdi.interceptor.MeasureTimeInterceptor</class>
	</interceptors>

</beans

That’s it. A last point, I would like to talk about briefly is the decorator pattern. Decorators and interceptors are very similar concepts. So what are the differences?

"... Decorators and Interceptors are similar because they can be both used to "enrich" or "decorate" one method call with additional tasks. However Interceptors are used for general tasks, which are not related with your class in particular (e.g. auditing). On the other hand, Decorators, do bear with them the Bean attributes/operations so they can actually specialize or override a specific business functionality..." [Interceptors and Decorators tutorial]

CDI is one of the most important and popular parts of the Java EE. That’s not surprising.
I hope you enjoyed the example.

You can find the example on GithubGitHub-Mark-32px

 

Resources

CDI http://www.cdi-spec.org/

Weld http://weld.cdi-spec.org/

Reference Implementation Weld http://docs.jboss.org/weld/reference/latest/en-US/html/

Interceptor and Decorator Tutorial http://www.mastertheboss.com/jboss-frameworks/cdi/interceptors-and-decorators-tutorial?start=1

Contexts and Dependency Injection for Java EE  https://docs.oracle.com/javaee/7/tutorial/partcdi.htm#GJBNR