I have been working on several projects now and I have observed one common requirement in all the web application projects I have worked so far. The requirement is that the application need to track the changes especially when the update happens to a record in the database. The application also need to track who updates and when the update happens on the record. Tracking changes is not a big deal as this can be easily implemented using database triggers. When the record gets updated in the database, the old record can be moved into a history table using database triggers. Let us see the big picture.
The Big Picture
I have often come across developers writing umpteen number of lines of code in the presentation layer of the web application just to compare the changes made to the domain data against the data retrieved from the database. One of the things that compelled them to implement the track changes logic in the presentation layer is to avoid invoking the business service layer unless there is a genuine change in the data.
Things can get unwieldy to implement the above mentioned track changes functionality in the presentation layer if the application demands tracking changes to a large number of objects. We also do not want to implement such functionality in the domain object either. So let us take a different approach in implementing such functionality that will scale to a very large number of objects in a less invasive way.
Use Case Requirement
Let us take Online User Management web application as an example where we will implement the track changes functionality with the help of AspectJ, Spring framework and Hibernate. The use case mandates whenever a user updates the profile, the system tracks the changes to the properties of the domain object and records the user who updates and the date on which the update happens in the database table.
- AOP : Aspect Oriented Programming a.k.a. AOP is a way of modularizing crosscutting concerns much like object-oriented programming is a way of modularizing common concerns.
- AspectJ: AspectJ is an implementation of aspect-oriented programming for Java. AspectJ adds to Java just one new concept, a join point -- and that's really just a name for an existing Java concept. It adds to Java only a few new constructs: pointcuts, advice, inter-type declarations and aspects. Pointcuts and advice dynamically affect program flow, inter-type declarations statically affects a program's class hierarchy, and aspects encapsulate these new constructs. For more details, please refer the AspectJ Programming Guide.
- Spring Framework - It is the leading open source J2EE application framework .
- Hibernate - It is one of the leading Object Relational Mapping tools. Hibernate is a powerful, high performance object/relational persistence and query service.
We will first design our domain objects we are going to use as an example to track the changes to their properties. A typical online user management application contains a minimum of Users, Contacts & Address domain objects. Let us now verify the structure of these objects.
Now we have identified the domain objects whose properties need to be tracked whenever they get updated by the user.
Our crosscutting concern is the notification of changes to Java bean properties. JavaBeans can have bound properties. This means we can register listeners to get notification when the value of the property changes. The java.beans package contains the classes and interfaces required to implement this functionality. The registered listener can implement the methods to record who updated the record and when the record is updated before saving the updated bean information in the database. We are going to use AspectJ, the leading AOP (Aspect Oriented Programming) implementation to address the above mentioned crosscutting concern.
Aspect-oriented programming complements object-oriented programming in many ways.
One interesting complementary feature is behavior composability. This means it should be possible to compose a class by adding behavior from different classes. OO uses inheritance and many patterns to add behavior to existing classes. AOP allows us to use mixins without changing the class inheritance hierarchy or otherwise changing the code.
The BeanTrackChangesAspect.aj file is self explanatory as it contains a lot of comments. This one AOP aspect adds behavior and additional methods to our Java Beans (domain objects). AOP language is invaluable in solving many problems. Let us analyze the internals of BeanTrackChangesAspect.aj file. It has references to the following properties.
- PropertyChangeSupport - A utility class that can be used by beans that support bound properties. You can use an instance of this class as a member field of your bean and delegate various work to it. This class is serializable.
- PropertySupport - This is the inter-type declaration. The behavior that needs to be composed in each of the domain object is declared as an interface. Declaring additional methods or fields on behalf of a type is called "Introductions". It is also known as an inter-type declaration in the AspectJ community.
- declare parents: Users implements PropertySupport - This "declare parents" applies the PropertySupport interface to Users domain object. In other words, it makes the Users domain object implement the PropertySupport interface. The other declare statements works the same way for other respective domain objects. The PropertySupport methods are implemented in the BeanTrackChangesAspect.aj files for each domain object.
- Join Point - a point during the execution of a program, such as the execution of a method or the handling of an exception.
- Pointcut - a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP
- Around Advice - Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.
The AOP pointcut implementation shown above uses a conditional expression to check whether "trackChanges" property is set prior to let the advice execute the joinpoint. The "trackChanges" property is set by our unit test prior to call the update on the respective domain object. This way we can control the behavior of the advice.
Now, let us look at the listener implementation that gets notified of the property changes.
In the listener implementation, the updated by and updated date properties are set. In the example shown above, the updated by property is hard coded. In reality, this value should come from the currently logged in user who modifies the record. Such information can be obtained from the SecurityContext once the user is authenticated. The listener sets the "propertyChanged" property on the respective domain object in addition to the updated by and updated date properties. This provides a hook to the service layer to further control the subsystem processing when the properties of the domain object are changed. This may even include notifying other systems about the change in the properties of the respective domain object.
Now, let us look at what Spring framework and Hibernate does in completing the use case and help us execute the unit tests.
Spring Framework - Spring is a lightweight application framework. Spring helps structure the whole application in a consistent and productive manner. The inversion of control container enables sophisticated configuration management of POJOs (Plain Old Java Objects). Some of the benefits of using Spring framework are :
- Spring focuses around providing a way to manage the business objects.
- Spring is an ideal framework for test driven projects.
- Spring provides a consistent framework for data access, whether using JDBC or an O/R mapping product such as TopLink, Hibernate or a JDO implementation
- Spring 2.0 provides tighter integration with AspectJ AOP language.
Hibernate - Spring framework provides first class integration with Hibernate like any other ORM tools. Hibernate is used in this use case example for data persistence. Spring provides abstract templates that deals with the low level plumbing of ORM tools. However, in this use case example, I will show the usage of template less data access strategy using Hibernate.
The unit test will invoke the createUser in IUsersService interface API to create the user. The test also invokes the updateUser API to update the user information.
The usersServiceImpl delegates the call to the collaborating data access object whose dependency is injected by Spring.
The finder API defined in the data access interface is used to fetch the user information from the database. Let us now see the implementation of IUsersServiceDao interface below.
With Hibernate 3.0.1, Spring is able to manage Hibernate Session without having to go through the Hibernate Session. Spring is able to manage the underlying resource without using templates that are available for those technologies. This is true for JPA (Java Persistence API) as well. What this means is that even if we use the Hibernate API directly (SessionFactory.getCurrentSession()), we will still be using a Spring managed Hibernate Session. This is true for EntityManager obtained through a JPA EntityManagerFactory as well.
The one thing that was missing was the exception translation. However we can still enjoy automatic exception translation using AOP, if using Hibernate in an environment where annotations are not available (example: J2sdk 1.4.x) by first declare an exception translator and then declare a piece of AOP configuration, like so in the following way:
Putting Things Together
Let us now wire everything in the Spring application context file such that we can run the tests to track the Bean changes.
The following figure is the Spring configuration of the managed beans declared in bean-track-context.xml file.
Figure 2. Spring Application Context
Let us now run the unit test and analyze the results. Spring Mock provides first class abstract templates that facilitates the dependency injection features and can run the tests in transaction.
Some of the Spring Mock Tests available from spring-mock.jar are given below:
Spring Mock Test Classes
This test class injects test dependencies so we don't need to specifically perform the Spring application context lookups. It also automatically locates the corresponding object in the set of configuration files specified in the getConfigLocations() method.AbstractTransactionalDataSourceSpringContextTests:
This test class is used to test the code that runs inside a transaction. It creates and rolls back a transaction for each test case. We write code with the assumption that there is a transaction present. It provides the fields such as a JdbcTemplate that can be used to verify database state after test operations, or verify the results of queries performed by application code. An ApplicationContext is also inheited, and can be used for explicit lookup if necessary.
This test class is used for testing JPA functionality. It provides an EntityManager instance that we can use to call JPA methods.
This class extends from AbstractJpaTests and it is used for load-time weaving (LTW) purposes using AspectJ. We override the method getActualAopXmlLocation() to specify the location of AspectJ's configuration xml file.
This is a convenient base class for testing the presentation and controller layers using Sping MVC in the application.
We use AbstractTransactionalDataSourceSpringContextTests from the above list such that our tests can run within a transaction and provides dependency injection features. The following is the result of executing two JUnit Tests. Since the tests are run within a transaction, the data that is used to populate the domain object prior to the insert and after the update do not affect the state of the database since the transaction is rolled back after the test execution is completed.
The red rectangular box in the Figure 3 above indicate the log of the bean properties that are changed and picked up properly by the listener. The blue rectangular box in the figure above indicate the log of the updated by and updated date properties of the beans.
A snippet of the Unit Test Log of the Listener
A snippet of the Unit Test Log After the Updates:
When the second test is run, the listener did not pick any changes as there are no changes in the bean properties. See the log snippet of the updated by and updated date bean properties.
A snippet of the Unit Test Log without updating the data:
AspectJ programs can be compiled using ajc (command line compiler) that comes with the AspectJ distribution. The AspectJ Development Tools (AJDT) project provides Eclipse platform based tool support for AOSD with AspectJ. I have used AJDT 1.5 for Eclipse 3.3 released as part of Europa!
Concerns that crosscut a class which is the fundamental manipulation in Java could be isolated so that such code is not spread out across the codebase. Aspect Oriented Programming makes this possible to separate such crosscutting concerns. This makes the modification easier. It also helps to add a concern without changing the code in a less invasive way.
1. The AspectJ Development Kit
2. Spring framework Reference Document
3. The AspectJ Programming Guide
4. Bound Properties
5. AOP mixin Implementation
About The Author
Vigil Bose is a Technical and Application Integration Architect at GlobalNet Services Inc, an application and infrastructure solutions provider to US Federal Government and other commercial clients. He primarily focuses on light-weight architectures and open source frameworks to build J2EE solutions for his clients. He has designed and built successful enterprise software solutions using Spring, and other open source tools and implemented them in production for his clients. He enjoys creating business value by applying simple techniques and principles to application architecture.