/* * Copyright 2005-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wamblee.test.persistence; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import javax.persistence.PersistenceException; import org.wamblee.general.ThreadSpecificProxyFactory; import org.wamblee.test.jndi.StubInitialContextFactory; import org.wamblee.test.transactions.TransactionResource; /** * Utility for building an appropriately configured EntityManagerFactory. The * idea is that a persistence.xml is used unchanged from the production version. * This utility will then add the additional properties required for execution * in a standalone environment. * * The other purpose is to to shield dependencies of the test code on a * particular JPA provider. */ public class JpaBuilder implements TransactionResource { private static final Logger LOGGER = Logger.getLogger(JpaBuilder.class .getName()); /** * Callback interface to execute some JPA code within a transaction with the * entitymanager to use provided as input. */ public static interface JpaUnitOfWork { /** * Executes the unit of work. A transaction has been started. * * @param aEm * Entity manager. * @return Result of the execute method. If you don't want to return * anything use Void for the return type and return * null from the implementation. */ T execute(EntityManager aEm) throws Exception; } private PersistenceUnitDescription persistenceUnit; private String url; private String user; private String password; private EntityManagerFactory factory; private ThreadSpecificProxyFactory entityManager; /** * Constructs the builder. * * @param aUrl * JDBC URL * @param aUser * User name * @param aPassword * Password. * @param aPersistenceUnit * Persistence unit. */ public JpaBuilder(String aUrl, String aUser, String aPassword, PersistenceUnitDescription aPersistenceUnit) { persistenceUnit = aPersistenceUnit; url = aUrl; user = aUser; password = aPassword; entityManager = new ThreadSpecificProxyFactory( EntityManager.class); } /** * Starts the builder, which in particular, mocks JNDI, binds the datasource * the JNDI where the persistence unit expects it, creates the entity * manager factory, and forces creation of the database schema. */ public void start() throws Exception { factory = createFactory(); try { execute(new JpaUnitOfWork() { public Void execute(EntityManager aEm) { // Empty, just to trigger database schema creation. return null; } }); } catch (PersistenceException e) { factory.close(); throw e; } } /** * Stops the entity manager factory and disables JNDI mocking. */ public void stop() { StubInitialContextFactory.unregister(); factory.close(); } /** * Creates a new entity manager factory. Typically not used by test code. * * @return Entity manager factory. */ public EntityManagerFactory createFactory() { Map jpaProps = new TreeMap(); jpaProps.put("javax.persistence.jtaDataSource", null); jpaProps.put("javax.persistence.transactionType", "RESOURCE_LOCAL"); jpaProps.put("javax.persistence.jdbc.url", url); jpaProps.put("javax.persistence.jdbc.user", user); jpaProps.put("javax.persistence.jdbc.password", password); JpaCustomizerBuilder.getCustomizer().customize(persistenceUnit, jpaProps); // jpaProps.put("javax.persistence.provider", // HibernatePersistence.class.getName()); EntityManagerFactory emf = Persistence.createEntityManagerFactory( persistenceUnit.getUnitName(), jpaProps); LOGGER.info("Using " + emf.getClass()); return emf; } /** * Executes a unit of work. This creates an entitymanager and runs the * {@link JpaUnitOfWork#execute(EntityManager)} within a transaction, * passing it the entity manager. Use of this method saves a lot of typing * for applications. * * @param aWork * Work to execute. * @return The return value of the execute method of the unit of work. */ public T execute(JpaUnitOfWork aWork) throws Exception { EntityManager em = begin(); try { T value = aWork.execute(em); commit(em); return value; } catch (Exception e) { LOGGER.log(Level.WARNING, "Exception occured", e); rollback(em); throw e; } } @Override public EntityManager begin() { EntityManager em = factory.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); entityManager.set(em); return em; } @Override public void commit(EntityManager aEntityManager) { try { aEntityManager.getTransaction().commit(); } finally { aEntityManager.close(); entityManager.set(null); } } @Override public void rollback(EntityManager aEntityManager) { try { EntityTransaction transaction = aEntityManager.getTransaction(); if (transaction.isActive()) { transaction.rollback(); } } finally { if (aEntityManager.isOpen()) { aEntityManager.close(); } entityManager.set(null); } } /** * Gets a contextual reference to an entity manager that delegates to the * appropriate (current) one which is active for the current transaction. * * @return EntityManager. */ public EntityManager getContextualEntityManager() { return entityManager.getProxy(); } }