Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts
Monday, November 26, 2012 4 comments

How cool is integration testing with Spring+Hibernate

I am guilty of not writing integration testing (At least for database related transactions) up until now. So in order to eradicate the guilt i read up on how one can achieve this with minimal effort during the weekend. Came up with a small example depicting how to achieve this with ease using Spring and Hibernate. With integration testing, you can test your DAO(Data access object) layer without ever having to deploy the application. For me this is a huge plus since now i can even test my criteria's, named queries and the sort without having to run the application.

There is a property in hibernate that allows you to specify an sql script to run when the Session factory is initialized. With this, i can now populate tables with data that required by my DAO layer. The property is as follows;


 <prop key="hibernate.hbm2ddl.import_files">import.sql</prop>

According to the hibernate documentation, you can have many comma separated sql scripts.One gotcha here is that you cannot create tables using the script. Because the schema needs to be created first in order for the script to run. Even if you issue a create table statement within the script, this is ignored when executing the script as i saw it.

Let me first show you the DAO class i am going to test;


 package com.unittest.session.example1.dao;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.unittest.session.example1.domain.Employee;

@Transactional(propagation = Propagation.REQUIRED)
public interface EmployeeDAO {

 public Long createEmployee(Employee emp);
 
 public Employee getEmployeeById(Long id);
}



package com.unittest.session.example1.dao.hibernate;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.unittest.session.example1.dao.EmployeeDAO;
import com.unittest.session.example1.domain.Employee;

public class EmployeeHibernateDAOImpl extends HibernateDaoSupport implements
  EmployeeDAO {

 @Override
 public Long createEmployee(Employee emp) {
  getHibernateTemplate().persist(emp);
  return emp.getEmpId();
 }

 public Employee getEmployeeById(Long id) {
  return getHibernateTemplate().get(Employee.class, id);
 }
}

Nothing major, just a simple DAO with two methods where one is to persist and one is to retrieve. For me to test the retrieval method i need to populate the Employee table with some data. This is where the import sql script which was explained before comes into play. The import.sql file is as follows;



 insert into Employee (empId,emp_name) values (1,'Emp test');

This is just a basic script in which i am inserting one record to the employee table. Note again here that the employee table should be created through the hibernate auto create DDL option in order for the sql script to run. More info can be found here. Also the import.sql script in my instance is within the classpath. This is required in order for it to be picked up to be executed when the Session factory is created.

Next up let us see how easy it is to run integration tests with Spring.


 package com.unittest.session.example1.dao.hibernate;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;

import com.unittest.session.example1.dao.EmployeeDAO;
import com.unittest.session.example1.domain.Employee;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring-context.xml")
@TransactionConfiguration(defaultRollback=true,transactionManager="transactionManager")
public class EmployeeHibernateDAOImplTest {

 @Autowired
 private EmployeeDAO employeeDAO;
 
 @Test
 public void testGetEmployeeById() {
  Employee emp = employeeDAO.getEmployeeById(1L);
  
  assertNotNull(emp);
 }
 
 @Test
 public void testCreateEmployee()
 {
  Employee emp = new Employee();
  emp.setName("Emp123");
  Long key = employeeDAO.createEmployee(emp);
  
  assertEquals(2L, key.longValue());
 }

}


A few things to note here is that you need to instruct to run the test within a Spring context. We use the SpringJUnit4ClassRunner for this. Also the transction attribute is set to defaultRollback=true. Note that with MySQL, for this to work, your tables must have the InnoDB engine set as the MyISAM engine does not support transactions.

And finally i present the spring configuration which wires everything up;


 <?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="  
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd  
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">


 <context:component-scan base-package="com.unittest.session.example1" />
 <context:annotation-config />

 <tx:annotation-driven />

 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="packagesToScan">
   <list>
    <value>com.unittest.session.example1.**.*</value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
    <prop key="hibernate.connection.url">jdbc:mysql://localhost:3306/hbmex1</prop>
    <prop key="hibernate.connection.username">root</prop>
    <prop key="hibernate.connection.password">password</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <!-- -->
    <prop key="hibernate.hbm2ddl.auto">create</prop>
    <prop key="hibernate.hbm2ddl.import_files">import.sql</prop>
   </props>
  </property>
 </bean>

 <bean id="empDAO"
  class="com.unittest.session.example1.dao.hibernate.EmployeeHibernateDAOImpl">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

 <bean id="transactionManager"
  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

