This article explains a real world implementation of Strategy design pattern used with technology stack as JSF, Hibernate, IBM Websphere Application Server.
Motivation
There are common situations when classes differ only in their behavior. For this cases is a good idea to isolate the algorithms in separate classes in order to have the ability to select different algorithms at runtime.
Story
In one of the previous projects during audit it came out the we have to build the Junit and coverage report. Since we were not using Spring for this project (I would not like to go into details that why we were not using Spring except one liner “We were not using Spring Framework due to our crazy technical managers.”), Transaction Management was not easy to handle. We used Hibernate implementation of JDBC Transaction management to manage transactions. This was not the reason to use Hibernate.
Our Business Classes were made of simple POJO classes. We made a practice that all transactions begin and commit or rollback will be handled in the business methods so that DAO methods of all modules are free from transaction management and DAO methods can be re-used in business methods without any restriction and fear of data corruption and inconsistency. So transaction management was intertwined with the business logic.
Example
| Java | | copy code | | ? |
| 01 | public Boolean addNewOrder(Order order) { |
| 02 | |
| 03 | HibernateUtilProxy.startSession(); |
| 04 | |
| 05 | HibernateUtilProxy.beginTransaction(); |
| 06 | |
| 07 | try { |
| 08 | |
| 09 | // Add new order |
| 10 | |
| 11 | } catch(OrderAlreadyExistException e) { |
| 12 | |
| 13 | HibernateUtilProxy.rollbackTransaction(); |
| 14 | |
| 15 | } finally { |
| 16 | |
| 17 | HibernateUtilProxy.commitTransaction(); |
| 18 | |
| 19 | HibernateUtilProxy.closeSession(); |
| 20 | |
| 21 | } |
| 22 | |
| 23 | } |
In managed environment testing is always a tricky task. Since we were getting DB connection using DataSource in a managed environment, it was difficult to test the same code using Junit outside the web container. If we had been using Spring, transaction management would have been managed easily using AOP. In that case we could have used EasyMock or similar framework to mock dependent objects like DAO objects (I am not considering here other advanced dependencies as other people might have faced; I just want to keep it simple). In such scenario testing outside the container is very trivial and easy to maintain.
Since we have a different scenario here, now to execute our business methods using JUnit test cases outside the container we need to cut the dependency on DataSource that is looked up by the Hibernate framework using JNDI during its initialization. But the big question arises how to cut that dependency with minimal changes in config files and without changing the java classes? It is not feasible to change dependent code in java classes every time and compile them to take effect of the change. We want to use to mechanism that should be able to change the implementation class dynamically just by changing a single parameter in the config file.
This case was a prefect candidate where Strategy Pattern can be used to solve the problem. The Design Patterns authors define the Strategy pattern as:
"Define a family of algorithms, encapsulate each one, and make them interchangeable. The Strategy pattern lets the algorithm vary independently from clients that use it".
The Strategy pattern embodies two such principles—encapsulate the concept that varies and program to an interface, not an implementation.
So to stick with this we defined an interface IHibernateUtil and defined to classes that implements IHibernateUtil are HibernateUtilWithDatasourceSupport (for managed environment) and HibernateUtilWithJDBCSupport (for non-managed environment). That means we followed both the principles –
1) By encapsulating the concept of connecting to database using DataSource and JDBC in two different implemenation.
2) By implementing the IHibernateUtil interface.
The below example code is not very detailed. The purpose is to give you the idea about how strategy pattern was implemented.
HibernateUtilProxy
| Java | | copy code | | ? |
| 01 | package com.test.hibernate; |
| 02 | |
| 03 | import java.io.IOException; |
| 04 | |
| 05 | import java.util.Properties; |
| 06 | |
| 07 | public class HibernateUtilProxy { |
| 08 | |
| 09 | private static IHibernateUtil iHibernateUtil = null; |
| 10 | |
| 11 | static { |
| 12 | |
| 13 | try { |
| 14 | |
| 15 | Properties prop = new Properties(); |
| 16 | |
| 17 | prop.load(HibernateUtilProxy.class.getClassLoader() |
| 18 | |
| 19 | .getResourceAsStream("config.properties")); |
| 20 | |
| 21 | String clazz = prop.getProperty("class"); |
| 22 | |
| 23 | Class cz = Class.forName(clazz); |
| 24 | |
| 25 | iHibernateUtil = (IHibernateUtil) cz.newInstance(); |
| 26 | |
| 27 | } catch (IOException e) { |
| 28 | |
| 29 | throw new Error("Unable to load HibernateUtil"); |
| 30 | |
| 31 | } catch (ClassNotFoundException e) { |
| 32 | |
| 33 | throw new Error( |
| 34 | |
| 35 | "Unable to load HibernateUtil. ClassNotFoundException occured."); |
| 36 | |
| 37 | } catch (IllegalAccessException e) { |
| 38 | |
| 39 | throw new Error("Unable to load HibernateUtil."); |
| 40 | |
| 41 | } catch (InstantiationException e) { |
| 42 | |
| 43 | throw new Error("Unable to load HibernateUtil."); |
| 44 | |
| 45 | } |
| 46 | |
| 47 | } |
| 48 | |
| 49 | public static void beginTransaction() { |
| 50 | |
| 51 | iHibernateUtil.beginTransaction(); |
| 52 | |
| 53 | } |
| 54 | |
| 55 | public void closeSession() { |
| 56 | |
| 57 | iHibernateUtil.closeSession(); |
| 58 | |
| 59 | } |
| 60 | |
| 61 | public void commitTransaction() { |
| 62 | |
| 63 | iHibernateUtil.commitTransaction(); |
| 64 | |
| 65 | } |
| 66 | |
| 67 | public void rollbackTransaction() { |
| 68 | |
| 69 | iHibernateUtil.rollbackTransaction(); |
| 70 | |
| 71 | } |
| 72 | |
| 73 | public void startSession() { |
| 74 | |
| 75 | iHibernateUtil.startSession(); |
| 76 | |
| 77 | } |
| 78 | |
| 79 | } |
IHibernateUtil
| Java | | copy code | | ? |
| 01 | package com.test.hibernate; |
| 02 | |
| 03 | public interface IHibernateUtil { |
| 04 | |
| 05 | public void startSession(); |
| 06 | |
| 07 | public void beginTransaction(); |
| 08 | |
| 09 | public void commitTransaction(); |
| 10 | |
| 11 | public void rollbackTransaction(); |
| 12 | |
| 13 | public void closeSession(); |
| 14 | |
| 15 | } |
HibernateUtilWithJDBCSupport (For Non Managed Environment) - Used with JUnit test cases
| Java | | copy code | | ? |
| 01 | package com.test.hibernate; |
| 02 | |
| 03 | import org.hibernate.Session; |
| 04 | |
| 05 | import org.hibernate.SessionFactory; |
| 06 | |
| 07 | import org.hibernate.Transaction; |
| 08 | |
| 09 | import org.hibernate.cfg.Configuration; |
| 10 | |
| 11 | public class HibernateUtilWithJDBCSupport implements IHibernateUtil { |
| 12 | |
| 13 | private static final SessionFactory sessionFactory; |
| 14 | |
| 15 | private static final ThreadLocal threadSession = new ThreadLocal(); |
| 16 | |
| 17 | private static final ThreadLocal threadTransaction = new ThreadLocal(); |
| 18 | |
| 19 | static { |
| 20 | |
| 21 | // Initialize the Hibernate Session Factory using JDBC Settings |
| 22 | |
| 23 | // in hibernate-config-jdbc.cfg.xml |
| 24 | |
| 25 | sessionFactory = new Configuration().configure( |
| 26 | |
| 27 | "hibernate-config-jdbc.cfg.xml").buildSessionFactory(); |
| 28 | |
| 29 | } |
| 30 | |
| 31 | @Override |
| 32 | |
| 33 | public void beginTransaction() { |
| 34 | |
| 35 | // TODO Auto-generated method stub |
| 36 | |
| 37 | } |
| 38 | |
| 39 | @Override |
| 40 | |
| 41 | public void closeSession() { |
| 42 | |
| 43 | // TODO Auto-generated method stub |
| 44 | |
| 45 | } |
| 46 | |
| 47 | @Override |
| 48 | |
| 49 | public void commitTransaction() { |
| 50 | |
| 51 | // TODO Auto-generated method stub |
| 52 | |
| 53 | } |
| 54 | |
| 55 | @Override |
| 56 | |
| 57 | public void rollbackTransaction() { |
| 58 | |
| 59 | // TODO Auto-generated method stub |
| 60 | |
| 61 | } |
| 62 | |
| 63 | @Override |
| 64 | |
| 65 | public void startSession() { |
| 66 | |
| 67 | // TODO Auto-generated method stub |
| 68 | |
| 69 | } |
| 70 | |
| 71 | } |
HibernateUtilWithDataSourceSupport (For Managed Environment) - Actual implementation that will be used in production.
| Java | | copy code | | ? |
| 01 | package com.test.hibernate; |
| 02 | |
| 03 | import org.hibernate.Session; |
| 04 | |
| 05 | import org.hibernate.SessionFactory; |
| 06 | |
| 07 | import org.hibernate.Transaction; |
| 08 | |
| 09 | import org.hibernate.cfg.Configuration; |
| 10 | |
| 11 | public class HibernateUtilWithDataSourceSupport implements IHibernateUtil{ |
| 12 | |
| 13 | private static final SessionFactory sessionFactory; |
| 14 | |
| 15 | private static final ThreadLocal threadSession = new ThreadLocal(); |
| 16 | |
| 17 | private static final ThreadLocal threadTransaction = new ThreadLocal(); |
| 18 | |
| 19 | static { |
| 20 | |
| 21 | // Initialize the Hibernate Session Factory using JDBC Settings |
| 22 | |
| 23 | // in hibernate-config-datasource.cfg.xml |
| 24 | |
| 25 | sessionFactory = new Configuration().configure( |
| 26 | |
| 27 | "hibernate-config-datasource.cfg.xml").buildSessionFactory(); |
| 28 | |
| 29 | } |
| 30 | |
| 31 | @Override |
| 32 | |
| 33 | public void beginTransaction() { |
| 34 | |
| 35 | // TODO Auto-generated method stub |
| 36 | |
| 37 | } |
| 38 | |
| 39 | @Override |
| 40 | |
| 41 | public void closeSession() { |
| 42 | |
| 43 | // TODO Auto-generated method stub |
| 44 | |
| 45 | } |
| 46 | |
| 47 | @Override |
| 48 | |
| 49 | public void commitTransaction() { |
| 50 | |
| 51 | // TODO Auto-generated method stub |
| 52 | |
| 53 | } |
| 54 | |
| 55 | @Override |
| 56 | |
| 57 | public void rollbackTransaction() { |
| 58 | |
| 59 | // TODO Auto-generated method stub |
| 60 | |
| 61 | } |
| 62 | |
| 63 | @Override |
| 64 | |
| 65 | public void startSession() { |
| 66 | |
| 67 | // TODO Auto-generated method stub |
| 68 | |
| 69 | } |
| 70 | |
| 71 | } |
config.properties
| Text | | copy code | | ? |
| 1 | class=com.test.hibernate.HibernateUtilWithDataSourceSupport |