223971c6cdfec69eec48daf8e224f452b2f61663
[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 import org.wamblee.test.transactions.TransactionResult;
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 implements TransactionResource<EntityManager> {
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 String url;
67     private String user;
68     private String password;
69     private EntityManagerFactory factory;
70     private ThreadSpecificProxyFactory<EntityManager> entityManager;
71
72     /**
73      * Constructs the builder.
74      * 
75      * @param aUrl
76      *            JDBC URL
77      * @param aUser
78      *            User name
79      * @param aPassword
80      *            Password.
81      * @param aPersistenceUnit
82      *            Persistence unit.
83      */
84     public JpaBuilder(String aUrl, String aUser, String aPassword,
85         PersistenceUnitDescription aPersistenceUnit) {
86         persistenceUnit = aPersistenceUnit;
87         url = aUrl;
88         user = aUser;
89         password = aPassword;
90         entityManager = new ThreadSpecificProxyFactory<EntityManager>(
91             EntityManager.class);
92     }
93
94     /**
95      * Starts the builder, which in particular, mocks JNDI, binds the datasource
96      * the JNDI where the persistence unit expects it, creates the entity
97      * manager factory, and forces creation of the database schema.
98      */
99     public void start() throws Exception {
100         factory = createFactory();
101         try {
102             execute(new JpaUnitOfWork<Void>() {
103                 public Void execute(EntityManager aEm) {
104                     // Empty, just to trigger database schema creation.
105                     return null;
106                 }
107             });
108         } catch (PersistenceException e) {
109             factory.close();
110             throw e;
111         }
112     }
113
114     /**
115      * Stops the entity manager factory and disables JNDI mocking.
116      */
117     public void stop() {
118         StubInitialContextFactory.unregister();
119         factory.close();
120     }
121
122     /**
123      * Creates a new entity manager factory. Typically not used by test code.
124      * 
125      * @return Entity manager factory.
126      */
127     public EntityManagerFactory createFactory() {
128         Map<String, String> jpaProps = new TreeMap<String, String>();
129
130         jpaProps.put("javax.persistence.jtaDataSource", null);
131         jpaProps.put("javax.persistence.transactionType", "RESOURCE_LOCAL");
132         jpaProps.put("javax.persistence.jdbc.url", url);
133         jpaProps.put("javax.persistence.jdbc.user", user);
134         jpaProps.put("javax.persistence.jdbc.password", password);
135
136         JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit,
137             jpaProps);
138
139         // jpaProps.put("javax.persistence.provider",
140         // HibernatePersistence.class.getName());
141         EntityManagerFactory emf = Persistence.createEntityManagerFactory(
142             persistenceUnit.getUnitName(), jpaProps);
143
144         LOGGER.info("Using " + emf.getClass());
145         return emf;
146     }
147
148     /**
149      * Executes a unit of work. This creates an entitymanager and runs the
150      * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction,
151      * passing it the entity manager. Use of this method saves a lot of typing
152      * for applications.
153      * 
154      * This method requires the transaction to succeed. Otherwise the test will
155      * fail. See {@link #execute(JpaUnitOfWork, TransactionResultCallback)} and
156      * {@link RequireTransactionStatus} for more possibilities. 
157      * 
158      * @param aWork
159      *            Work to execute.
160      * 
161      * @return The return value of the execute method of the unit of work.
162      */
163     public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
164         return execute(aWork, new RequireTransactionStatus(TransactionResult.COMMIT));
165     }
166     
167     /**
168      * Executes a unit of work. This creates an entitymanager and runs the
169      * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction,
170      * passing it the entity manager. Use of this method saves a lot of typing
171      * for applications.
172      * 
173      * @param aWork
174      *            Work to execute.
175      * @param aTransactionResultCallback
176      *            callback to notify of the result of the transaction.
177      * 
178      * @return The return value of the execute method of the unit of work.
179      */
180     public <T> T execute(JpaUnitOfWork<T> aWork,
181         TransactionResultCallback aCallback) throws Exception {
182         EntityManager em = begin();
183         try {
184             T value = aWork.execute(em);
185             TransactionResult result = commit(em);
186             aCallback.status(result);
187             return value;
188         } catch (Exception e) {
189             LOGGER.log(Level.WARNING, "Exception occured", e);
190             TransactionResult result = rollback(em);
191             aCallback.status(result);
192             throw e;
193         }
194     }
195
196     @Override
197     public EntityManager begin() {
198         EntityManager em = factory.createEntityManager();
199         EntityTransaction transaction = em.getTransaction();
200         transaction.begin();
201         entityManager.set(em);
202         return em;
203     }
204
205     @Override
206     public TransactionResult commit(EntityManager aEntityManager) {
207         try {
208             EntityTransaction transaction = aEntityManager.getTransaction();
209             if (transaction.isActive()) {
210                 if (transaction.getRollbackOnly()) {
211                     transaction.rollback();
212                     return TransactionResult.ROLLBACK;
213                 }
214                 transaction.commit();
215                 return TransactionResult.COMMIT;
216             }
217             return TransactionResult.UNKNOWN;
218         } finally {
219             aEntityManager.close();
220             entityManager.set(null);
221         }
222     }
223
224     @Override
225     public TransactionResult rollback(EntityManager aEntityManager) {
226         try {
227             EntityTransaction transaction = aEntityManager.getTransaction();
228             if (transaction.isActive()) {
229                 transaction.rollback();
230                 return TransactionResult.ROLLBACK;
231             }
232             return TransactionResult.UNKNOWN;
233         } finally {
234             if (aEntityManager.isOpen()) {
235                 aEntityManager.close();
236             }
237             entityManager.set(null);
238         }
239     }
240
241     /**
242      * Gets a contextual reference to an entity manager that delegates to the
243      * appropriate (current) one which is active for the current transaction.
244      * 
245      * @return EntityManager.
246      */
247     public EntityManager getContextualEntityManager() {
248         return entityManager.getProxy();
249     }
250 }