Search

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

Notice that we use @Inject to inject the securityManager. Also we mark the method that implements that around advice with and @AroundInvoke annotation. This essentially says this is the method that does the dynamic decoration.
Thus, the checkSecurity method of SecurityAdvice is the method that implements the advice. You can think of advice as the decoration that we want to apply to other objects. The objects getting the decoration are called advised objects.
Notice that the SecurityService gets injected into the SecurityAdvice and the checkSecuritymethod uses the SecurityService* to see if the user is logged in and the user has the rights to execute the method.
An instance of InvocationContext, namely joinPoint, is passed as an argument to checkSecurity. The InvocationContext has information about the method that is being called and provides control that determines if the method on the advised object's methods gets invoked (e.g.,AutomatedTellerMachineImpl.withdraw and AutomatedTellerMachineImpl.deposit). If *`joinPoint.proceed()`* is not called then the wrapped method of the advised object (withdraw ordeposit) is not called. (The proceed method causes the actual decorated method to be invoked or the next interceptor in the chain to get invoked.)
In Spring, to apply an Advice like SecurityAdvice to an advised object, you need a pointcut. A pointcut is like a filter that picks the objects and methods that get decorated. In CDI, you just mark the class or methods of the class that you want decorated with an interceptor binding annotation. There is no complex pointcut language. You could implement one as a CDI extention, but it does not come with CDI by default. CDI uses the most common way developer apply interceptors, i.e., with annotations.
CDI scans each class in each jar (and other classpath locations) that has a META-INF/beans.xml. TheSecurityAdvice get installed in the CDI beans.xml.


META-INF/beans.xml
 
view source
print?
01.<beans xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
02.xsi:schemaLocation="
03.http://java.sun.com/xml/ns/javaee
04.http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
05. 
06.<interceptors>
07.<class>org.cdi.advocacy.security.SecurityAdvice</class>
08.</interceptors>
09.</beans>
You can install interceptors in the order you want them called.
In order to associate a interceptor with the classes and methods it decorates, you have to define anInterceptorBinding annotation. An example of such a binding is defined below in the @Secureannotation.


Secure.java annotation
 
view source
print?
01.package org.cdi.advocacy.security;
02. 
03.import java.lang.annotation.Retention;
04.import java.lang.annotation.Target;
05.import static java.lang.annotation.ElementType.*;
06.import static java.lang.annotation.RetentionPolicy.*;
07.import javax.interceptor.InterceptorBinding;
08. 
09. 
10.@InterceptorBinding
11.@Retention(RUNTIME) @Target({TYPE, METHOD})
12.public @interface Secure {
13. 
14.}
Notice that we annotated the @Secure annotation with the @InterceptorBinding annotation.
InterceptorBindings follow a lot of the same rules as Qualifiers as discussed in the first two articles in this series. InterceptorBindings are like qaulifiers for injection in that they can have members which can further qualify the injection. You can also disable InterceptorBinding annotation members from qualifying an interception by using the @NonBinding just like you can in Qualifiers.
To finish our example, we need to annotate our AutomatedTellerMachine with the same @Secureannotation; thus, associating the AutomatedTellerMachine with our SecurityAdvice.


AutomatedTellerMachine class using @Secure
 
view source
print?
01.package org.cdi.advocacy;
02....
03.import javax.inject.Inject;
04. 
05.import org.cdi.advocacy.security.Secure;
06. 
07.@Secure
08.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
09. 
10.@Inject
11.@Json
12.private ATMTransport transport;
13. 
14.public void deposit(BigDecimal bd) {
15.System.out.println("deposit called");
16.transport.communicateWithBank(null);
17. 
18.}
19. 
20.public void withdraw(BigDecimal bd) {
21.System.out.println("withdraw called");
22. 
23.transport.communicateWithBank(null);
24. 
25.}
26. 
27.}
You have the option of use @Secure on the methods or at the class level. In this example, we annotated the class itself, which then applies the interceptor to every method.
Let's complete our example by reviewing the AtmMain main method that looks up the atm out of CDI'sbeanContainer.
Let's review AtmMain as follows:


AtmMain.java
 
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.math.BigDecimal;
04. 
05.import org.cdi.advocacy.security.SecurityToken;
06.import org.cdiadvocate.beancontainer.BeanContainer;
07.import org.cdiadvocate.beancontainer.BeanContainerManager;
08.import org.cdi.advocacy.security.SecurityService;
09. 
10.public class AtmMain {
11. 
12.public static void simulateLogin() {
13.SecurityService.placeSecurityToken(new SecurityToken(true,
14."Rick Hightower"));
15.}
16. 
17.public static void simulateNoAccess() {
18.SecurityService.placeSecurityToken(new SecurityToken(false,
19."Tricky Lowtower"));
20.}
21. 
22.public static BeanContainer beanContainer = BeanContainerManager
23..getInstance();
24.static {
25.beanContainer.start();
26.}
27. 
28.public static void main(String[] args) throws Exception {
29.simulateLogin();
30.//simulateNoAccess();
31. 
32.AutomatedTellerMachine atm = beanContainer
33..getBeanByType(AutomatedTellerMachine.class);
34.atm.deposit(new BigDecimal("1.00"));
35.}
36. 
37.}
Continue reading... Click on the navigation links below the author bio to read the other pages of this article.
Be sure to check out part I of this series as well: CDI DI Tutorial!


Before we added AOP support when we looked up the atm, we looked up the object directly as shown in figure 1, now that we applied AOP when we look up the object we get what is in figure 2. When we look up the atm in the application context, we get the AOP proxy that applies the decoration (advice, method interceptor) to the atm target by wrapping the target and delegating to it after it invokes the series of method interceptors.

Victroy lap

