Thursday, July 31, 2014

Configure encrypted password in data-source inside spring configuration file

In prod env, generally password need to be encrypted before put to spring configure file. There are 3 different approaches to achieve it.

1) use jasypt

refer to http://www.jasypt.org/

but it does not support AES algorithm

2) use java based configuration

look like this

 @Configuration
 public class AppConfig {

 }

3) use wrapper class to extends org.springframework.jdbc.datasource.DriverManagerDataSource


  • jdbc.properties to configure jdbc

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/testdb2
jdbc.username=tester2
jdbc.password=45236fe16c32d37d8bdac1f53a75cde9

take note the password is encrypted with AES
  • key.properties to configure key/iv to encrypt/decrypt password
key.hex=5b518a1640d253003626a1200940eb21
iv.hex=fd9e24303da422dd9e243ac4d03da443

  • spring configuration
<?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"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>

<tx:annotation-driven transaction-manager="txManager" />

<!-- load properties files -->
<context:property-placeholder location="classpath:jdbc.properties,key.properties" />

<bean id="dataSource" class="wx.jpa2.dao.SecureDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>

<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="wx.jpa2.entity" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>

<context:component-scan base-package="wx.jpa2" />

</beans>

  • wrapper class
package wx.jpa2.dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;

import wx.jpa2.util.Crypto;

@Component
public class SecureDataSource extends DriverManagerDataSource {

private String url;
private String username;
private String password;
private @Value("${key.hex}")
String keyHex;
private @Value("${iv.hex}")
String ivHex;

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getKeyHex() {
return keyHex;
}

public void setKeyHex(String keyHex) {
this.keyHex = keyHex;
}

public String getIvHex() {
return ivHex;
}

public void setIvHex(String ivHex) {
this.ivHex = ivHex;
}

public SecureDataSource() {

}

@Override
protected Connection getConnectionFromDriver(Properties props)
throws SQLException {

props.setProperty("username", username);
props.setProperty("password", getDecryptedPassword());
return getConnectionFromDriverManager(url, props);
}

private String getDecryptedPassword() {
// use key/iv to decrypt the encrypted password via AES
                       Crypto aes = new Crypto();
return aes.decrypt(keyHex, ivHex, password);
}
}

Friday, July 25, 2014

JPA + Spring + hibernate Integration with JPA DAO Pattern

The target is to use JPQL in the code, use hibernate as underlying jpa provider, and use spring to manage transaction.


  • This is the project structure:





















  • pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>wx</groupId>
<artifactId>jpa1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>jpa1</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.2.14.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0.1</version>
</dependency>

</dependencies>
</project>

  • spring configuration file 
<?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"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>

<tx:annotation-driven transaction-manager="txManager" />

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/testdb1" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>

<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="wx.jpa1.entity" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>

<context:component-scan base-package="wx.jpa1" />

</beans>

----- persistence.xml is not required since spring v3.1
----- use spring container to manage transaction propagation
----- use org.springframework.orm.jpa.JpaTransactionManager
  • create database and table in mysql
create database testdb1;

use testdb1;