</beans>

That is about it. Personally i would much rather use a more light weight in-memory database such as hsqldb in order to run my integration tests.

Here is the eclipse project for anyone who would like to run the program and try it out.


Wednesday, December 1, 2010 6 comments

Plugin Based Architecture With Spring Integration

Introduction :

                                The purpose of this article is to demonstrate that it is possible to achieve a pluggable architecture using Spring Integration and the patterns it supports. If i were to give an introduction to Spring Integration it is a fairly new addition to the spring's solutions suite. It implements most of the Enterprise Integration Patterns currently known which makes it easier for developers as they do not need to re-invent the wheel. Some of the solutions provided by Spring Integration are as follows;

  1. Router
  2. Transformer
  3. Gateway
  4. Splitter
There are many more. As i am just getting my feet wet with Spring integration this is all i have covered up to now.

Pre-requisites :
 In order to run this example you need the following jar files;
  1. com.springsource.org.aopalliance-1.0.0.jar
  2. commons-logging-1.1.1.jar
  3. spring-aop-3.0.3.RELEASE.jar
  4. spring-asm-3.0.3.RELEASE.jar
  5. spring-beans-3.0.3.RELEASE.jar
  6. spring-context-3.0.3.RELEASE.jar
  7. spring-context-3.0.5.RELEASE.jar
  8. spring-context-support-3.0.3.RELEASE.jar
  9. spring-core-3.0.3.RELEASE.jar
  10. spring-expression-3.0.3.RELEASE.jar
  11. spring-tx-3.0.3.RELEASE.jar
  12. spring-integration-core-2.0.0.RC2.jar 

Note that i have used Spring 3.0.3 for this project. If you are using Spring 2.0 the required jars will be less. But as i used Spring Integration 2.0 i wanted to go with Spring 3.0.

Proposed Solution :
I have used the banking domain to demonstrate my example. The solution is to develop a system which will allows you to make payments to any banking system. The architecture is such that all code interfacing to any banking system is developed independently which can later be integrated to the main application as an when required. And you just have to inject the respective spring-integration config xml along with the plugin developed which can be injected to the project. 

In this solution i have done it in the same code base but in real life the plugin development should be in a different module.

Implmentation :
First let me give you an overview diagram of the proposed solution;

   

As you can see this is a typical architecture for a j2EE project. The controller i have specified here can be anything from struts to JSF to Spring MVC. The Service layer is basically the Spring layer which the controller will mainly be in contact with.

The main point to note is the spring integration layer. This is what injects all the plugins in the plugin repository. Next i will explain in detail the patterns of spring integration used in the solution. The following digram clearly explains this;


I will not go into detail on this digram as the diagram it self is self explanatory. So now lets get our hands dirty with some code;

First off i will start with the service layer;

package com.paymentgateway.services;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;

/**
 * The service interface is what the client from our application interacts with
 * the client is not aware of spring integration being used.
 * 
 * @author dinuka
 */
public interface PaymentService {

    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO);

}


package com.paymentgateway.services;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.message.GenericMessage;
import org.springframework.stereotype.Component;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;
import com.paymentgateway.dto.PaymentStatusCode;
import com.paymentgateway.dto.SystemActions;
import com.paymentgateway.gateway.CentralPaymentGateway;

@Component("paymentService")
public class PaymentServiceImpl implements PaymentService {

    @Autowired
    private CentralPaymentGateway gateway;

