Search

CDI AOP Tutorial: Java Standard Method Interception Tutorial - Java EE

This article discusses CDI based AOP in a tutorial format. CDI is the Java standard for dependency injection (DI) and interception (AOP). It is evident from the popularity of DI and AOP that Java needs to address DI and AOP so that it can build other standards on top of it. DI and AOP are already the foundation of many Java frameworks.
CDI is a foundational aspect of Java EE 6. It is or will be shortly supported by Caucho's Resin Java Application Server, Java EE WebProfile certified, IBM's WebSphere, Oracle's Glassfish, Red Hat's JBoss and many more application servers. CDI is similar to core Spring and Guice frameworks. Like JPA did for ORM, CDI simplifies and sanitizes the API for DI and AOP. If you have worked with Spring or Guice, you will find CDI easy to use and easy to learn. If you are new to AOP, then CDI is an easy on ramp for picking up AOP quickly, as it uses a small subset of what AOP provides. CDI based AOP is simpler to use and learn.
One can argue that CDI only implements a small part of AOP—method interception. While this is a small part of what AOP has to offer, it is also the part that most developers use.
CDI can be used standalone and can be embedded into any application.
Here is the source code for this tutorial, and instructions for use. It is no accident that this tutorial follows many of the same examples in the Spring 2.5 AOP tutorial written three years ago.
It will be interesting to compare and contrast the examples in this tutorial with the one written three years ago for Spring based AOP.



Design goals of this tutorial


This tutorial is meant to be a description and explanation of AOP in CDI without the clutter of EJB 3.1 or JSF. There are already plenty of tutorials that cover EJB 3.1 and JSF (and CDI).
We believe that CDI has merit on its own outside of the EJB and JSF space. This tutorial only covers CDI. Repeat there is no JSF 2 or EJB 3.1 in this tutorial. There are plenty of articles and tutorials that cover using CDI as part of a larger JEE 6 application. This tutorial is not that. This tutorial series is CDI and only CDI.
This tutorial only has full, complete code examples with source code you can download and try out on your own. There are no code snippets where you can't figure out where in the code you are suppose to be.
So far these tutorials have been well recieved and we got a lot of feedback. There appears to be a lot of interest in the CDI standard. Thanks for reading and thanks for your comments and participation so far.



AOP Basics


For some, AOP seems like voodoo magic. For others, AOP seems like a cure-all. For now, let's just say that AOP is a tool that you want in your developer toolbox. It can make seemingly impossible things easy. Aagin, when we talk about AOP in CDI, we are really talking about interception which is a small but very useful part of AOP. For brevity, I am going to refer to interception as AOP.
The first time that I used AOP was with Spring's transaction management support. I did not realize I was using AOP. I just knew Spring could apply EJB-style declarative transaction management to POJOs. It was probably three to six months before I realized that I was using was Spring's AOP support. The Spring framework truly brought AOP out of the esoteric closet into the main stream light of day. CDI brings these concepts into the JSR standards where other Java standards can build on top of CDI.
You can think of AOP as a way to apply services (called cross-cutting concerns) to objects. AOP encompasses more than this, but this is where it gets used mostly in the main stream.
I've using AOP to apply caching services, transaction management, resource management, etc. to any number of objects in an application. I am currently working with a team of folks on the CDI implementation for the revived JSR-107 JCache. AOP is not a panacea, but it certainly fits a lot of otherwise difficult use cases.
You can think of AOP as a dynamic decorator design pattern. The decorator pattern allows additional behavior to be added to an existing class by wrapping the original class and duplicating its interface and then delegating to the original. See this article decorator pattern for more detail about the decorator design pattern. (Notice in addition to supporting AOP style interception CDI also supports actual decorators, which are not covered in this article.)



Sample application revisited


For this introduction to AOP, let's take a simple example, let's apply security services to our Automated Teller Machine example from the first the first in this series.
Let's say when a user logs into a system that a SecurityToken is created that carries the user's credentials and before methods on objects get invoked, we want to check to see if the user has credentials to invoke these methods. For review, let's look at the AutomatedTellerMachine interface.


