Enterprise Java

Simple Aspect Oriented Programming (AOP) using CDI in JavaEE

We write service APIs which cater to certain business logic. There are few cross-cutting concerns that cover all service APIs like Security, Logging, Auditing, Measuring Latencies and so on. This is a repetitive non-business code which can be reused among other methods. One way to reuse is to move these repetitive code into its own methods and invoke them in the service APIs somethings like:
 
 
 
 
 
 

public class MyService{
   public ServiceModel service1(){
      isAuthorized();
      //execute business logic.
   }
}

public class MyAnotherService{
  public ServiceModel service1(){
    isAuthorized():
    //execute business logic. 
  }
}

The above approach will work but not without creating code noise, mixing cross-cutting concerns with the business logic. There is another approach to solve the above requirements which is by using Aspect and this approach is called Aspect Oriented Programming (AOP). There are a different ways you can make use of AOP – by using Spring AOP, JavaEE AOP. In this example I Will try to use AOP using CDI in Java EE applications. To explain this I have picked a very simple example of building a web application to fetch few records from Database and display in the browser.

Creating the Data access layer

The table structure is:

create table people(
    id INT NOT NULL AUTO_INCREMENT, 
    name varchar(100) NOT NULL,
    place varchar(100),
    primary key(id));

Lets create a Model class to hold a person information

package demo.model;
public class Person{
  private String id;
  private String name;
  private String place;
  public String getId(){ return id; } 
  public String setId(String id) { this.id = id;}
  public String getName(){ return name; } 
  public String setName(String name) { this.name = name;}
  public String getPlace(){ return place; } 
  public String setPlace(String place) { this.place = place;}
}

Lets create a Data Access Object which exposes two methods –

  1. to fetch the details of all the people
  2. to fetch the details of one person of given id
package demo.dao;

import demo.common.DatabaseConnectionManager;
import demo.model.Person;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class PeopleDAO {

    public List<Person> getAllPeople() throws SQLException {
        String SQL = "SELECT * FROM people";
        Connection conn = DatabaseConnectionManager.getConnection();
        List<Person> people = new ArrayList<>();
        try (Statement statement = conn.createStatement();
                ResultSet rs = statement.executeQuery(SQL)) {
            while (rs.next()) {
                Person person = new Person();
                person.setId(rs.getString("id"));
                person.setName(rs.getString("name"));
                person.setPlace(rs.getString("place"));
                people.add(person);
            }
        }
        return people;
    }

    public Person getPerson(String id) throws SQLException {
        String SQL = "SELECT * FROM people WHERE id = ?";
        Connection conn = DatabaseConnectionManager.getConnection();
        try (PreparedStatement ps = conn.prepareStatement(SQL)) {
            ps.setString(1, id);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    Person person = new Person();
                    person.setId(rs.getString("id"));
                    person.setName(rs.getString("name"));
                    person.setPlace(rs.getString("place"));
                    return person;
                }
            }
        }

        return null;
    }
}

You can use your own approach to get a new Connection. In the above code I have created a static utility that returns me the same connection.

Creating Interceptors

Creating Interceptors involves 2 steps:

  1. Create Interceptor binding which creates an annotation annotated with @InterceptorBinding that is used to bind the interceptor code and the target code which needs to be intercepted.
  2. Create a class annotated with @Interceptor which contains the interceptor code. It would contain methods annotated with @AroundInvoke, different lifecycle annotations, @AroundTimeout and others.

Lets create an Interceptor binding by name @LatencyLogger

package demo;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
import javax.interceptor.InterceptorBinding;

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

Now we need to create the Interceptor code which is annotated with @Interceptor and also annotated with the Interceptor binding we created above i.e @LatencyLogger:

package demo;
import java.io.Serializable;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@LatencyLogger
public class LatencyLoggerInterceptor implements Serializable{
  