    @Override
    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO) {
        /**
         * Here you can do any validation checks for null values if you need
         * and throw any relevant exception as needed. For simplicity purposes
         * i have not done so here.
         */

        /**
         * In the header we specify the banking system this message needs to be routed to<br>
         * Then in the
         */
        Map headerMap = new HashMap();
        headerMap.put("BANKING_SYSTEM", paymentRequestDTO.getBankingSystem());
        headerMap.put("ACTION", SystemActions.PAYMENT.toString());
        GenericMessage<PaymentRequestDTO> paymentRequestMsg = new GenericMessage<PaymentRequestDTO>(paymentRequestDTO,
                headerMap);
        PaymentResponseDTO paymentResponseDTO = gateway.makePayment(paymentRequestMsg);

        if (paymentResponseDTO.getStatusCode() == PaymentStatusCode.FAILURE) {
            /**
             * Throw relevant exception
             */
        }
        return paymentResponseDTO;

    }

}


And the DTOs used are as follows;



package com.paymentgateway.dto;

import java.io.Serializable;

/**
 * This DTO holds the data that needs to be passed to the
 * relevant plugin in order to make a payment
 * 
 * @author dinuka
 */
public class PaymentRequestDTO implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 582470760696219645L;

    /**
     * The account number of the customer
     */
    private String accountNumber;

    /**
     * The amount needed to be reduced
     */
    private Double deductAmount;

    /**
     * The First Name of the customer
     */
    private String firstName;

    /**
     * The Last Name of the customer
     */
    private String lastName;

    /**
     * This should ideally be moved to a CommonDTO as this will be reused by all
     * subsequent DTOs. Default banking system is "abc". The client needs to set
     * which banking system is needed to connect to.
     */
    private String bankingSystem = "abc";

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public Double getDeductAmount() {
        return deductAmount;
    }

    public void setDeductAmount(Double deductAmount) {
        this.deductAmount = deductAmount;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getBankingSystem() {
        return bankingSystem;
    }

    public void setBankingSystem(String bankingSystem) {
        this.bankingSystem = bankingSystem;
    }

    @Override
    public String toString() {
        return "PaymentRequestDTO [accountNumber=" + accountNumber + ", deductAmount=" + deductAmount + ", firstName="
                + firstName + ", lastName=" + lastName + "]";
    }

}


package com.paymentgateway.dto;

import java.io.Serializable;

/**
 * This is the default payment response DTO that every plugin
 * must return back to the system
 * 
 * @author dinuka
 */
public class PaymentResponseDTO implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 2773607380706313950L;

    /**
     * The account number of the customer
     */
    private String accountNumber;

    /**
     * The first name of the customer
     */
    private String firstName;

    /**
     * The last name of the customer
     */
    private String lastName;

    /**
     * The remaining balance in the account of the customer
     */
    private Double availableBalance;

    /**
     * The balance reduced from the customer account
     */
    private Double reducedBalance;

    /**
     * The status code indicating whether the transaction was a success or not
     */
    private PaymentStatusCode statusCode = PaymentStatusCode.SUCCESS;

    /**
     * The transaction id assigned to the relevant transaction
     */
    private Long transationId;

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Double getAvailableBalance() {
        return availableBalance;
    }

    public void setAvailableBalance(Double availableBalance) {
        this.availableBalance = availableBalance;
    }

    public Double getReducedBalance() {
        return reducedBalance;
    }

    public void setReducedBalance(Double reducedBalance) {
        this.reducedBalance = reducedBalance;
    }

    public PaymentStatusCode getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(PaymentStatusCode statusCode) {
        this.statusCode = statusCode;
    }

    public Long getTransationId() {
        return transationId;
    }

    public void setTransationId(Long transationId) {
        this.transationId = transationId;
    }

    @Override
    public String toString() {
        return "PaymentResponseDTO [accountNumber=" + accountNumber + ", firstName=" + firstName + ", lastName="
                + lastName + ", availableBalance=" + availableBalance + ", reducedBalance=" + reducedBalance
                + ", statusCode=" + statusCode + ", transationId=" + transationId + "]";
    }

}



package com.paymentgateway.dto;

/**
 * The status codes returned from each plugin indicating
 * if the transaction was a success or not
 * 
 * @author dinuka
 */
public enum PaymentStatusCode {

    SUCCESS, FAILURE
}



package com.paymentgateway.dto;

import com.paymentgateway.util.PaymentRouter;

/**
 * This enum defines the system wide actions
 * We use this name in our {@link PaymentRouter}
 * to decide which channel to route the message
 * 
 * @author dinuka
 */
