d50ca76e10b92172d0a51e4caf8218f3b90e191d
[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      * @param aWork
155      *            Work to execute.
156      * 
157      * @return The return value of the execute method of the unit of work.
158      */
159     public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
160         return execute(aWork, new LoggingTransactionResultCallback(Level.INFO));
161     }
162
163     /**
164      * Executes a unit of work. This creates an entitymanager and runs the
165      * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction,
166      * passing it the entity manager. Use of this method saves a lot of typing
167      * for applications.
168      * 
169      * @param aWork
170      *            Work to execute.
171      * @param aTransactionResultCallback
172      *            callback to notify of the result of the transaction.
173      * 
174      * @return The return value of the execute method of the unit of work.
175      */
176     public <T> T execute(JpaUnitOfWork<T> aWork,
177         TransactionResultCallback aCallback) throws Exception {
178         EntityManager em = begin();
179         try {
180             T value = aWork.execute(em);
181             TransactionResult result = commit(em);
182             aCallback.status(result);
183             return value;
184         } catch (Exception e) {
185             LOGGER.log(Level.WARNING, "Exception occured", e);
186             TransactionResult result = rollback(em);
187             aCallback.status(result);
188             throw e;
189         }
190     }
191
192     @Override
193     public EntityManager begin() {
194         EntityManager em = factory.createEntityManager();
195         EntityTransaction transaction = em.getTransaction();
196         transaction.begin();
197         entityManager.set(em);
198         return em;
199     }
200
201     @Override
202     public TransactionResult commit(EntityManager aEntityManager) {
203         try {
204             EntityTransaction transaction = aEntityManager.getTransaction();
205             if (transaction.isActive()) {
206                 if (transaction.getRollbackOnly()) {
207                     transaction.rollback();
208                     return TransactionResult.ROLLBACK;
209                 }
210                 transaction.commit();
211                 return TransactionResult.COMMIT;
212             }
213             return TransactionResult.UNKNOWN;
214         } finally {
215             aEntityManager.close();
216             entityManager.set(null);
217         }
218     }
219
220     @Override
221     public TransactionResult rollback(EntityManager aEntityManager) {
222         try {
223             EntityTransaction transaction = aEntityManager.getTransaction();
224             if (transaction.isActive()) {
225                 transaction.rollback();
226                 return TransactionResult.ROLLBACK;
227             }
228             return TransactionResult.UNKNOWN;
229         } finally {
230             if (aEntityManager.isOpen()) {
231                 aEntityManager.close();
232             }
233             entityManager.set(null);
234         }
235     }
236
237     /**
238      * Gets a contextual reference to an entity manager that delegates to the
239      * appropriate (current) one which is active for the current transaction.
240      * 
241      * @return EntityManager.
242      */
243     public EntityManager getContextualEntityManager() {
244         return entityManager.getProxy();
245     }
246 }