Code Listing: AutomatedTellerMachine interface
 
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.math.BigDecimal;
04. 
05.public interface AutomatedTellerMachine {
06. 
07.public abstract void deposit(BigDecimal bd);
08. 
09.public abstract void withdraw(BigDecimal bd);
10. 
11.}
In a web application, you could write a ServletFilter, that stored this SecurityToken in HttpSessionand then on every request retrieved the token from Session and put it into a ThreadLocal variable where it could be accessed from a SecurityService that you could implement.
Perhaps the objects that needed the SecurityService could access it as follows: 

Code Listing: AutomatedTellerMachineImpl implementing security without AOP
 
view source
print?
01.public void deposit(BigDecimal bd) {
02./* If the user is not logged in, don't let them use this method */
03.if(!securityManager.isLoggedIn()){
04.throw new SecurityViolationException();
05.}
06./* Only proceed if the current user is allowed. */
07. 
08.if (!securityManager.isAllowed("AutomatedTellerMachine", operationName)){
09.throw new SecurityViolationException();
10.}
11....
12. 
13.transport.communicateWithBank(...);
14.}
In our ATM example, the above might work out well, but imagine a system with thousands of classes that needed security. Now imagine, the way we check to see if a user is "logged in" changed. If we put this code into every method that needed security, then we could possibly have to change this a thousand times if we changed the way we checked to see if a user was logged in.
What we want to do instead is to use CDI to create a decorated version of theAutomateTellerMachineImpl bean. The decorated version would add the additional behavior to theAutomateTellerMachineImpl object without changing the actual implementation of theAutomateTellerMachineImpl. In AOP speak, this concept is called a cross-cutting concern. A cross-cutting concern is a concern that crosses the boundry of many objects.
CDI does this by creating what is called an AOP proxy. An AOP proxy is like a dynamic decorator. Underneath the covers CDI can generate a class at runtime (the AOP proxy) that has the same interface as our AutomatedTellerMachine. The AOP proxy wraps our existing atm object and provides additional behavior by delegating to a list of method interceptors. The method interceptors provide the additional behavior and are similar to ServletFilters but for methods instead of requests.



Diagrams of CDI AOP support

Thus before we added CDI AOP, our atm example was like Figure 1.
 

Figure 1: Before AOP advice
 

  
 After we added AOP support, we now get an AOP proxy that applies the securityAdvice to the atm as show in figure 2. 

Figure 2: After AOP advice
 

 
You can see that the AOP proxy implements the AutomatedTellerMachine interface. When the client object looks up the atm and starts invoking methods instead of executing the methods directly, it executes the method on the proxy, which then delegates the call to a series of method interceptor called advice, which eventually invoke the actual atm instance (now called atmTarget).
Let's actually look at the code for this example.
For this example, we will use a simplified SecurityToken that gets stored into a ThreadLocalvariable, but one could imagine one that was populated with data from a database or an LDAP server or some other source of authentication and authorization.
Here is the SecurityToken, which gets stored into a ThreadLocal variable, for this example: 

SecurityToken.java Gets stored in ThreadLocal
 
view source
print?
01.package org.cdi.advocacy.security;
02. 
03./**
04.* @author Richard Hightower
05.*
06.*/
07.public class SecurityToken {
08. 
09.private boolean allowed;
10.private String userName;
11. 
12.public SecurityToken() {
13. 
14.}
15. 
16. 
17. 
18.public SecurityToken(boolean allowed, String userName) {
19.super();
20.this.allowed = allowed;
21.this.userName = userName;
22.}
23. 
24. 
25. 
26.public boolean isAllowed(String object, String methodName) {
27.return allowed;
28.}
29. 
30. 
31./**
32.* @return Returns the allowed.
33.*/
34.public boolean isAllowed() {
35.return allowed;
36.}
37./**
38.* @param allowed The allowed to set.
39.*/
40.public void setAllowed(boolean allowed) {
41.this.allowed = allowed;
42.}
43./**
44.* @return Returns the userName.
45.*/
46.public String getUserName() {
47.return userName;
48.}
49./**
50.* @param userName The userName to set.
51.*/
52.public void setUserName(String userName) {
53.this.userName = userName;
54.}
55.}
The SecurityService stores the SecurityToken into the ThreadLocal variable, and then delegates to it to see if the current user has access to perform the current operation on the current object as follows: 