public enum SystemActions {

    PAYMENT {
        @Override
        public String toString() {

            return "Payment";
        }
    }
}



Those are the DTOs i have used. Moving on, as the second diagram above specified we have defined a Central Gateway & A Router. So lets see how we have implemented those using spring integration;

package com.paymentgateway.gateway;

import org.springframework.integration.message.GenericMessage;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;

/**
 * This interface represents the common gateway which
 * will be used by Spring Integration to wire up the plugins
 * and also will be the central and first point of contact
 * by any client calling our business layer
 * 
 * @author dinuka
 */
public interface CentralPaymentGateway {

    /**
     * This method takes a parameter type of {@link GenericMessage} which wraps&lt;br&gt;
     * an instance of {@link PaymentRequestDTO}. Usage of sending an instance of&lt;br&gt;
     * Generic Message is so that we can add header values which can indicate&lt;br&gt;
     * which banking system to call to
     * 
     * @param paymentRequestDTO
     * @return
     */
    public PaymentResponseDTO makePayment(GenericMessage&lt;PaymentRequestDTO&gt; paymentRequestDTO);
}

Note that the gateway is just an interface defining our input parameters. We have used the GenericMessage defined by Spring integration. If you go back to the service layer implementation you can see that we have populated an instance of GenericMessage with the relevant DTO which is passed onto the gateway. The gateway here acts as a mediation layer.

Moving on with the Router implementation;

package com.paymentgateway.util;

import org.springframework.integration.Message;

/**
 * This is the base Router for All payment related functions
 * We route the message based on the banking system and the action
 * which comes in the header of the message. Ofcourse we can enhance this
 * to put the message on an error queue if the {@link Message} does not have the
 * relevant header values.
 * 
 * @author dinuka
 */
public class PaymentRouter {

    public String resolveBankChannel(Message message) {
        return (String) message.getHeaders().get("BANKING_SYSTEM") + (String) message.getHeaders().get("ACTION")
                + "Channel";
    }
}


Again if you go back to the PaymentServiceImpl class you can see we set the two headers BANKING_SYSTEM and ACTION. The router decides which channel this message should go on. You can see this in the next section when we wire up all this together.

The Spring configurations are as follows;

First off i present to you the main config file named context-config.xml. This mainly injects the service layer beans.

<?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 <context:component-scan base-package="com.paymentgateway.services" />
 <context:annotation-config />
</beans>


Next we look at the core configuration where we wire up the Spring Integration related components;

spring-integration-config.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:int="http://www.springframework.org/schema/integration"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd">

 <!-- The generic input channel which would be used to pass through all messages 
  coming into the Central Gateway -->
 <int:channel id="inputChannel"></int:channel>

 <!-- Here we wire up the Central Gateway which is the central point of access 
  from our service layer -->
 <int:gateway id="gateway" default-request-channel="inputChannel"
  service-interface="com.paymentgateway.gateway.CentralPaymentGateway"
  default-reply-channel="outputChannel"></int:gateway>



 <!-- This is the generic Output channel which will be used by to send the 
  output from any plugin. -->
 <int:channel id="outputChannel"></int:channel>

 <!-- The router is the one who decides which channel to send the message 
  passed in from input channel into. The client should send the name of the 
  Banking system where by 'SearchChannel' keyword is appended by the defaultRouter 
  bean. -->
 <int:router id="centralRouter" ref="defaultRouter" method="resolveBankChannel"
  input-channel="inputChannel"></int:router>

 <bean id="defaultRouter" name="defaultRouter"
  class="com.paymentgateway.util.PaymentRouter" />

 
 
</beans>


That is the core configuration which wires up the Gateway, Router and defines the Channels required by the application. Next off lets go into our plugin( of many plugins to come) the ABC Bank Plugin.



First we define the Base plugin interface which all plugin developers should adhere to;


package com.paymentgateway.plugins;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;

/**
 * This is the base plugin interface. All plugin developers should adhere to<br>
 * this interface when they write new plugins connecting to different banking<br>
 * systems.
 * 
 * @author dinuka
 */
public interface BasePlugin {

    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO);

}