create table student(
stud_id int not null auto_increment,
name varchar(50) not null,
age int,
depart varchar(20),
primary key (stud_id)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

  • entity class with jpa annotation
package wx.jpa1.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * student entity object
 * 
 */
@Entity
@Table(name = "student")
public class StudentEO implements Serializable {

private static final long serialVersionUID = 1492053620157953609L;
private Long studId;
private String name;
private int age;
private String depart;

/*
* As an entity object, the default constructor is a must.
*/
public StudentEO() {

}

@Id
@Column(name = "stud_id")
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getStudId() {
return studId;
}

public void setStudId(Long studId) {
this.studId = studId;
}

@Column(name = "name")
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Column(name = "age")
public int getAge() {
return age;
}

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

@Column(name = "depart")
public String getDepart() {
return depart;
}

public void setDepart(String depart) {
this.depart = depart;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}

}

  • DAO tier

------------interface IGenericDao ------------

package wx.jpa1.dao;

import java.io.Serializable;
import java.util.List;

public interface IGenericDao<T, PK extends Serializable> {

public T find(PK id);

public void save(T entity);

public T update(T entity);

public void delete(PK id);

public List<T> findAll();

public List<T> findByNamedQuery(String queryName, Object... objects);

}

---------------- implement above interface with jpa ----------

package wx.jpa1.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class GenericDaoJpaImpl<T, PK extends Serializable> implements
IGenericDao<T, PK> {

protected Class<T> entityClass;

@PersistenceContext
protected EntityManager entityManager;

@SuppressWarnings("unchecked")
public GenericDaoJpaImpl() {
if (entityClass == null) {
this.entityClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
}

public T find(PK id) {
return this.entityManager.find(entityClass, id);
}

public void save(T entity) {
this.entityManager.persist(entity);
}

public T update(T entity) {
return this.entityManager.merge(entity);
}

public void delete(PK id) {
this.entityManager.remove(this.entityManager.getReference(entityClass,
id));
}

@SuppressWarnings("unchecked")
public List<T> findAll() {
return this.entityManager.createQuery(
"select h from " + this.entityClass.getName() + " h")
.getResultList();
}

public List<T> findByNamedQuery(String queryName, Object... objects) {
// TODO Auto-generated method stub
return null;
}
}

---------------- for particular DAO interface----
package wx.jpa1.dao;

import java.util.List;

import wx.jpa1.entity.StudentEO;

public interface IStudentDao extends IGenericDao<StudentEO, Long> {

public List<StudentEO> findByDepart(String depart);
}

---------------- implement above dao interface -------

package wx.jpa1.dao;

import java.util.List;

import org.springframework.stereotype.Repository;

import wx.jpa1.entity.StudentEO;

@Repository("studentDao")
public class StudentDaoImpl extends GenericDaoJpaImpl<StudentEO, Long>
implements IStudentDao {

public List<StudentEO> findByDepart(String depart) {
// TODO
return null;
}

}

  • Service tier
------------- service interface -----------------
package wx.jpa1.service;

import java.util.List;

import wx.jpa1.entity.StudentEO;

public interface IStudentService {
public void registerStudent(StudentEO student);
public List<StudentEO> findAll();
}

------------- implement above interface -------------

package wx.jpa1.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import wx.jpa1.dao.IStudentDao;
import wx.jpa1.entity.StudentEO;

@Service("studentService")
@Transactional
public class StudentServiceImpl implements IStudentService {

@Autowired
@Qualifier("studentDao")
private IStudentDao studentDao;

public IStudentDao getStudentDao() {
return studentDao;
}

public void setStudentDao(IStudentDao studentDao) {
this.studentDao = studentDao;
}

public void registerStudent(StudentEO student) {
studentDao.save(student);
}

public List<StudentEO> findAll() {
return studentDao.findAll();
}

}


-------------------- junit test -----------------

package wx.jpa1;

import java.util.List;

import org.junit.After;
import org.junit.Before;
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.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import wx.jpa1.entity.StudentEO;
import wx.jpa1.service.IStudentService;

@RunWith(org.springframework.test.context.junit4.SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:app-context.xml")
@Transactional
@TransactionConfiguration(defaultRollback=false)
public class IStudentServiceTest {

@Autowired
IStudentService studentService;

@Before
public void setUp() throws Exception {

}

@After
public void tearDown() throws Exception {
studentService = null;
}

@Test
public void testRegisterStudent() {
StudentEO student = new StudentEO();
student.setAge(20);
student.setDepart("Computer");
student.setName("Adam");
studentService.registerStudent(student);
}
@Test
public void testQueryStudent() {
List<StudentEO> result = studentService.findAll();
System.out.println(result);
}
}

-------------- test result ------

mysql> select * from student;
+-------------+----------------------------------------------------+-------------+----------------------+
| stud_id     | name                                               | age         | depart               |
+-------------+----------------------------------------------------+-------------+----------------------+
|           5 | Adam                                               |          20 | Computer             |
|           6 | Adam                                               |          20 | Computer             |
|           7 | Adam                                               |          20 | Computer             |
|           8 | Adam                                               |          20 | Computer             |
|          11 | John                                               |          20 | Computer             |
+-------------+----------------------------------------------------+-------------+----------------------+
5 rows in set (0.00 sec)

Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@1b6aa42e]; rollback [false]
Hibernate: 
    select
        studenteo0_.stud_id as stud_id1_0_,
        studenteo0_.age as age2_0_,
        studenteo0_.depart as depart3_0_,
        studenteo0_.name as name4_0_ 
    from
        student studenteo0_
[wx.jpa1.entity.StudentEO@13ef45e0[studId=5,name=Adam,age=20,depart=Computer], wx.jpa1.entity.StudentEO@2d0c94a7[studId=6,name=Adam,age=20,depart=Computer], wx.jpa1.entity.StudentEO@14f3770c[studId=7,name=Adam,age=20,depart=Computer], wx.jpa1.entity.StudentEO@7c0cbf92[studId=8,name=Adam,age=20,depart=Computer]]
Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [TestContext@22dca7d0 testClass = IStudentServiceTest, testInstance = wx.jpa1.IStudentServiceTest@448d5a91, testMethod = testQueryStudent@IStudentServiceTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1c3508c0 testClass = IStudentServiceTest, locations = '{classpath:app-context.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (2): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@1b6aa42e]; rollback [false]
Hibernate: 
    insert 
    into
        student
        (age, depart, name) 
    values
        (?, ?, ?)
Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [TestContext@22dca7d0 testClass = IStudentServiceTest, testInstance = wx.jpa1.IStudentServiceTest@f292738, testMethod = testRegisterStudent@IStudentServiceTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1c3508c0 testClass = IStudentServiceTest, locations = '{classpath:app-context.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]

Thursday, July 24, 2014

JPA - JTA or Resouce_Local

Before using JPA, we have to consider to use transaction type as JTA or Resource_Local because it will cause different coding and transaction management.


  • JTA - container-managered entity manager
  1. injected to app automatically by j2ee container
  2. close/rollback transaction automatically without extra coding
  3. transaction propagation supported by j2ee container
  4. multiple data resources are required, such as more than 1 DB, DB and JMS,etc  
  • Resource_Local - application-managed entity manager
  1. can be used in j2ee container, J2SE and web container
  2. need to create entity manager explicitly
  3. need to close entity manager explicitly
  4. NO transaction propagation
But I think we can rely on Spring framework to inject entity manager and support transaction propagation.


  • eliminate DAO or JPA DAO Pattern
There are some discussion on DAO tier since JPA is introduced. I prefer to adopt jpa dao pattern personally.

1. A generic DAO interface

public interface GenericDao <T, PK extends Serializable> {

    PK create(T newInstance);
    T read(PK id);
    void update(T transientObject);
}

2. implement the genericDAO
3. For particular entity, if any new behavior is required
public interface UserDAO extends GenericDao<Person,ID> {
// define new method
List<User> findByName(String name);
}


Reference URL:
http://employees.oneonta.edu/higgindm/sweng/Generic_DAO.htm
http://www.ibm.com/developerworks/java/library/j-genericdao/index.html