e6943197b85e66ca18cbb4eee8dcb3e96bba4387
[utils] / support / test / src / main / java / org / wamblee / support / persistence / JpaBuilder.java
1 package org.wamblee.support.persistence;
2
3 import java.sql.SQLException;
4 import java.util.Map;
5 import java.util.ServiceLoader;
6 import java.util.TreeMap;
7 import java.util.logging.Level;
8 import java.util.logging.Logger;
9
10 import javax.naming.InitialContext;
11 import javax.naming.NamingException;
12 import javax.persistence.EntityManager;
13 import javax.persistence.EntityManagerFactory;
14 import javax.persistence.EntityTransaction;
15 import javax.persistence.Persistence;
16 import javax.sql.DataSource;
17
18 import org.hibernate.ejb.HibernatePersistence;
19 import org.wamblee.support.jndi.StubInitialContextFactory;
20 import org.wamblee.support.persistence.toplink.JndiSessionCustomizer;
21
22
23 /**
24  * Utility for building an appropriately configured EntityManagerFactory. The
25  * idea is that a persistence.xml is used unchanged from the production version.
26  * This utility will then add the additional properties required for execution
27  * in a standalone environment.
28  * 
29  * The other purpose is to to shield dependencies of the test code on a
30  * particular JPA provider.
31  */
32 public class JpaBuilder {
33
34         private static final Logger LOGGER = Logger.getLogger(JpaBuilder.class
35                         .getName());
36
37         /**
38          * Callback interface to execute some JPA code within a transaction with the
39          * entitymanager to use provided as input.
40          */
41         public static interface JpaUnitOfWork<T> {
42                 /**
43                  * Executes the unit of work. A transaction has been started.  
44                  * @param em Entity manager. 
45                  * @return Result of the execute method. If you don't want to return anything use
46                  *  <code>Void</code> for the return type and return null from the implementation. 
47                  */
48                 T execute(EntityManager em);
49         }
50
51         private PersistenceUnitDescription persistenceUnit;
52         private DataSource dataSource;
53         private EntityManagerFactory factory;
54
55         /**
56          * Constructs the builder.
57          * 
58          * @param aDataSource
59          *            Datasource of database.
60          * @param aPersistenceUnit
61          *            Persistence unit.
62          */
63         public JpaBuilder(DataSource aDataSource,
64                         PersistenceUnitDescription aPersistenceUnit) {
65                 persistenceUnit = aPersistenceUnit;
66                 dataSource = aDataSource;
67                 StubInitialContextFactory.register();
68         }
69
70         /**
71          * Starts the builder, which in particular, mocks JNDI, binds the datasource
72          * the JNDI where the persistence unit expects it, creates the entity
73          * manager factory, and forces creation of the database schema.
74          */
75         public void start() throws Exception {
76                 try {
77                         InitialContext ctx = new InitialContext();
78                         ctx.bind(persistenceUnit.getJndiName(), dataSource);
79                 } catch (NamingException e) {
80                         throw new RuntimeException("JNDI problem", e);
81                 }
82                 factory = createFactory();
83                 execute(new JpaUnitOfWork<Void>() {
84                         public Void execute(EntityManager em) {
85                                 // Empty, just to trigger database schema creation.
86                                 return null;
87                         }
88                 });
89         }
90
91         /**
92          * Stops the entity manager factory and disables JNDI mocking. 
93          */
94         public void stop() {
95                 StubInitialContextFactory.unregister();
96                 factory.close();
97         }
98
99         /**
100          * Creates a new entity manager factory. Typically not used by test code. 
101          * @return Entity manager factory. 
102          */
103         public EntityManagerFactory createFactory() {
104                 Map<String, String> jpaProps = new TreeMap<String, String>();
105         
106                 JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit, jpaProps);
107                 
108                 jpaProps.put("javax.persistence.provider", HibernatePersistence.class.getName() +"unknown");
109                 EntityManagerFactory factory = Persistence.createEntityManagerFactory(persistenceUnit
110                                 .getUnitName(), jpaProps);
111                 
112                 LOGGER.info("Using " + factory.getClass());
113                 return factory;
114         }
115
116         /**
117          * Executes a unit of work. This creates an entitymanager and runs the 
118          * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction, passing
119          * it the entity manager. Use of this method saves a lot of typing for applications. 
120          * 
121          * @param aWork Work to execute. 
122          * @return The return value of the execute method of the unit of work. 
123          */
124         public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
125                 EntityManager em = factory.createEntityManager();
126                 EntityTransaction transaction = em.getTransaction();
127                 transaction.begin();
128                 try {
129                         T value = aWork.execute(em);
130                         transaction.commit();
131                         return value; 
132                 } catch (Exception e) {
133                         LOGGER.log(Level.WARNING, "Exception occured", e);
134                         transaction.rollback();
135                         throw e; 
136                 } finally {
137                         em.close();
138                 }
139         }
140 }