And the implementation of this interface is as follows;

package com.paymentgateway.plugins;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;
import com.paymentgateway.dto.PaymentStatusCode;

/**
 * This is the plugin used to connect to the ABC banking system
 * in order to do the payment transaction.
 * 
 * @author dinuka
 */
public class ABCBankPlugin implements BasePlugin {

    @Override
    /**
     * Right now we just return a mock value. But when the true implementation
     * comes you will deal with any connection rellated information
     * at this point.
     */
    public PaymentResponseDTO makePayment(PaymentRequestDTO paymentRequestDTO) {
        PaymentResponseDTO paymentResponseDTO = new PaymentResponseDTO();
        paymentResponseDTO.setAccountNumber("abc123");
        paymentResponseDTO.setAvailableBalance(10000d);
        paymentResponseDTO.setFirstName("Dinuka");
        paymentResponseDTO.setLastName("Arseculeratne");
        paymentResponseDTO.setReducedBalance(500d);
        paymentResponseDTO.setStatusCode(PaymentStatusCode.SUCCESS);
        paymentResponseDTO.setTransationId(1233424234l);
        return paymentResponseDTO;
    }

}


As this is just a mock implementation i have just returned the response DTO with values filled. Now that we have developed our plugin lets wire it up;



abc_bank_plugin-config.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:int="http://www.springframework.org/schema/integration"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd">

 <!-- Start of ABC Banking System Plugin Injection -->

 <!-- This is the payment channel used for the ABC banking system -->
 <int:channel id="abcPaymentChannel"></int:channel>

 <!-- Wire up the ABC Banking plugin -->
 <bean id="abcBakingSysPlugin" name="abcBakingSysPlugin"
  class="com.paymentgateway.plugins.ABCBankPlugin" />

 <!-- This service activator is used to handle the payment response from 
  ABC banking system -->
 <int:service-activator input-channel="abcPaymentChannel"
  ref="abcBakingSysPlugin" method="makePayment" output-channel="outputChannel"></int:service-activator>

 <!-- End of ABC Banking System Plugin Injection -->
</beans>

And lastly i present a test class just so that you can run the solution given above;

package com.paymentgateway.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.paymentgateway.dto.PaymentRequestDTO;
import com.paymentgateway.dto.PaymentResponseDTO;
import com.paymentgateway.services.PaymentService;

/**
 * This is a test class showing how it all comes together
 * 
 * @author dinuka
 */
public class TestBankingApp {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("context-config.xml",
                "spring-integration-config.xml","abc_bank_plugin-config.xml");

        PaymentService paymentService = (PaymentService) context.getBean("paymentService");

        PaymentRequestDTO paymentRequestDTO = new PaymentRequestDTO();
        PaymentResponseDTO paymentResponseDTO = paymentService.makePayment(paymentRequestDTO);

        /**
         * We just print out the resulting DTO returned from the plugin<br>
         * as this is just a tutorial
         */
        System.out.println(paymentResponseDTO);
    }
}


Thats it. Your done with your plugin architecture. If you ever do develop another plugin all you have to do is implement the BasePlugin Interface and as well as give the wiring up spring file. The following diagram explains the flow in which the message travels which will give you an even clearer picture of what we have accomplished;


Future Enhancements:
  1. Implement a transformer pattern which will do the conversion of DTOs to application specific DTOs.
  2. Introduce an error channel where any error populated will be put in to. 

Thats it guys. Your comments and suggestions are most welcome.

References:

[1] http://static.springsource.org/spring-integration/docs/2.0.0.RELEASE/reference/htmlsingle/
Wednesday, June 16, 2010 0 comments

Defining Custom Editors With Spring

Custom editors gives you a lot of flexibility and reduces XML configuration when defining your spring beans. Say for example i have the following Person object;


public class Person {

private String firstName;

private String lastName;

private int age;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}


}


And the following Class called PersonManager is using this Person object as follows;


public class PersonManager {

private Person person;

public Person getPerson() {
return person;
}

public void setPerson(Person person) {
this.person = person;
}


}


If you wanted to inject a person object to the PersonManager class then you would first register the Person class as a spring bean and register that bean as a ref bean of PersonManager class.

