An Introduction to Aspect-Oriented programming with JBoss AOP
Observer with Introductions and Mixins
Say we want to be able to be notified somehow when BankAccount.balance is changed. An option would be to implement the Observer pattern, but we don't want to include all the Observable plumbing code in our BankAccount class. First of all let's add an annotation to the field we want to monitor.
package bank;
public class BankAccount
{
int accountNumber;
@Observed
int balance;
...
}
Next we can write an implementation of Observable:
package bank;
import java.util.ArrayList;
import java.util.List;
public class ObservableMixin implements Observable
{
List<Observer> observers = new ArrayList<Observer>();
public void addObserver(Observer listener)
{
observers.add(listener);
}
public void removeObserver(Observer listener)
{
observers.remove(listener);
}
public void notifyObservers(Object event)
{
for (Observer observer : observers)
{
observer.update(event);
}
}
}
This is a mixin class, which implements the Observable interface. It contains all the plumbing code needed for the Observable part of the pattern. It can be introduced into other POJOs using the following xml
<aop>
<introduction expr="hasfield(* *->@bank.Observed)">
<mixin>
<interfaces>
bank.Observable
</interfaces>
<class>bank.ObservableMixin</class>
</mixin>
</introduction>
...
This does two things. It makes all classes that have a field annotated with @Observed implement the @Observable interface and implement those methods. Second, when anybody attempts to call the methods from the Observable interface, it delegates all the calls to an instance of the ObservableMixin.
Next we have an aspect to trap when the annotated fields change their values bound using the following xml.
...
<aspect class="bank.ObserverAspect"/>
<bind pointcut="set(* bank.BankAccount->@bank.Observed)">
<around aspect="bank.ObserverAspect" name="fieldChanged"/>
</bind>
</aop>
The aspect is an around advice that captures the values before and after modifying the field.
package bank;
import java.lang.reflect.Field;
import org.jboss.aop.joinpoint.FieldWriteInvocation;
public class ObserverAspect
{
public Object fieldChanged(FieldWriteInvocation invocation) throws Throwable
{
Observable tgt = (Observable)invocation.getTargetObject();
Field fld = invocation.getField();
String fieldName = fld.getName();
fld.setAccessible(true);
Object oldVal = invocation.getField().get(tgt);
Object result = invocation.invokeNext();
Object newVal = invocation.getField().get(tgt);
tgt.notifyObservers("Changed " + fieldName + " from " + oldVal + " to " + newVal);
return result;
}
}
Since the classes captured by the pointcut to select which classes should have the ObserverAspect applied are in the set of classes captured by the class expression to pick out classes that should have the Observable introduction and mixin, we can safely cast the target of the invocation to Observable. We then get the values before and after writing the field, and then use the Observable target object to notify the observers. As mentioned the call to Observable.notifyObservers() will end up inside BankAccount's ObservableMixin.
Finally, let us modify the Bank.main() method to register an Observer with a BankAccount instance
public static void main(String[] args)
{
System.out.println("*** Creating account 1");
BankAccount acc1 = new BankAccount(1);
acc1.credit(150);
bankAccounts.put(acc1.getAccountNumber(), acc1);
System.out.println("*** Creating account 2");
BankAccount acc2 = new BankAccount(2);
acc2.credit(230);
bankAccounts.put(acc2.getAccountNumber(), acc2);
System.out.println("Installing observer");
((Observable)acc2).addObserver(new Observer(){
public void update(Object evt)
{
System.out.println("!!! Observer: " + evt);
}
});
System.out.println("*** Balance acount 1: " + acc1.getBalance());
System.out.println("*** Balance acount 2: " + acc2.getBalance());
//Transfer some money
System.out.println("*** Transfer 50 from account 1 to account 2");
transfer(acc1, acc2, 50);
System.out.println("*** Balance acount 1: " + acc1.getBalance());
System.out.println("*** Balance acount 2: " + acc2.getBalance());
}
Although until woven the BankAccount class does not implement the Observable interface, the Java compiler does not perform any checks when casting to an interface (only to a superclass), so the cast from BankAccount to Observable will compile. (If the example is run without weaving you would get a ClassCastException since then BankAccount would not implement the Observable interface). When we run this example we can see the registered Observer gets triggered:
*** Creating account 1
*** Bank Account constructor
*** BankAccount.credit()
*** Creating account 2
*** Bank Account constructor
*** BankAccount.credit()
Installing observer
*** Balance acount 1: 150
*** Balance acount 2: 230
*** Transfer 50 from account 1 to account 2
*** BankAccount.debit()
*** BankAccount.credit()
!!! Observer: Changed balance from 230 to 280
*** Balance acount 1: 100
*** Balance acount 2: 280
The code for this example can be found in the listing5/ folder of the download bundle.
Conclusion
We have taken a simple application, added extra behaviour to it using JBoss AOP, and explored a few of the features offered. Apart from adding a few annotations to help with our pointcut expressions (and there are even less intrusive, although more incovenient ways to achieve the similar effect), we have left the core application as is, and added extra cross-cutting behaviour such as logging and security by applying aspects, as well as using a combination of aspects, interface introductions and mixins to implement the Observer pattern.
JBoss AOP can be downloaded from http://www.jboss.org/jbossaop/ and comes with a tutorial to get you started.
| Attachment | Size |
|---|---|
| jboss-aop-samples.zip | 4.76 MB |
- Login or register to post comments
- 21945 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
Joshua Partogi replied on Fri, 2008/08/29 - 10:05pm
jaikiran replied on Sat, 2008/08/30 - 3:52am
Good article, Kabir.
I have a question. Is the aspect code allowed to change the value that is being passed to the joinpoint? I mean, if the credi(int amount) method was being passed a value of 200, can the code in the aspect change this value to 100 before the credit method is invoked? If yes, is there any way to secure such access?
Kabir Khan replied on Mon, 2008/09/01 - 5:06am
Joshua,
Yes annotations can be used instead.An example can be found here and the containing directory contains more examples. If using annotations, instead of passing in the path of a jboss-aop.xml file with -Djboss.aop.path, you point JBoss AOP to a directory containing your annotated aspects using -Djboss.aop.class.path. The tutorial examples that come with the download will show how in more detail.
Kabir Khan replied on Mon, 2008/09/01 - 5:13am
Jaikiran,
The aspect code can modify the values that are passed in. There is currently no way to stop this, if that is what you mean by securing it. If this feature is important to you please ask for it on our user forums, and we will take it into consideration.
gsowji replied on Tue, 2009/02/10 - 11:37pm
gsowji replied on Wed, 2009/02/11 - 1:18am
daveeeed replied on Fri, 2009/06/05 - 2:54am
warrenty replied on Mon, 2009/06/08 - 1:40am
modthoa replied on Wed, 2009/06/17 - 10:51am
emad964 replied on Mon, 2009/06/29 - 4:14pm
jiji530 replied on Mon, 2009/06/29 - 9:42pm
wikaniko replied on Thu, 2009/07/09 - 1:02pm
superpan3721 replied on Sat, 2009/08/01 - 1:38am
nakul replied on Tue, 2009/10/20 - 1:49pm
in response to: thejavafreak
markgrant1st replied on Wed, 2009/11/04 - 2:27am
Wish I could learn the programming . I know each and everything about the Markets but computer, me and computer can not be friends at all :)\
Home Insurance Rates
Teddy P replied on Thu, 2009/11/05 - 9:46am
abcdentist replied on Sun, 2009/11/08 - 2:57am
hpmedia replied on Sun, 2009/11/08 - 8:44pm
hpmedia replied on Tue, 2009/11/17 - 5:40pm