Business Requirement
The customer registration system (CRS) needs to send notifications to its customers after completing their online registration and transmit their address data to the invoice system in order to generate an invoice for fee payment.Technical Design
Let us decompose the above mentioned business requirement into a technical design. In this example, we will define a custom business event to indicate the customer Registration process.An event can be considered as something that occurs during a particular interval of time. In this case, it is the customer registration process. Typically, a single event may contain one or more actions that need to happen when the event occurs. As per the business requirement, we have identified two actions such as:
- Send mail notification to the customer.
- Transmit customer address data to the invoice system.
We will now design the event data structure to hold the information stored in the event database table. The following event properties are identified.
- Event Identifier: 1
- Event Description: Customer Registered Event
- Action Codes: MT
The event identifier is the primary key mapped in the database. The event description defines the description about the event. The last one is the action code that represents different actions that need to happen when the event occurs. The action codes are defined in action codes reference table.
The action codes identified for the event mentioned above are M and T whereas M represents send a mail notification to the customer and T represents transmit customer address data to invoice system.
Example: Event.java
/**
*Event.java - The event domain object
*@author - Vigil Bose
*/
public class Event implements Serializable {
private Integer eventId;
private String eventDesc;
private String eventActionCodes;
private static final long serialVersionUID = 1L;
/** The cached hash code value for this instance. Settting to 0 triggers
re-calculation. */
private int hashValue = 0;
/**
*@return the eventActionCodes
*/
public String getEventActionCodes(){
return eventActionCodes;
}
/**
* @param eventActionCodes the eventActionCodes to set
*/
public void setEventActionCodes(String eventActionCodes) {
this.eventActionCodes = eventActionCodes;
}
/**
* @return the eventDesc
*/
public String getEventDesc() {
return eventDesc;
}
/**
* @param eventDesc the eventDesc to set
*/
public void setEventDesc(String eventDesc) {
this.eventDesc = eventDesc;
}
/**
* Return the simple primary key value that identifies this object.
* @return the eventId
*/
public Integer getEventId() {
return eventId;
}
/**
* Set the simple primary key value that identifies this object.
* @param eventId the eventId to set
*/
public void setEventId(Integer eventId) {
this.hashValue = 0;
this.eventId = eventId;
}
/**
*Implementation of the equals comparison on the basis of equality
*of the primary key values.
* @param rhs
* @return boolean
*/
public boolean equals(Object rhs){
if (rhs == null)
return false;
if (! (rhs instanceof Event))
return false;
Event that = (Event) rhs;
if (this.getEventId() == null || that.getEventId() == null)
return false;
return (this.getEventId().equals(that.getEventId()));
}
/**
* Implementation of the hashCode method conforming to the Bloch pattern with
* the exception of array properties (these are very unlikely primary key types).
* @return int
*/
public int hashCode(){
if (this.hashValue == 0){
int result = 17;
int eventIdValue = this.getEventId() == null ? 0 :
this.getEventId().hashCode();
result = result * 37 + eventIdValue;
this.hashValue = result;
}
return this.hashValue;
}
}
Now we have designed the event domain object to represent the customer registration event. We now move on to designing the API contract between the web layer and the business service layer. One of the design constraints is that the domain model changes should not break the API contract between layers. In order to meet this design constraint, two data wrapper classes are identified. The AbstractData and the UserData. The AbstractData is abstract in nature that define some behaviors. The UserData is the subclass of AbstractData that provide other behaviors. For example, if you have an application framework, an abstract class may provide default services such as event and message handling.
In this example, AbstractData is responsible for collecting various business events. The subclass of AbstractData is the UserData and it is used to hold our main domain object which is the User object.
The User domain object is composed of different properties that are required to identify a user such as userId, firstName, lastName and encrypted password. It is also composed of Address domain object that has various address properties like address line 1, address line 2, city, state etc.
Example: AbstractData.java
/**
*AbstractData.java - A template pattern like class that implments
*the event collection behavior. This class is used by all data
*transfer wrapper objects between UI Layer and Server side Layer
*@author - Vigil Bose
*/
public abstract class AbstractData{
/**
*Stores all the events identified during the transaction
*Processing.
*/
private Set eventSet = Collections.synchronizedSet(new HashSet());
/**
* @return Returns the eventSet.
*/
public Set getEventSet() {
return eventSet;
}
/**
*@param event - An instance of a business event resulted from a particular
*business transaction
*/
public void addToEventSet(Event event) {
this.eventSet.add(event);
}
}
The reason for declaring a set in AbstractData is to avoid duplication of same events in the collection at a given point of time. Let us see how our UserData looks like. UserData contains the actual User domain object. So any changes to the User domain object is confined to this wrapper class and will not break the interface contract between the client and the business service layers.
Example: UserData.java
/**
*UserData.java - A concrete POJO data wrapper whose responsibility is to
*holds the main domain object reference and used between client and business
*service layers.
*@author - Vigil Bose
*/
public class UserData extends AbstractData{
private User user;
/**
* @return The user domain object instance
*/
public Users getUsers(){
return this.users;
}
/**
*@param The user instance to set.
*/
public void setUser(User user){
this.user = user;
}
}
Let's look at the business service interface. In this example, we will define an API contract called doRegister() in the IRegistrationService interface to complete the user registration business process. This API is transactional in nature since it insert records in more than one database tables. The client layer and business layer interacts through this interface.
Example: IRegistrationService.java
/**
*IRegistrationService.java - A classic example of EJB's business
*methods interface pattern that exposes all the Registration
*related business methods that can be implemented by both the
*enterprise session bean as well as the Pojo service. Moreover
*this pattern allows us to switch to POJO implementation later
*if it makes sense to do so.
*@author - Vigil Bose
*/
public interface IRegistrationService{
/**
*API to complete the registration business process
*@param userData - The data wrapper object
*/
public void doRegister(AbstractData userData);
}
For simplicity, we are only using POJO (Plain Old Java Object) service in this example. The business service implementation implements the business logic to complete the customer registration process. In this case, the only responsibility of this service implementation is to delegate the call to Data Access Layer to record the state of the customer registration transaction in the appropriate database tables.
Example: RegistrationServiceImpl.java
/**
*The primary business method implementation of Customer Registration Service.
*This is a POJO. It does not depend on any Spring APIs. It's usable outside a
*Spring container, and can be instantiated using new in a JUnit test. However,
*we can still apply declarative transaction management to it using Spring AOP.
*@author - Vigil Bose
*/
public class RegistrationServiceImpl implements IRegistrationService{
private IRegistrationServiceDao registrationServiceDao;
/**
* A setter method of dependency injection
* @param registrationServiceDao - The registrationServiceDao to set.
*/
public setRegistrationServiceDao(IRegistrationServiceDao
registrationServiceDao){
this.registrationServiceDao = registrationServiceDao;
}
/**
* API to register the user
* @param user - The user domain object
*/
public void doRegister(AbstractData userData){
this.registrationServiceDao.completeRegistration(userData.getUser());
}
}
Pragmatic use of DAO pattern
Data Access Object (DAO) is an integration tier design pattern as cataloged in the book Core J2EE Design Pattern. It encapsulates persistence store access and manipulation code into a separate layer. The persistent store in the context of this article is an RDBMS.
This pattern introduces an abstraction layer between the business logic tier and the persistent storage tier. Business objects access the RDBMS (data source) through the data access objects. This abstraction layer streamlines application code and introduces flexibility. Ideally, changes made to the data source, such as switching database vendors or type, would require changes only to the data access objects and should have minimal impact on the business objects. In the example, we are using Hibernate implementation data access strategy.
The flexibility the DAO design pattern provides is attributed primarily to a best practice for object design: Program to Interface. This principle states that concrete objects must implement an interface that is used in the caller program rather than the concrete object itself. Therefore, you can easily substitute a different implementation with little impact on client code.
Following the above said principle, we will define Registration Service DAO interface - IRegistrationServiceDao.java with a behavior of completeRegistration(). The business components will interact with the DAOs through this interface.
Example: IRegistrationServiceDao.java
/**
*A POJO data access object interface for the CRS services business layer.
*The API's defined in this interface are all transactional APIs within the
*business services layer
*@author - Vigil Bose
*/
public interface IRegistrationServiceDao{
/**
* Data Access API to create the new user in the system
* @param user - The composite user domain object
*/
public void completeRegistration(User user) throws DataAccessException;
}
Having defined the Data Access Interface, now I must provide a concrete implementation of IRegistrationServiceDao which is RegistrationServiceDaoImpl.
The implementation shown in this example is Hibernate specific. The pattern used here is a strategy and can be replaced by any Object Relational Mapping product or JDBC. The responsibility of this class is to record the state of the customer registration transaction in the database tables.
Example: RegistrationServiceDaoImpl.java
/**
*The Registration Services Data Access Strategy implementation
*using Hibernate persistence mechanism that support various
*registration related business transactions.
*@author - Vigil Bose
*/
public class RegistrationServiceDaoImpl extends HibernateDaoSupport
implements IRegistrationServiceDao{
/**
* Data Access API to create the new user in the system
* @param users - The composite users domain object
*/
public void completeRegistration(Users users) throws DataAccessException {
getHibernateTemplate().save(users);
}
}
In any application, access to the read only data is important. Let's see an example of a plain java interface ILookUpServiceDao that is used in Customer Registration System where it exposes finder and getter methods to access read only data.
Example: ILookUpServiceDao.java
/**
*A POJO data access object interface that exposes the lookup API's in Customer
*Registration System.
*The API's defined in this interface can be used with or without any other
*transactional APIs within the business services layer
*@author - Vigil Bose
*/
public interface ILookUpServiceDao{
/**
* Data Access API to find the event instance based on its primary key
* @param eventId - The event tables primary key identifier
*/
public Event findEventById(Integer eventId) throws DataAccessException;
}
The example shown below is a strategy implementation of Hibernate. The API's defined in ILookUpServiceDao interface are implemented in the concrete class LookUpServiceDaoImpl. For simplicity, only one API implementation is shown in the example.
Example: LookUpServiceDaoImpl.java
/**
*A POJO data access implementation that implements the lookup API's in Customer
*Registration System.
*The API's defined in this interface can be used with any other
*transactional APIs within the business services layer
*@author - Vigil Bose
*/
public classe LookUpServiceDaoImpl extends HibernateDaoSupport
implements ILookUpServiceDao {
/**
* Data Access API to find the event instance based on its primary key
* @param eventId - The event tables primary key identifier
* @return an instance of Event domain object
* @throws DataAccessException
*/
public Event findEventById(Integer eventId) throws DataAccessException{
return (Event)getHibernateTemplate().get(Event.class, eventId);
}
}
The HibernateDaoSupport class provided by Spring framework is a template pattern implementation and it abstracts Hibernate related API's and resource management with respect to Hibernate Session.
With the data access implementation, we satisfied one aspect of the business requirement which is the customer registration process. Now we will account for the second aspect of the requirement which is the subsystem functionality identified during the design. The requirement is such that when the registration completes, the system should send mail notification to the customer as well as transmit customer address data to invoice system to generate an invoice. We will realize the subsystem processing through the famous command pattern.
Command Pattern
The Command Pattern is used to provide a common interface to execute different commands. Any class that implements the command interface can provide specific implementation of a task in the execute() method.
During the design, we have identified two different command implementations of the same interface. The subsystem functionality is a seperation of concern as far as the business service layer is concerned. So I decoupled this separation of concern out of the main business service layer implementation which is RegistrationServiceImpl with the help of Aspect Oriented programming technique (AOP).
Those who are interested in knowing more about AOP concepts, please click here. You may also scroll down to see some introduction about AOP. Now, let us design the command interface.
Example: ICommand.java
/**
*ICommand.java - The famous command interface that exposes the execute API.
*@author - Vigil Bose
*/
public interface ICommand{
/**
*The Command design pattern encapsulates the concept of the
*command into an object. The issuer holds a reference to the
*command object rather than to the recipient.The issuer sends
*the command to the command object by executing a specific
*method on it. The command object is then responsible for
*dispatching the command to a specific recipient to get the
*job done.
*@param data - The data transfer object
*/
public void execute(Object data);
}
The typical command implementation provides a way to package a piece of computation (a receiver and a set of actions) and pass it around as a first class object. This command object invokes the method of the receiver of the command request to actually process the request. It is also common to find a command implementation that handles the specific task by itself without the need to delegate the request to the receiver. In this case, the MailingCommandImpl implements the mailing task by invoking the EmailService to send out the mail notifications. For simplicity, EmailService implementation is not shown in the example. After all, the intent is to provide an insight on how business events can be routed to subsystem processors with the aid of AOP and Spring 2.0.
Example: MailingCommandImpl.java
/**
*MailingCommandImpl.java - A command implementation that implements
*the task of sending mail notification to the customer who completed
*the registration process.
*@author - Vigil Bose
*/
public class MailingCommandImpl implements ICommand{
private IEmailService emailService;
/**
*A setter method of dependency injection
*@param emailService - The emailService instance to set.
*/
public void setEmailService(IEmailService emailService){
this.emailService = emailService;
}
/**
*API execute is used to execute the mailing tasks implemented
*@param args - An instance of AbstractData used in business service layer
*/
public void execute(Object args){
//get the reference of user object
User user = (User)args;
//get the reference of address object via its parent object User.
Address address = user.getAddress()
//Invoke the EmailService API here to send out the notifications....
}
}
I will now design the second command implementation that will help realize the business requirement to transmit the customer address data to the invoice application. In this particular implementation, we can choose any protocol of choice (e.g, Webservices, Messaging or XML over HTTP etc) to send the customer information to the invoice application provided the invoice application is capable of using any of the above mentioned protocols for application integration. For simplicity, the given example below uses JMS Messaging. The internals of JMS Messaging is not shown in the example.
Example: SendCustomerInfoCommandImpl.java
/**
*SendCustomerInfoCommandImpl.java - A command implementation that implements
*the task of transmiting the customer's address data to the invoice system.
*@author - Vigil Bose
*/
public class SendCustomerInfoCommandImpl implements ICommand{
private IMessagingService messagingService;
/**
* A setter method of dependency injection
*/
public void setMessagingService(IMessagingService messagingService){
this.messagingService = messagingService;
}
/**
*API execute is used to execute the messaging task implemented.
*@param args - An instance of AbstractData used in the business service layer
*/
public void execute(Object args){
User user = (User)args;
//Invoke the appropriate messagingService API
//to send the customer information here....
}
}
Basic concepts of AOP
AOP a.k.a. Aspect Oriented Programming attempt to aid programmers in separation of concerns, specifically cross-cutting concerns. Procedures, packages, classes, and methods all help programmers encapsulate concerns into single entities. But some concerns defy these forms of encapsulation. We call these cross-cutting concerns, because they cut across many modules in a program. It could be some code scattered or tangled, making it harder to understand and maintain. It is scattered when one concern (like in this case, it is event routing) is spread over a number of modules (e.g., classes and methods). That means to change event dispatching can require modifying all affected modules.
The code has lost its elegance and simplicity because the various new concerns have become tangled with the basic functionality (sometimes called the business logic concern). Transactions, Messaging, security, and logging all exemplify cross-cutting concerns.
AOP attempts to solve this problem by allowing the programmer to express cross-cutting concerns in stand-alone modules called aspects. Aspects can contain advice (code joined to specified points in the program) and inter-type declarations (structural members added to other classes). For example, a security module can include advice that performs a security check before accessing a bank account. The pointcut defines the times (join points) that a bank account can be accessed, and the code in the advice body defines how the security check is implemented. That way, both the check and the places can be maintained in one place. Further, a good pointcut can anticipate later program changes, so if another developer creates a new method to access the bank account, the advice will apply to the new method when it executes. The leading AOP implementations are AspectJ , AspectWorkz, Spring AOP etc.
Spring AOP is implemented in pure Java. There is no need for a special compilation process. AspectJ requires special compilation process. Spring AOP does not need to control the class loader hierarchy, and is thus suitable for use in a J2EE web container or application server. Spring 2.0 provides tighter integration with AspectJ.
Event Routing
In order to satisfy our business requirement, I have identified two AOP components. They are RegistrationBeforeAdvice and RegistrationAfterAdvice. Please refer Spring reference document to get to know more about various AOP advices and other concepts.
The rationale behind in identifying two AOP components is to support event routing and minimize the cross-dependencies in the code. The RegistrationBeforeAdvice's responsibility is limited to identify and collect appropriate business events. The before advice implementation of Spring AOP can be configured in Spring application context file to intercept the business service interface API's to inject custom behavior to identify and add appropriate business event to the event collection.
In this case, the RegistrationBeforeAdvice intercepts the doRegister(AbstractData data) API of the business service interface IRegistrationService. This advice has access to the input arguments of the service interface API which is the AbstractData. The event collection mechanism implemented earlier in the AbstractData layer becomes handy at this point. The RegistrationBeforeAdvice identifies the correct business event and add it to the event collection.
The eventMap configured in Spring application context is a global event map. The eventKeys are mapped with the appropriate business service interface API name to the event identifier. This allows us to map a new business API defined in the business service interface seamlessly to an event id in the global event map configuration without any code change in the RegistrationBeforeAdvice AOP component. However there is one caveat to this approach. When the programmer makes a mistake in configuring wrong method name to the eventId in the global event map, it will not be very obvious during compile time. But a simple Junit test will help figure out this misnomer.
Business API Name: doRegister
Event Id: 1
Mapping another business API name for example, doUpdate() to another event id 2 becomes very easy now. The only change we have to make is after defining the new API in the interface, add a mapping in the event map in the spring application context file. See the sample snippet of the configuration below.
<!-- creates a java.util.Map instance with values loaded from
the supplied 'sourceMap'.Global Event Map. The values are mapped
in event table. The keys are matched with the business API name-->
<util:map id="eventMap">
<entry key="doRegister">
<value type="java.lang.Integer">1</value></entry>
<entry key="doUpdate">
<value type="java.lang.Integer">2</value></entry>
</util:map>
In some cases, a single business process may result in multiple events. This is still doable with a slight modification to the code in RegistrationAfterAdvice and the event map configuration. In such cases, we need to account for a list of events per business transaction. For simplicity, the example in this article is restricted to show only single event per business transaction.
Please refer the code example below to see the before advice in action.
Example: RegistrationBeforeAdvice.java
/**
*RegistrationBeforeAdvice.java - This advise acts before the doRegister() API in
*the business service interface executes and sets the appropriate event in the
*eventSet collection implemented in the AbstractData layer. The event is Customer
*Registered Event. This advice inserts custom behavior in IRegistrationService API
*doRegister() before it is executed and identifies the correct business event add
*it to the event collection
*@author - Vigil Bose
*/
public class RegistrationBeforeAdvice implements MethodBeforeAdvice{
private Map eventMap;
private ILookUpServiceDao lookUpServiceDao;
/**
* A setter method of dependency injection
* @param Map - The eventMap instance to set.
*/
public void setEventMap(Map eventMap){
this.eventMap = eventMap;
}
/**
* A setter method of dependency injection
* @param lookUpServiceDao - The lookUpServiceDao instance to set.
*/
public void setLookUpServiceDao(ILookUpServiceDao lookUpServiceDao){
this.lookUpServiceDao = lookUpServiceDao;
}
/**
*Before advice can insert custom behavior before the join point
*executes, but cannot change the return value.If a before advice
*throws an exception, this will abort further execution of the
*interceptor chain. The exception will propagate back up the
*interceptor chain. If it is unchecked, or on the signature of the
*invoked method, it will be passed directly to the client; otherwise
*it will be wrapped in an unchecked exception by the AOP proxy.
*@param method - method being invoked
*@param args - arguments to the method
*@param target - target of the method invocation. May be null
*@throws Throwable - if this object wishes to abort the call.
*Any exception thrown will be returned to the caller if it's allowed
*by the method signature. Otherwise the exception will be wrapped
*as a runtime exception
*@see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method
*java.lang.Object[], java.lang.Object)
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
AbstractData data = (AbstractData)args[0];
Set keySet = this.eventMap.keySet();
Integer eventId;
Event event = null;
String eventKey = null;
//Iterate through the key set and extract event identifier and
//retrieve the event from the database and add it to the event
//collection.
for (Iterator iter = keySet.iterator(); iter.hasNext();) {
eventKey = (String) iter.next();
//Check whether the eventKey is matched with the business
//service interface API name. If it does, extract the eventId
//and retrieve the event instance from the datastore and add it
//to the event collection.
if (eventKey.equalsIgnoreCase(method.getName()){
eventId = (Integer)eventMap.get(eventKey);
event = this.lookupService.findEventById(Integer eventId);
data.addToEventSet(event);
}
}
}
}
In this example, one of the design constraints that should be considered is the exceptions that arise from either the Before advice or After advice components should not affect the online business transaction. The online customer should not be penalized for event routing errors. For simplicity, I am not showing how the exception handling should work in this example.
The RegistrationAfterAdvice's responsibility is to iterate through the event collection and action codes and initiate the routing process. The action codes used in this example are M and T. There are commands mapped for each action code in the spring application context file. It then iterate through each of the action code associated with the event (Customer Registered Event) and retrieves the mapped command object instance. Once the command object references are obtained, the routing takes place automatically by passing the customer data to each of the command implementation for appropriate task execution.
Example: RegistrationAfterAdvice.java
/**
*RegistrationAfterAdvice.java - This advise acts after when the doRegister()
*API of the business service implementation is executed. This advise will
*actually delegate the event actions associated with the Customer Registered
*Event to the appropriate command. This advice inserts custom behavior to
*IRegistrationService interface API's after the API is executed.
*@author - Vigil Bose
*/
public class RegistrationAfterAdvice implements AfterReturningAdvice {
/**
*After returning advice is invoked only on normal method return,
*not if an exception is thrown. Such advice can see the return
*value, but cannot change it.
*
*@param returnValue - the value returned by the method, if any
*@param method - method being invoked
*@param args - arguments to the method
*@param target - target of the method invocation. May be null
*@throws Throwable - if this object wishes to abort the call.
*Any exception thrown will be returned to the caller if it's allowed
*by the method signature. Otherwise the exception will be wrapped as a runtime
*exception
*@see org.springframework.aop.AfterReturningAdvice#afterReturning
*(java.lang.Object, java.lang.reflect.Method, java.lang.Object[],
*java.lang.Object)
*/
public void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {
AbstractData data = (AbstractData)args[0];
User userInfo = (User)data.getUser();
Set eventSet = data.eventSet();
Set keySet = this.commandMap.keySet();
Event event = null;
String actionCodes = null;
String actionCode = null;
//Iterate through the event set
for (Iterator iter = eventSet.iterator(); iter.hasNext();) {
event = (Event) iter.next();
actionCodes = event.getEventActionCodes();
//Loop through the action codes and extract each command mapped to
//the action code in spring application context file.
for (int i=0; i < actionCodes.length();i++){
actionCode = new Character(eventActionCodes.charAt(i)).toString();
command = (ICommand)commandMap.get(actionCode);
if (command != null){
command.execute(userInfo);
}
}
}
}
}
In the example shown above, I could have implemented both event collection and event routing mechanisms in RegistrationAfterAdvice itself. But to keep the responsibilites of event collection and event routing separate, I decided to use two AOP advice components to handle the subsystem routing functionality.
Putting Things Together
Now wire everything together in Spring application context xml file. In the example below, the Hibernate implementation is used as the object relational mapping for data access layer. It is recommended to use JtaTransaction strategy when dealing with multiple resource providers (e.g. Database & JMS). To keep things simple, only local transaction strategy is shown.
Example: applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
<bean id="registrationService" class="RegistrationServiceImpl">
<property name="registrationServiceDao" ref="registrationServiceDao"/>
</beans>
<bean id="registrationServiceDao" class="RegistrationServiceDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</beans>
<bean id="lookUpServiceDao" class="LookUpServiceDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</beans>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"/>
<property> name="dataSource" ref="dataSource"/>
<!-- Use the following property jtaTransactionManager when dealing
with CMT transactions -->
<!-- <property name="jtaTransactionManager" ref="jtaTransactionManager"/>-->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.connection.pool_size">3</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.region_prefix">node1</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>Users.hbm.xml</value>
<value>Address.hbm.xml</value>
</list>
</property>
</bean>
<bean>id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- <bean id="jtaTransactionManager"
class="org.springframework.jndi.JndiObjectFactoryBean"> -->
<!--The JNDI name of TransactionManager published in OC4J Container in 10g-->
<!-- <property name="jndiName"
value="java:comp/pm/TransactionManager"/>
</bean> -->
<!-- Transaction manager that delegates to JTA for ultimate coordinate of transactions -->
<!-- <bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>-->
<aop:config>
<!--Format: execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)-->
<!--The pointcut expression here is the execution of any public method
defined by the IRegistrationService interface-->
<aop:pointcut id="registrationServicePointcut"
expression="execution(public * *..IRegistrationService.*(..))"/>
<!--
Here: applying the advice named "registrationBeforeAdvice" to all methods on classes named RegistrationServiceImpl.
-->
<aop:advisor pointcut-ref="registrationServicePointcut"
advice-ref="registrationBeforeAdvice" order="1"/>
<!--
This definition creates auto-proxy infrastructure based on the given
pointcut, expressed in AspectJ pointcut language. Here: applying the
advice named "registrationServiceTransactionAdvice" to all methods
on classes named RegistrationServiceImpl.-->
<aop:advisor pointcut-ref="registrationServicePointcut"
advice-ref="registrationServiceTransactionAdvice" order="2"/>
<!--
This definition creates auto-proxy infrastructure based on the given
pointcut,expressed in AspectJ pointcut language. Here: applying the
advice named "registrationAfterAdvice" to all methods on
classes named RegistrationServiceImpl.
-->
<aop:advisor pointcut-ref="registrationServicePointcut"
advice-ref="registrationAfterAdvice" order="3"/>
</aop:config>
<!--
Transaction advice definition, based on method name patterns.
Defaults to PROPAGATION_REQUIRED for all methods whose name starts with
"do". By default, the transaction is rolled back for runtime
exceptions including DataAccessException.
-->
<tx:advice id="registrationServiceTransactionAdvice"
transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="do*"/>
</tx:attributes>
</tx:advice>
<bean id="registrationBeforeAdvice" class="RegistraionBeforeAdvice">
<property name="order" value="1"/>
<property name="eventMap" ref="eventMap"/>
<property name="lookUpServiceDao" ref="lookUpServiceDao"/>
</bean>
<bean id="registrationAfterAdvice"
class="RegistrationAfterAdvice">
<property name="order" value="3"/>
<property name="commandMap">
<map>
<entry key="M"><ref bean="mailingCommand"/></entry>
<entry key="T"><ref bean="sendCustomerInfoCommand"/> </entry>
</map>
</property>
</beans>
<bean id="mailingCommand" class="MailingCommandImpl">
<property name="emailService" ref="emailService"/>
</beans>
<bean id="sendCustomerInfoCommand" class="SendCustomerInfoCommandImpl">
<property name="messagingService" ref="messagingService"/>
</beans>
<bean id="messagingService" class="MessagingService">
<property name="jmsTemplate" ref="jmsTemplate"/>
</beans>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="defaultQueueConnectionFactory" />
<property name="defaultDestination" ref="defaultQueueDestination" />
</bean>
<!-- JNDI Lookup configuration for event queue connection factory
used in messaging -->
<jee:jndi-lookup id="defaultQueueConnectionFactory" jndi-name="EVENT_QUEUE"/>
<!-- JNDI Lookup configuration for event queue destination used in messaging -->
<jee:jndi-lookup id="defaultQueueDestination" jndi-name="EVENTQueue"/>
<!-- JNDI Lookup configuration for DataSource in J2EE environments -->
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/userDS"/>
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="localhost"/>
</bean>
<bean id="emailService" class="EmailServicesImpl">
<property name="mailSender" ref="mailSender"/>
</bean>
<!-- creates a java.util.Map instance with values loaded from
the supplied 'sourceMap'.Global Event Map. The values are mapped
in event table. The keys are matched with the business API name-->
<util:map id="eventMap">
<entry key="doRegister">
<value type="java.lang.Integer">1</value></entry>
</util:map>
</beans>
AOP a.k.a Aspect Oriented Programming is relatively a new concept in programming. This technology is complementary to the object oriented technology and delivers more power and separation of concern where object oriented technology is weak.
Conclusion
Separation of concerns is a key principle to developing Service Oriented Architectures. It needs to be applied both at the architectural and implementation level respectively. In this article, we have demonstrated how to separate the cross cutting concern using the principles of Spring framework's dependency injection and aspect oriented programming features. As you have seen in the sample code, using this approach allowed us to minimize the cross-dependencies in the code that handled each concern of the service.
References
Spring Reference Document: http://www.springframework.org/docs/reference/
Disclaimer
Please view the code supplied as more of a how-to, than a certified production ready. If you are using the code, and have any issues, please let me know and I will update them.