But spring offers an easy way of injecting the Person object to the PersonManager by use of Custom PropertyEditors. With this you can define your values as a string value and create the Person object as you deem appropriate from the format you define. In my example i have defined the format to be;
firstname,lastname,age
hence is would be comma separated values which defined the values needed to create the Person object. Now in order to let spring understand how to handle this string value you need to define your own propertye editor. It is as follows;






public class PersonPropertyEditor extends PropertyEditorSupport{

@Override
public void setAsText(String arg0) throws IllegalArgumentException {

String[]vals = arg0.split(",");
Person p = new Person();
p.setFirstName(vals[0]);
p.setLastName(vals[1]);
p.setAge(Integer.valueOf(vals[2]));
setValue(p);
}
}



Here i simply split the string value and set the relevant values of the Person object and then call the setValue inherited method to set the value. Its as simple as that.

And finally you define your customer registar which binds your customer editor.


public class PersonConfiguratorWithRegistars implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry arg0) {
arg0.registerCustomEditor(Person.class, new PersonPropertyEditor());

}
}



Now when letting spring know about your custom editor there were two ways initially before Spring 3.0. They are;

  1. Define as a customEditors property within org.springframework.beans.factory.config.CustomEditorConfigurer
  2. Define as a propertyEditorRegistrars within org.springframework.beans.factory.config.CustomEditorConfigurer
As of Spring 3.0 the customerEditors property is deprecated as they had a thread locking issue as explained here.

So lastly i show you how to wire your new customer editor and the PersonManager object by providing a string value. The application context XML is as follows;






<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--

This is the OLD way of wiring your custom editor which is not thread safe

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="Person">
<bean id="personEditor" class="PersonPropertyEditor">
</bean>
</entry>
</map>
</property>
</bean>

-->

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="customPersonPropertyEditor" />
</list>
</property>
</bean>

<bean id="customPersonPropertyEditor"
class="PersonConfiguratorWithRegistars" />

<bean id="perMgt" class="PersonManager">
<property name="person" value="John,Alan,23" />
</bean>


</beans>



Thats it. You can use the following test class to test the same;


public class PropertyEditorTest {

/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("appContext.xml");
PersonManager personMgr = (PersonManager)appContext.getBean("perMgt");
Person p = personMgr.getPerson();
System.out.println(p.getFirstName()+" "+p.getLastName()+" "+p.getAge());
}

}

Friday, May 21, 2010 0 comments

Spring - How to use Component and Autowired

Just put together a sample which made me understand how to use the above mentioned annotations defined by Spring hence thought i should blog about the same.

First lets take a look at the two classes that are exposed as spring beans.





package chapter3;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("calculator")
public class Calculator {
private Adder adder;

@Autowired
public void setAdder(Adder adder) {
this.adder = adder;
}

public void makeAnOperation(){
int r1 = adder.add(1,2);
System.out.println("r1 = " + r1);
}
}




package chapter3;

import org.springframework.stereotype.Component;

@Component("adder")
public class Adder {
public int add(int a, int b){
return a + b;
}
}

Few pointers to note here are as follows;

  • By annotation the class with @Component you tell the Spring container that these beans are needed to be instantiated and managed by the Spring container. This reduces XML configurations required because you do not need to manually edit the XML file entering bean definitions to each and every bean.
  • The @Autowired element binds an instance of the Adder class when the Spring container scans the Calculator class.
Next we look at the application context 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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scan base-package="chapter3"/>
<context:annotation-config/>

</beans>


And finally to run the sample following is the main class;



package chapter3;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ComponentCheck {

public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
"chapter3AppXML.xml");
Calculator calculator = (Calculator) appContext.getBean("calculator");
calculator.makeAnOperation();

}

}

Tuesday, September 29, 2009 11 comments

Spring Mail API Wrapper

Recently it was needed at my working place to have a module to send SMTP mails. I found out that Spring provides a nice wrapper around the Java Mail API so you do not need to deal with boilerplate code. Hence after a few hours of Googling and coding I was able to come up with an API which sends mail using Spring. Following I give you the code snippets required.


