Chapter 5. Adding Behavior with AOP

Object Oriented Programming (OOP) contains many useful techniques for software development including encapsulation, inheritance, and polymorphism. However, it does not solve the problem of addressing logic that is often repeated in many different classes. Examples of this include logging, security, and transactional logic which is traditionally hard-coded into each class. This type of logic is called a cross-cutting concern.
Aspect Oriented Programming (AOP) works to allow cross-cutting concerns to be applied to classes after they have been compiled. This keeps the source code free of logic which is not central to the main purpose of the class and streamlines maintenance. The method depends on the AOP implementation. Typically if a class implements an interface, each method call to an instance of the class first passes through a proxy. This proxy implements the same interface, adding the required behavior. Alternatively, if an interface is not used, then the java bytecode of the compiled class is modified: the original methods are renamed and replaced by methods that implement the cross-cutting logic. These new methods then call the original methods after the cross-cutting logic has been executed. Another method to achieve the same result is modifying the bytecode to create a subclass of the original class that overrides its methods. The overridden methods then execute the cross-cutting logic before calling the corresponding methods of the super class.
JBoss AOP is a framework for AOP. Using it, you can create cross-cutting concerns using conventional java classes and methods. In AOP terminology each concern is represented by an aspect that you implement using a simple POJO. Behavior is provided by methods within the aspect called advices. These advices follow certain rules for their parameter, and return types and any exceptions that they throw. Within this framework, you can use conventional object-oriented notions such as inheritance, encapsulation, and composition to make your cross-cutting concerns easy to maintain. Aspects are applied to code using an expression language that allows you to specify which constructors, methods and even fields to target. You can quickly change the behavior of multiple classes by editing a configuration file.
This chapter contains examples which demonstrate how to use JBoss AOP alongside the Microcontainer to create and apply an auditing aspect to the Human Resources Service. The auditing code could be placed within the HRManager class, but it would clutter the class with code which is not relevant to its central purpose, bloating it and making it harder to maintain. The design of the aspect also provide modularity, making it easy to audit other classes in the future, if the scope of the project changes.
AOP can also be used to apply additional behavior during the deployment phase. This example will create and bind a proxy to a bean instance into a basic JNDI service, allowing it to be accessed using a JNDI look-up instead of the Microcontainer controller.

5.1. Creating An Aspect

The examples/User_Guide/gettingStarted/auditAspect directory contains all the files needed to create the aspect.
  • pom.xml
  • src/main/java/org/jboss/example/aspect/AuditAspect.java

Example 5.1. Example POJO

public class AuditAspect {

    private String logDir;
    private BufferedWriter out;

    public AuditAspect() {
        logDir = System.getProperty("user.dir") + "/log";

        File directory = new File(logDir);
        if (!directory.exists()) {
            directory.mkdir();
        }
    }

    public Object audit(ConstructorInvocation inv) throws Throwable {
        SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyy-kkmmss");
        Calendar now = Calendar.getInstance();
        String filename = "auditLog-" + formatter.format(now.getTime());

        File auditLog = new File(logDir + "/" + filename);
        auditLog.createNewFile();
        out = new BufferedWriter(new FileWriter(auditLog));
        return inv.invokeNext();
    }

    public Object audit(MethodInvocation inv) throws Throwable {
        String name = inv.getMethod().getName();
        Object[] args = inv.getArguments();
        Object retVal = inv.invokeNext();

        StringBuffer buffer = new StringBuffer();
        for (int i=0; i < args.length; i++) {
            if (i > 0) {
                buffer.append(", ");
            }
            buffer.append(args[i].toString());
        }

        if (out != null) {
            out.write("Method: " + name);
            if (buffer.length() > 0) {
                out.write(" Args: " + buffer.toString());
            }
            if (retVal != null) {
		out.write(" Return: " + retVal.toString());
            }
            out.write("\n");
            out.flush();
        }
        return retVal;
    }
}
			
			
			

Procedure 5.1. Creating the POJO

  1. The constructor checks for the presence of a log directory in the current working directory, and creates one if not found.
  2. Next, an advice is defined. This advice is called whenever the constructor of the target class is called. This creates a new log file within the log directory to record method calls made on different instances of the target class in separate files.
  3. Finally, another advice is defined. This advice applies to each method call made on the target class.The method name and arguments are stored, along with the return value. This information is used to construct an audit record and write it to the current log file. Each advice calls inv.invokeNext(), which chains the advices together if more than one cross-cutting concern has been applied, or to call the target constructor/method.

Note

Each advice is implemented using a method that takes an invocation object as a parameter, throws Throwable and returns Object. At design time you do not know which constructors or methods these advices will be applied to, so make the types as generic as possible.
To compile the class and create an auditAspect.jar file that can be used by other examples, type mvn install from the auditAspect directory.