  @AroundInvoke
    public Object computeLatency(InvocationContext invocationCtx) throws Exception{
        long startTime = System.currentTimeMillis();
        //execute the intercepted method and store the return value
        Object returnValue = invocationCtx.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("Latency of " + invocationCtx.getMethod().getName() +": " + (endTime-startTime)+"ms");
        return returnValue;
        
    }
}

There are two interesting things in the above code:

  1. use of @AroundInvoke
  2. parameter of type InvocationContext passed to the method

@AroundInvoke designates the method as an interceptor method. An Interceptor class can have only ONE method annotated with this annotation. When ever a target method is intercepted, its context is passed to the interceptor. Using the InvocationContext one can get the method details, the parameters passed to the method.

We need to declare the above Interceptor in the WEB-INF/beans.xml file

<?xml version="1.0" encoding="UTF-8"?>
<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>demo.LatencyLoggerInterceptor</class>
    </interceptors>
</beans>

Creating Service APIs annotated with Interceptors

We have already created the Interceptor binding and the interceptor which gets executed. Now lets create the Service APIs and then annotate them with the Interceptor binding

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package demo.service;

import demo.LatencyLogger;
import demo.dao.PeopleDAO;
import demo.model.Person;
import java.sql.SQLException;
import java.util.List;
import javax.inject.Inject;

public class PeopleService {

  @Inject
  PeopleDAO peopleDAO;

  @LatencyLogger
  public List<Person> getAllPeople() throws SQLException {
    return peopleDAO.getAllPeople();
  }

  @LatencyLogger
  public Person getPerson(String id) throws SQLException {
    return peopleDAO.getPerson(id);
  }

}

We have annotated the service methods with the Interceptor binding @LatencyLogger. The other way would be to annotate at the class level which would then apply the annotation to all the methods of the class. Another thing to notice is the @Inject annotation that injects the instance i.e injects the dependency into the class.

Next is to wire up the Controller and View to show the data. The controller is the servlet and view is a plain JSP using JSTL tags.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package demo;

import demo.model.Person;
import demo.service.PeopleService;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "AOPDemo", urlPatterns = {"/AOPDemo"})
public class AOPDemoServlet extends HttpServlet {

  @Inject
  PeopleService peopleService;

  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    try {
      List<Person> people = peopleService.getAllPeople();
      Person person = peopleService.getPerson("2");
      request.setAttribute("people", people);
      request.setAttribute("person", person);
      getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
    } catch (SQLException ex) {
      Logger.getLogger(AOPDemoServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}

The above servlet is available at http://localhost:8080/

/AOPDemo. It fetches the data and redirects to the view to display the same. Note that the Service has also been injected using @Inject annotation. If the dependencies are not injected and instead created using new then the Interceptors will not work. This is an important point which I realised while building this sample.

The JSP to render the data would be

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" 
           uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>AOP Demo</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <table>
      <tr>
        <th>Id</th>
        <th>Name</th>
        <th>Place</th>
      </tr>
      <c:forEach items="${requestScope.people}" var="person">
        <tr>
          <td><c:out value="${person.id}"/></td>
          <td><c:out value="${person.name}"/></td>
          <td><c:out value="${person.place}"/></td>
        </tr>
      </c:forEach>
    </table>
    <br/>
    Details for person with id=2
    <c:out value="Name ${person.name} from ${person.place}" />
  </body>
</html>

With this you would have built a very simple app using Interceptors. Thanks for reading and staying with me till this end. Please share your queries/feedback as comments. And also share this article among your friends!

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
jose
jose
10 years ago

Mohamed,

Thank you for posting. Found this to be very helpful. I have a question. You mention that we must use @Inject to utilize this. How exactly is that done?

Mohamed Sanaulla
10 years ago
Reply to  jose

Java EE framework takes care of instantiation of that particular variable annotated with @Inject. It identifies the type of object to be instantiated from the type of the variable declared.

Rance Moest
Rance Moest
10 years ago

Yet another AOP == Logging example.

Back to top button