package com.test.commons.mailservices.core.remoting.ejb.service;

import com.test.commons.mailservices.core.exceptions.MailSenderException;

public interface MailSenderService {

/**
* This method sends the mail to multiple recipients with the given subject
* @param mailMsg The message needed to be send as plain text
* @param recipientAddresses The mail addresses in an array to which the mail has to be sent
* @param subject The subject of the mail to be sent
* @throws MailSenderException this exception wraps the MailException throw by the Spring framework
*/
public void sendMessage(String mailMsg,String[] recipientAddresses,String subject)throws MailSenderException;

/**
* This method sends the mail to a single recipient with the given subject
* @param mailMsg The message needed to be send as plain text
* @param recipientAddresses The mail addresses in an array to which the mail has to be sent
* @param subject The subject of the mail to be sent
* @throws MailSenderException this exception wraps the MailException throw by the Spring framework
*/
public void sendMessage(String mailMsg,String recipientAddress,String subject)throws MailSenderException;

/**
* This method sends the mail to multiple recipients which are given as comma separated values, with the given subject
* @param mailMsg The message needed to be send as plain text
* @param recipientAddresses The mail addresses in an array to which the mail has to be sent
* @param subject The subject of the mail to be sent
* @throws MailSenderException this exception wraps the MailException throw by the Spring framework
*/
public void sendMessageWithCommaSeparatedMailAddresses(String mailMsg,String recipientAddress,String subject)throws MailSenderException;
}

This is the main contract which any calling party can use to send mails. The method is overloaded so that clients can call with different functionality. Next I show you the implmentation of this interface as well as the exception class that i have defined which wraps the MailException thrown by Spring. I wrapped it so that any calling party only needs to know about the MailSending module and not about Spring exceptions.


package com.test.commons.mailservices.core.remoting.ejb.bl;

import org.apache.log4j.Logger;
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;

import com.test.commons.mailservices.core.exceptions.MailSenderException;
import com.test.commons.mailservices.core.remoting.ejb.service.MailSenderService;

/**
* This is the Main class involved in sending email message to the outside world<br>
* The class uses the Spring Framework which wraps the java mail API to send mails.
* Common functionality of obtaining a SimpleMailMessage object is given by the parent<br>
* class named MailSenderCommon. The instance variables of the class are instantiated with<br>
* through a spring config. The spring config file responsible is mailconfig.spring.xml found <br>
* under the resources directory.
*
* @author dinuka
*
*/
public class MailSenderImpl extends MailSenderCommon implements MailSenderService {

private static final Logger log = Logger.getLogger(MailSenderImpl.class);

private MailSender mailSender;

private String fromAddress;

public MailSenderImpl() {

}

/**
* {@inheritDoc}
*/
public void sendMessage(String mailMsg, String[] recipientAddresses, String subject)throws MailSenderException {
if (mailMsg == null || recipientAddresses == null) {
log.error("Input parameters received for sendMessage(String,String[]) are null");
}

sendEmail(mailMsg, subject, recipientAddresses);

}

/**
* {@inheritDoc}
*/
public void sendMessage(String mailMsg, String recipientAddress, String subject)throws MailSenderException {
if (mailMsg == null || recipientAddress == null) {
log.error("Input parameters received for sendMessage(String,String) are null");
throw new IllegalArgumentException("Received Input Parameters Are Null");
}
sendEmail(mailMsg.trim(), subject, recipientAddress);
}

/**
* {@inheritDoc}
*/
public void sendMessageWithCommaSeparatedMailAddresses(String mailMsg, String recipientAddress, String subject)throws MailSenderException {

if (mailMsg == null || recipientAddress == null) {
log
.error("Input parameters received for sendMessageWithCommaSeparatedMailAddresses(String,String) are null");
throw new IllegalArgumentException("Received Input Parameters Are Null");
}
sendEmail(mailMsg, subject, recipientAddress.split(","));

}

public MailSender getMailSender() {
return mailSender;
}

public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}

public String getFromAddress() {
return fromAddress;
}

public void setFromAddress(String fromAddress) {
this.fromAddress = fromAddress;
}

