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