2 * Copyright 2005-2010 the original author or authors.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.wamblee.test.persistence;
19 import java.util.TreeMap;
20 import java.util.logging.Level;
21 import java.util.logging.Logger;
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;
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;
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.
40 * The other purpose is to to shield dependencies of the test code on a
41 * particular JPA provider.
43 public class JpaBuilder implements TransactionResource<EntityManager> {
45 private static final Logger LOGGER = Logger.getLogger(JpaBuilder.class
49 * Callback interface to execute some JPA code within a transaction with the
50 * entitymanager to use provided as input.
52 public static interface JpaUnitOfWork<T> {
54 * Executes the unit of work. A transaction has been started.
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.
62 T execute(EntityManager aEm) throws Exception;
65 private PersistenceUnitDescription persistenceUnit;
68 private String password;
69 private EntityManagerFactory factory;
70 private ThreadSpecificProxyFactory<EntityManager> entityManager;
73 * Constructs the builder.
81 * @param aPersistenceUnit
84 public JpaBuilder(String aUrl, String aUser, String aPassword,
85 PersistenceUnitDescription aPersistenceUnit) {
86 persistenceUnit = aPersistenceUnit;
90 entityManager = new ThreadSpecificProxyFactory<EntityManager>(
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.
99 public void start() throws Exception {
100 factory = createFactory();
102 execute(new JpaUnitOfWork<Void>() {
103 public Void execute(EntityManager aEm) {
104 // Empty, just to trigger database schema creation.
108 } catch (PersistenceException e) {
115 * Stops the entity manager factory and disables JNDI mocking.
118 StubInitialContextFactory.unregister();
123 * Creates a new entity manager factory. Typically not used by test code.
125 * @return Entity manager factory.
127 public EntityManagerFactory createFactory() {
128 Map<String, String> jpaProps = new TreeMap<String, String>();
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);
136 JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit,
139 // jpaProps.put("javax.persistence.provider",
140 // HibernatePersistence.class.getName());
141 EntityManagerFactory emf = Persistence.createEntityManagerFactory(
142 persistenceUnit.getUnitName(), jpaProps);
144 LOGGER.info("Using " + emf.getClass());
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
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.
161 * @return The return value of the execute method of the unit of work.
163 public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
164 return execute(aWork, new RequireTransactionStatus(TransactionResult.COMMIT));
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
175 * @param aTransactionResultCallback
176 * callback to notify of the result of the transaction.
178 * @return The return value of the execute method of the unit of work.
180 public <T> T execute(JpaUnitOfWork<T> aWork,
181 TransactionResultCallback aCallback) throws Exception {
182 EntityManager em = begin();
184 T value = aWork.execute(em);
185 TransactionResult result = commit(em);
186 aCallback.status(result);
188 } catch (Exception e) {
189 LOGGER.log(Level.WARNING, "Exception occured", e);
190 TransactionResult result = rollback(em);
191 aCallback.status(result);
197 public EntityManager begin() {
198 EntityManager em = factory.createEntityManager();
199 EntityTransaction transaction = em.getTransaction();
201 entityManager.set(em);
206 public TransactionResult commit(EntityManager aEntityManager) {
208 EntityTransaction transaction = aEntityManager.getTransaction();
209 if (transaction.isActive()) {
210 if (transaction.getRollbackOnly()) {
211 transaction.rollback();
212 return TransactionResult.ROLLBACK;
214 transaction.commit();
215 return TransactionResult.COMMIT;
217 return TransactionResult.UNKNOWN;
219 aEntityManager.close();
220 entityManager.set(null);
225 public TransactionResult rollback(EntityManager aEntityManager) {
227 EntityTransaction transaction = aEntityManager.getTransaction();
228 if (transaction.isActive()) {
229 transaction.rollback();
230 return TransactionResult.ROLLBACK;
232 return TransactionResult.UNKNOWN;
234 if (aEntityManager.isOpen()) {
235 aEntityManager.close();
237 entityManager.set(null);
242 * Gets a contextual reference to an entity manager that delegates to the
243 * appropriate (current) one which is active for the current transaction.
245 * @return EntityManager.
247 public EntityManager getContextualEntityManager() {
248 return entityManager.getProxy();