SecurityService.java Service
 
view source
print?
01.package org.cdi.advocacy.security;
02. 
03. 
04.public class SecurityService {
05. 
06.private static ThreadLocal<SecurityToken> currentToken = newThreadLocal<SecurityToken>();
07. 
08.public static void placeSecurityToken(SecurityToken token){
09.currentToken.set(token);
10.}
11. 
12.public static void clearSecuirtyToken(){
13.currentToken.set(null);
14.}
15. 
16.public boolean isLoggedIn(){
17.SecurityToken token = currentToken.get();
18.return token!=null;
19.}
20. 
21.public boolean isAllowed(String object, String method){
22.SecurityToken token = currentToken.get();
23.return token.isAllowed();
24.}
25. 
26.public String getCurrentUserName(){
27.SecurityToken token = currentToken.get();
28.if (token!=null){
29.return token.getUserName();
30.}else {
31.return "Unknown";
32.}
33.}
34. 
35.}
The SecurityService will throw a SecurityViolationException if a user is not allowed to access a resource. SecurityViolationException is just a simple exception for this example.


SecurityViolationException.java Exception
 
view source
print?
01.package com.arcmind.springquickstart.security;
02. 
03./**
04.* @author Richard Hightower
05.*
06.*/
07.public class SecurityViolationException extends RuntimeException {
08. 
09./**
10.*
11.*/
12.private static final long serialVersionUID = 1L;
13. 
14.}
To remove the security code out of the AutomatedTellerMachineImpl class and any other class that needs security, we will write an Aspect in CDI to intercept calls and perform security checks before the method call. To do this we will create a method interceptor (known is AOP speak as an advice) and intercept method calls on the atm object.
Here is the SecurityAdvice class which will intercept calls on the AutomatedTellerMachineImplclass.


SecurityAdvice
 
view source
print?
01.package org.cdi.advocacy.security;
02. 
03. 
04. 
05.import javax.inject.Inject;
06.import javax.interceptor.AroundInvoke;
07.import javax.interceptor.Interceptor;
08.import javax.interceptor.InvocationContext;
09. 
10./**
11.* @author Richard Hightower
12.*/
13.@Secure @Interceptor
14.public class SecurityAdvice {
15. 
16.@Inject
17.private SecurityService securityManager;
18. 
19.@AroundInvoke
20.public Object checkSecurity(InvocationContext joinPoint) throwsException {
21. 
22.System.out.println("In SecurityAdvice");
23. 
24./* If the user is not logged in, don't let them use this method */
25.if(!securityManager.isLoggedIn()){           
26.throw new SecurityViolationException();
27.}
28. 
29./* Get the name of the method being invoked. */
30.String operationName = joinPoint.getMethod().getName();
31./* Get the name of the object being invoked. */
32.String objectName = joinPoint.getTarget().getClass().getName();
33. 
34. 
35./*
36.* Invoke the method or next Interceptor in the list,
37.* if the current user is allowed.
38.*/
39.if (!securityManager.isAllowed(objectName, operationName)){
40.throw new SecurityViolationException();
41.}
42. 
43.return joinPoint.proceed();
44.}
45.}
Notice that we annotate the SecuirtyAdvice class with an @Secure annotation. The @Secureannotation is an @InterceptorBinding. We use it to denote both the interceptor and the classes it intercepts. More on this later.

No comments:

Post a Comment