private void sendEmail(String mailMsg, String subject, String... address)throws MailSenderException {

try {
mailSender.send(getSimpleMessage(mailMsg, fromAddress, subject, address));
} catch (MailException ex) {
log.error("MailSenderImpl: sendMessage() Exception occured" + ex.getMessage());
throw new MailSenderException(ex.getMessage());
}
}

}





package com.test.commons.mailservices.core.remoting.ejb.bl;

import java.util.Date;

import org.springframework.mail.SimpleMailMessage;

public class MailSenderCommon {

/**
* This is the common method which creates a simple message object which is used to send mails out
* Note that var arg is used as the second parameter to facilitate String[] and normal String object parsing
* to the same method. Also please note that you should always keep the var arg parameter as the last parameter in
* this method as the specification requires it.
* @param mailMsg
* @param addresses
* @return
*/
protected SimpleMailMessage getSimpleMessage(final String mailMsg, final String fromAddress,String subject,final String... addresses) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSentDate(new Date());
message.setTo(addresses);
message.setFrom(fromAddress);
message.setSubject(subject);
message.setText(mailMsg);


return message;
}

}




package com.test.commons.mailservices.core.exceptions;

public class MailSenderException extends Exception{

/**
*
*/
private static final long serialVersionUID = -6281925344129197510L;

private String message;





public MailSenderException(String message){
this.message = message;
}

@Override
public String getMessage() {
return message;
}
}



Then we have the SMTP Authenticator class which is used for mail authentication. It is as follows;


package com.jkcs.commons.mailservices.core.remoting.ejb.bl;

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

public class SmtpAuthenticator extends Authenticator {
private String username;
private String password;

public SmtpAuthenticator(String username, String password) {
super();
this.username = username;
this.password = password;
}

public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}

}


And finally you the following spring xml shows how to configure and integrate all this together.



<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


<!-- our Authenticator implementation -->
<bean id="smtpAuthenticator"
class="com.test.commons.mailservices.core.remoting.ejb.bl.SmtpAuthenticator">
<constructor-arg>
<value>${outgoing.mail.server.userid}</value>
</constructor-arg>
<constructor-arg>
<value>${outgoing.mail.server.password}</value>
</constructor-arg>

</bean>

<!-- now setup an authenticated session -->
<bean id="mailSession" class="javax.mail.Session"
factory-method="getInstance">
<constructor-arg>
<props>
<prop key="mail.smtp.auth">true</prop>
<!-- <prop key="mail.smtp.socketFactory.port">465</prop>
<prop key="mail.smtp.socketFactory.class">
javax.net.ssl.SSLSocketFactory
</prop>
<prop key="mail.smtp.socketFactory.fallback">
false
</prop> -->
</props>
</constructor-arg>
<constructor-arg ref="smtpAuthenticator" />
</bean>


<bean id="mailService" class="com.test.commons.mailservices.core.remoting.ejb.bl.MailSenderImpl">
<property name="mailSender" ref="mailSender"/>
<property name="fromAddress">
<value>${outgoing.mail.server.userid}</value>
</property>
</bean>

<!-- Mail service -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host">
<value>${outgoing.mail.server.ip}</value>
</property>
<property name="port">
<value>${outgoing.mail.server.port}</value>
</property>
<property name="username">
<value>${outgoing.mail.server.userid}</value>
</property>
<property name="password">
<value>${outgoing.mail.server.password}</value>
</property>
<property name="session" ref="mailSession" />
<property name="javaMailProperties">
<props>
<!-- Use SMTP-AUTH to authenticate to SMTP server -->
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.sendpartial">true</prop>
<!-- Use TLS to encrypt communication with SMTP server -->
<!-- <prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.sendpartial">true</prop> -->

</props>
</property>
</bean>

</beans>


As you can see i have used parameters as ${var_name}. This is because i have used Spring property file loading mechanism. Hence those variables are taken from a .property file. To configure that use the following;


<bean id="PropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!--
The order of these properties files is important, as properties
will override one another
-->
<value>xxx.properties</value>
<value>mail.properties</value>
</list>
</property>
</bean>


Thats about it. Hope this would be helpful to someone who is looking to do the same.
 
;