(no commit message)
[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>(EntityManager.class);
90     }
91
92     /**
93      * Starts the builder, which in particular, mocks JNDI, binds the datasource
94      * the JNDI where the persistence unit expects it, creates the entity
95      * manager factory, and forces creation of the database schema.
96      */
97     public void start() throws Exception {
98         factory = createFactory();
99         try {
100             execute(new JpaUnitOfWork<Void>() {
101                 public Void execute(EntityManager aEm) {
102                     // Empty, just to trigger database schema creation.
103                     return null;
104                 }
105             });
106         } catch (PersistenceException e) {
107             factory.close();
108             throw e;
109         }
110     }
111
112     /**
113      * Stops the entity manager factory and disables JNDI mocking.
114      */
115     public void stop() {
116         StubInitialContextFactory.unregister();
117         factory.close();
118     }
119
120     /**
121      * Creates a new entity manager factory. Typically not used by test code.
122      * 
123      * @return Entity manager factory.
124      */
125     public EntityManagerFactory createFactory() {
126         Map<String, String> jpaProps = new TreeMap<String, String>();
127
128         jpaProps.put("javax.persistence.jtaDataSource", null);
129         jpaProps.put("javax.persistence.transactionType", "RESOURCE_LOCAL");
130         jpaProps.put("javax.persistence.jdbc.url", url);
131         jpaProps.put("javax.persistence.jdbc.user", user);
132         jpaProps.put("javax.persistence.jdbc.password", password);
133
134         JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit,
135             jpaProps);
136
137         // jpaProps.put("javax.persistence.provider",
138         // HibernatePersistence.class.getName());
139         EntityManagerFactory emf = Persistence.createEntityManagerFactory(
140             persistenceUnit.getUnitName(), jpaProps);
141
142         LOGGER.info("Using " + emf.getClass());
143         return emf;
144     }
145
146     /**
147      * Executes a unit of work. This creates an entitymanager and runs the
148      * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction,
149      * passing it the entity manager. Use of this method saves a lot of typing
150      * for applications.
151      * 
152      * @param aWork
153      *            Work to execute.
154      * @return The return value of the execute method of the unit of work.
155      */
156     public <T> T execute(JpaUnitOfWork<T> aWork) throws Exception {
157         EntityManager em = begin();
158         try {
159             T value = aWork.execute(em);
160             commit(em);
161             return value;
162         } catch (Exception e) {
163             LOGGER.log(Level.WARNING, "Exception occured", e);
164             rollback(em);
165             throw e;
166         } 
167     }
168
169     @Override
170     public EntityManager begin() {
171         EntityManager em = factory.createEntityManager();
172         EntityTransaction transaction = em.getTransaction();
173         transaction.begin();
174         entityManager.set(em);
175         return em;
176     }
177
178     @Override
179     public void commit(EntityManager aEntityManager) {
180         try {
181             aEntityManager.getTransaction().commit();
182         } finally {
183             aEntityManager.close();
184             entityManager.set(null);
185         }
186     }
187
188     @Override
189     public void rollback(EntityManager aEntityManager) {
190         try {
191             aEntityManager.getTransaction().rollback();
192         } finally {
193             aEntityManager.close();
194             entityManager.set(null);
195         }
196     }
197     
198     /**
199      * Gets a contextual reference to an entity manager that delegates to the 
200      * appropriate (current) one which is active for the current transaction. 
201      * @return EntityManager. 
202      */
203     public EntityManager getContextualEntityManager() { 
204         return entityManager.getProxy(); 
205     }
206 }