Now working with both hibernate and eclipselink
[utils] / test / enterprise / src / main / java / org / wamblee / support / persistence / JpaBuilder.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.support.persistence;
17
18 import java.util.Map;
19 import java.util.TreeMap;
20 import java.util.logging.Level;
21 import java.util.logging.Logger;
22
23 import javax.naming.InitialContext;
24 import javax.naming.NamingException;
25 import javax.persistence.EntityManager;
26 import javax.persistence.EntityManagerFactory;
27 import javax.persistence.EntityTransaction;
28 import javax.persistence.Persistence;
29 import javax.persistence.PersistenceException;
30 import javax.sql.DataSource;
31
32 import org.wamblee.support.jndi.StubInitialContextFactory;
33
34 /**
35  * Utility for building an appropriately configured EntityManagerFactory. The
36  * idea is that a persistence.xml is used unchanged from the production version.
37  * This utility will then add the additional properties required for execution
38  * in a standalone environment.
39  * 
40  * The other purpose is to to shield dependencies of the test code on a
41  * particular JPA provider.
42  */
43 public class JpaBuilder {
44
45     private static final Logger LOGGER = Logger.getLogger(JpaBuilder.class
46         .getName());
47
48     /**
49      * Callback interface to execute some JPA code within a transaction with the
50      * entitymanager to use provided as input.
51      */
52     public static interface JpaUnitOfWork<T> {
53         /**
54          * Executes the unit of work. A transaction has been started.
55          * 
56          * @param aEm
57          *            Entity manager.
58          * @return Result of the execute method. If you don't want to return
59          *         anything use <code>Void</code> for the return type and return
60          *         null from the implementation.
61          */
62         T execute(EntityManager aEm) throws Exception;
63     }
64
65     private PersistenceUnitDescription persistenceUnit;
66     private DataSource dataSource;
67     private EntityManagerFactory factory;
68
69     /**
70      * Constructs the builder.
71      * 
72      * @param aDataSource
73      *            Datasource of database.
74      * @param aPersistenceUnit
75      *            Persistence unit.
76      */
77     public JpaBuilder(DataSource aDataSource,
78         PersistenceUnitDescription aPersistenceUnit) {
79         persistenceUnit = aPersistenceUnit;
80         dataSource = aDataSource;
81         StubInitialContextFactory.register();
82     }
83
84     /**
85      * Starts the builder, which in particular, mocks JNDI, binds the datasource
86      * the JNDI where the persistence unit expects it, creates the entity
87      * manager factory, and forces creation of the database schema.
88      */
89     public void start() throws Exception {
90         try {
91             InitialContext ctx = new InitialContext();
92             ctx.bind(persistenceUnit.getJndiName(), dataSource);
93         } catch (NamingException e) {
94             throw new RuntimeException("JNDI problem", e);
95         }
96         factory = createFactory();
97         try {
98             execute(new JpaUnitOfWork<Void>() {
99                 public Void execute(EntityManager aEm) {
100                     // Empty, just to trigger database schema creation.
101                     return null;
102                 }
103             });
104         } catch (PersistenceException e) {
105             factory.close();
106             throw e;
107         }
108     }
109
110     /**
111      * Stops the entity manager factory and disables JNDI mocking.
112      */
113     public void stop() {
114         StubInitialContextFactory.unregister();
115         factory.close();
116     }
117
118     /**
119      * Creates a new entity manager factory. Typically not used by test code.
120      * 
121      * @return Entity manager factory.
122      */
123     public EntityManagerFactory createFactory() {
124         Map<String, String> jpaProps = new TreeMap<String, String>();
125
126         JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit,
127             jpaProps);
128
129         // jpaProps.put("javax.persistence.provider",
130         // HibernatePersistence.class.getName());
131         EntityManagerFactory factory = Persistence.createEntityManagerFactory(
132             persistenceUnit.getUnitName(), jpaProps);
133
134         LOGGER.info("Using " + factory.getClass());
135         return factory;
136     }
137
138     /**
139      * Executes a unit of work. This creates an entitymanager and runs the
140      * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction,
141      * passing it the entity manager. Use of this method saves a lot of typing
142      * for applications.
143      * 
144      * @param aWork
145      *            Work to execute.
146      * @return The return value of the execute method of the unit of work.
147      */
148     public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
149         EntityManager em = factory.createEntityManager();
150         EntityTransaction transaction = em.getTransaction();
151         transaction.begin();
152         try {
153             T value = aWork.execute(em);
154             transaction.commit();
155             return value;
156         } catch (Exception e) {
157             LOGGER.log(Level.WARNING, "Exception occured", e);
158             transaction.rollback();
159             throw e;
160         } finally {
161             em.close();
162         }
163     }
164 }