The last code listing works just like you think. If you use simulateLogin, atm.deposit does not throw aSecurityException. If you use simulateNoAccess, it does throw a SecurityException. Now let's weave in a few more "Aspects" to the mix to drive some points home and to show how interception works with multiple interceptors.
I will go quicker this time.


LoggingInterceptor
 
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.util.Arrays;
04.import java.util.logging.Logger;
05. 
06.import javax.interceptor.AroundInvoke;
07.import javax.interceptor.Interceptor;
08.import javax.interceptor.InvocationContext;
09. 
10. 
11.@Logable @Interceptor
12.public class LoggingInterceptor {
13. 
14.@AroundInvoke
15.public Object log(InvocationContext ctx) throws Exception {
16.System.out.println("In LoggingInterceptor");
17. 
18.Logger logger = Logger.getLogger(ctx.getTarget().getClass().getName());
19.logger.info("before call to " + ctx.getMethod() + " with args " + Arrays.toString(ctx.getParameters()));
20.Object returnMe = ctx.proceed();
21.logger.info("after call to " + ctx.getMethod() + " returned " + returnMe);
22.return returnMe;
23.}
24.}
Now we need to define the Logable interceptor binding annotation as follows:
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.lang.annotation.Retention;
04.import java.lang.annotation.Target;
05.import static java.lang.annotation.ElementType.*;
06.import static java.lang.annotation.RetentionPolicy.*;
07.import javax.interceptor.InterceptorBinding;
08. 
09. 
10.@InterceptorBinding
11.@Retention(RUNTIME) @Target({TYPE, METHOD})
12.public @interface Logable {
13. 
14.}
Now to use it we just mark the methods where we want this logging.


AutomatedTellerMachineImpl.java using Logable
 
view source
print?
01.package org.cdi.advocacy;
02. 
03....
04. 
05.@Secure
06.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
07. 
08....
09. 
10.@Logable
11.public void deposit(BigDecimal bd) {
12.System.out.println("deposit called");
13.transport.communicateWithBank(null);
14. 
15.}
16. 
17.public void withdraw(BigDecimal bd) {
18.System.out.println("withdraw called");
19. 
20.transport.communicateWithBank(null);
21. 
22.}
23. 
24.}
Notice that we use the @Secure at the class level which will applies the security interceptor to every mehtod in the AutomatedTellerMachineImpl. But, we use @Logable only on the deposit method which applies it, you guessed it, only on the deposit method.
Now you have to add this interceptor to the beans.xml:


META-INF/beans.xml
 
view source
print?
1.<beans
2....
3.<interceptors>
4.<class>org.cdi.advocacy.LoggingInterceptor</class>
5.<class>org.cdi.advocacy.security.SecurityAdvice</class>
6.</interceptors>
7.</beans>
When we run this again, we get something like this in our console output:
view source
print?
1.May 152011 6:46:22 PM org.cdi.advocacy.LoggingInterceptor log
2.INFO: before call to public voidorg.cdi.advocacy.AutomatedTellerMachineImpl.deposit(java.math.BigDecimal) with args [1.00]
3.May 152011 6:46:22 PM org.cdi.advocacy.LoggingInterceptor log
4.INFO: after call to public voidorg.cdi.advocacy.AutomatedTellerMachineImpl.deposit(java.math.BigDecimal) returned null
Notice that the order of interceptors in the beans.xml file determines the order of execution in the code. (I added a println to each interceptor just to show the ordering.) When we run this, we get the following output. 

Output:
 
view source
print?
1.In LoggingInterceptor
2.In SecurityAdvice
If we switch the order in the beans.xml file, we will get a different order in the console output. 

META-INF/beans.xml
 
view source
print?
1.<beans
2....
3.<interceptors>
4.<class>org.cdi.advocacy.security.SecurityAdvice</class>
5.<class>org.cdi.advocacy.LoggingInterceptor</class>
6.</interceptors>
7.</beans>
view source
print?
1.In SecurityAdvice
2.In LoggingInterceptor
This is important as many interceptors can be applied. You have one place to set the order. 
Conclusion

AOP is neither a cure all or voodoo magic, but a powerful tool that needs to be in your bag of tricks. The Spring framework has brought AOP to the main stream masses and Spring 2.5/3.x has simplified using AOP. CDI brings AOP and DI into the standard's bodies where it can get further mainstreamed, refined and become part of future Java standards like JCache, Java EE 6 and Java EE 7.
You can use Spring CDI to apply services (called cross-cutting concerns) to objects using AOP's interception model. AOP need not seem like a foreign concept as it is merely a more flexible version of the decorator design pattern. With AOP you can add additional behavior to an existing class without writing a lot of wrapper code. This can be a real time saver when you have a use case where you need to apply a cross cutting concern to a slew of classes. 

To reiterate...
CDI is the Java standard for dependency injection 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 the foundation of many Java frameworks. I hope you share my excitement of CDI as a basis for other JSRs, Java frameworks and standards.
CDI is a foundational aspect of Java EE 6. It is or will be shortly supported by Caucho's Resin, IBM's WebSphere, Oracle's Glassfish, Red Hat's JBoss and many more application servers. CDI is similar to core Spring and Guice frameworks. However CDI is a general purpose framework that can be used outside of JEE 6.
CDI simplifies and sanitizes the API for DI and AOP. I find that working with CDI based AOP is easier and covers the most common use cases. CDI is a rethink on how to do dependency injection and AOP (interception really). It simplifies it. It reduces it. It gets rid of legacy, outdated ideas.
CDI is to Spring and Guice what JPA is to Hibernate, and Toplink. CDI will co-exist with Spring and Guice. There are plugins to make them interoperate nicely (more on these shortly).
Download Spring examples source code

No comments:

Post a Comment