dac2789b942e8f5a79442a6b5fb80e37d7bd8fe9
[utils] / test / enterprise / src / main / java / org / wamblee / test / 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.test.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.persistence.EntityManager;
24 import javax.persistence.EntityManagerFactory;
25 import javax.persistence.EntityTransaction;
26 import javax.persistence.Persistence;
27 import javax.persistence.PersistenceException;
28
29 import org.wamblee.general.ThreadSpecificProxyFactory;
30 import org.wamblee.test.jndi.StubInitialContextFactory;
31 import org.wamblee.test.transactions.TransactionResource;
32
33 /**
34  * Utility for building an appropriately configured EntityManagerFactory. The
35  * idea is that a persistence.xml is used unchanged from the production version.
36  * This utility will then add the additional properties required for execution
37  * in a standalone environment.
38  * 
39  * The other purpose is to to shield dependencies of the test code on a
40  * particular JPA provider.
41  */
42 public class JpaBuilder implements TransactionResource<EntityManager> {
43
44     private static final Logger LOGGER = Logger.getLogger(JpaBuilder.class
45         .getName());
46
47     /**
48      * Callback interface to execute some JPA code within a transaction with the
49      * entitymanager to use provided as input.
50      */
51     public static interface JpaUnitOfWork<T> {
52         /**
53          * Executes the unit of work. A transaction has been started.
54          * 
55          * @param aEm
56          *            Entity manager.
57          * @return Result of the execute method. If you don't want to return
58          *         anything use <code>Void</code> for the return type and return
59          *         null from the implementation.
60          */
61         T execute(EntityManager aEm) throws Exception;
62     }
63
64     private PersistenceUnitDescription persistenceUnit;
65     private String url;
66     private String user;
67     private String password;
68     private EntityManagerFactory factory;
69     private ThreadSpecificProxyFactory<EntityManager> entityManager;
70
71     /**
72      * Constructs the builder.
73      * 
74      * @param aUrl
75      *            JDBC URL
76      * @param aUser
77      *            User name
78      * @param aPassword
79      *            Password.
80      * @param aPersistenceUnit
81      *            Persistence unit.
82      */
83     public JpaBuilder(String aUrl, String aUser, String aPassword,
84         PersistenceUnitDescription aPersistenceUnit) {
85         persistenceUnit = aPersistenceUnit;
86         url = aUrl;
87         user = aUser;
88         password = aPassword;
89         entityManager = new ThreadSpecificProxyFactory<EntityManager>(
90             EntityManager.class);
91     }
92
93     /**
94      * Starts the builder, which in particular, mocks JNDI, binds the datasource
95      * the JNDI where the persistence unit expects it, creates the entity
96      * manager factory, and forces creation of the database schema.
97      */
98     public void start() throws Exception {
99         factory = createFactory();
100         try {
101             execute(new JpaUnitOfWork<Void>() {
102                 public Void execute(EntityManager aEm) {
103                     // Empty, just to trigger database schema creation.
104                     return null;
105                 }
106             });
107         } catch (PersistenceException e) {
108             factory.close();
109             throw e;
110         }
111     }
112
113     /**
114      * Stops the entity manager factory and disables JNDI mocking.
115      */
116     public void stop() {
117         StubInitialContextFactory.unregister();
118         factory.close();
119     }
120
121     /**
122      * Creates a new entity manager factory. Typically not used by test code.
123      * 
124      * @return Entity manager factory.
125      */
126     public EntityManagerFactory createFactory() {
127         Map<String, String> jpaProps = new TreeMap<String, String>();
128
129         jpaProps.put("javax.persistence.jtaDataSource", null);
130         jpaProps.put("javax.persistence.transactionType", "RESOURCE_LOCAL");
131         jpaProps.put("javax.persistence.jdbc.url", url);
132         jpaProps.put("javax.persistence.jdbc.user", user);
133         jpaProps.put("javax.persistence.jdbc.password", password);
134
135         JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit,
136             jpaProps);
137
138         // jpaProps.put("javax.persistence.provider",
139         // HibernatePersistence.class.getName());
140         EntityManagerFactory emf = Persistence.createEntityManagerFactory(
141             persistenceUnit.getUnitName(), jpaProps);
142
143         LOGGER.info("Using " + emf.getClass());
144         return emf;
145     }
146
147     /**
148      * Executes a unit of work. This creates an entitymanager and runs the
149      * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction,
150      * passing it the entity manager. Use of this method saves a lot of typing
151      * for applications.
152      * 
153      * @param aWork
154      *            Work to execute.
155      * @return The return value of the execute method of the unit of work.
156      */
157     public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
158         EntityManager em = begin();
159         try {
160             T value = aWork.execute(em);
161             commit(em);
162             return value;
163         } catch (Exception e) {
164             LOGGER.log(Level.WARNING, "Exception occured", e);
165             rollback(em);
166             throw e;
167         }
168     }
169
170     @Override
171     public EntityManager begin() {
172         EntityManager em = factory.createEntityManager();
173         EntityTransaction transaction = em.getTransaction();
174         transaction.begin();
175         entityManager.set(em);
176         return em;
177     }
178
179     @Override
180     public void commit(EntityManager aEntityManager) {
181         try {
182             aEntityManager.getTransaction().commit();
183         } finally {
184             aEntityManager.close();
185             entityManager.set(null);
186         }
187     }
188
189     @Override
190     public void rollback(EntityManager aEntityManager) {
191         try {
192             EntityTransaction transaction = aEntityManager.getTransaction();
193             if (transaction.isActive()) {
194                 transaction.rollback();
195             }
196         } finally {
197             if (aEntityManager.isOpen()) {
198                 aEntityManager.close();
199             }
200             entityManager.set(null);
201         }
202     }
203
204     /**
205      * Gets a contextual reference to an entity manager that delegates to the
206      * appropriate (current) one which is active for the current transaction.
207      * 
208      * @return EntityManager.
209      */
210     public EntityManager getContextualEntityManager() {
211         return entityManager.getProxy();
212     }
213 }