/*
* Copyright (c) OSGi Alliance (2013). All Rights Reserved.
*
* 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 osgi.jpa.managed.support;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;
import javax.transaction.Synchronization;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionRequiredException;
/**
* A Transactional Entity Manager delegates all requests to another Entity
* Manager. The delegate is created when it is needed for the first time in a
* transaction and later reused. The delegate is automatically closed at the end
* of the transaction.
*/
@SuppressWarnings("rawtypes")
class TransactionalEntityManager implements EntityManager {
private final TransactionManager transactionManager;
private final EntityManagerFactory entityManagerFactory;
final ThreadLocal<EntityManager> perThreadEntityManager = new ThreadLocal<EntityManager>();
volatile boolean open = true;
public TransactionalEntityManager(TransactionManager tm, EntityManagerFactory emf) {
this.transactionManager = tm;
this.entityManagerFactory = emf;
}
/**
* The delegated methods call this method to get the delegate. This method
* verifies if we're still open, if there already is an Entity Manager for
* this thread and otherwise creates it and enlists it for auto close at the
* current transaction.
*
* @return an Entity Manager
*/
private EntityManager getEM() throws IllegalStateException {
if (!open)
throw new IllegalStateException("The JPA bridge has closed");
try {
//
// Do we already have one on this thread?
//
EntityManager em = perThreadEntityManager.get();
if (em != null)
return em;
//
// Nope, so we need to check if there actually is a transaction
//
final Transaction transaction = transactionManager.getTransaction();
if (transaction == null)
throw new TransactionRequiredException("Cannot create an EM since no transaction active");
//
// Ok, now we can create one since we can also close it.
//
final Thread transactionThread = Thread.currentThread();
em = entityManagerFactory.createEntityManager();
try {
//
// Register a callback at the end of the transaction
//
transaction.registerSynchronization(new Synchronization() {
@Override
public void beforeCompletion() {
if (!open)
throw new IllegalStateException(
"The Transaction Entity Manager was closed in the mean time");
}
@Override
public void afterCompletion(int arg0) {
// TODO this is true?
assert transactionThread == Thread.currentThread();
EntityManager em = perThreadEntityManager.get();
perThreadEntityManager.set(null);
em.close();
}
});
//
// Make it available for later calls on this thread
//
perThreadEntityManager.set(em);
//
// And make sure it joins the current transaction. I guess
// this means that the Jta Data Source is used?
//
em.joinTransaction();
return em;
} catch (Exception e) {
em.close();
throw new IllegalStateException("Registering synchronization to close EM", e);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
void shutdown() {
open = false;
}
/**
* We automatically close so ignore.
*/
@Override
public void close() {
}
@Override
public void clear() {
getEM().clear();
}
@Override
public boolean contains(Object arg0) {
return getEM().contains(arg0);
}
@Override
public <T> TypedQuery<T> createNamedQuery(String arg0, Class<T> arg1) {
return getEM().createNamedQuery(arg0, arg1);
}
@Override
public Query createNamedQuery(String arg0) {
return getEM().createNamedQuery(arg0);
}
@Override
public Query createNativeQuery(String arg0, Class arg1) {
return getEM().createNativeQuery(arg0, arg1);
}
@Override
public Query createNativeQuery(String arg0, String arg1) {
return getEM().createNativeQuery(arg0, arg1);
}
@Override
public Query createNativeQuery(String arg0) {
return getEM().createNativeQuery(arg0);
}
@Override
public <T> TypedQuery<T> createQuery(CriteriaQuery<T> arg0) {
return getEM().createQuery(arg0);
}
@Override
public <T> TypedQuery<T> createQuery(String arg0, Class<T> arg1) {
return getEM().createQuery(arg0, arg1);
}
@Override
public Query createQuery(String arg0) {
return getEM().createQuery(arg0);
}
@Override
public void detach(Object arg0) {
getEM().detach(arg0);
}
@Override
public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2, Map<String, Object> arg3) {
return getEM().find(arg0, arg1, arg2, arg3);
}
@Override
public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2) {
return getEM().find(arg0, arg1, arg2);
}
@Override
public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2) {
return getEM().find(arg0, arg1, arg2);
}
@Override
public <T> T find(Class<T> arg0, Object arg1) {
return getEM().find(arg0, arg1);
}
@Override
public void flush() {
getEM().flush();
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
return getEM().getCriteriaBuilder();
}
@Override
public Object getDelegate() {
return getEM().getDelegate();
}
@Override
public EntityManagerFactory getEntityManagerFactory() {
return getEM().getEntityManagerFactory();
}
@Override
public FlushModeType getFlushMode() {
return getEM().getFlushMode();
}
@Override
public LockModeType getLockMode(Object arg0) {
return getEM().getLockMode(arg0);
}
@Override
public Metamodel getMetamodel() {
return getEM().getMetamodel();
}
@Override
public Map<String, Object> getProperties() {
return getEM().getProperties();
}
@Override
public <T> T getReference(Class<T> arg0, Object arg1) {
return getEM().getReference(arg0, arg1);
}
@Override
public EntityTransaction getTransaction() {
return getEM().getTransaction();
}
@Override
public boolean isOpen() {
return getEM().isOpen();
}
@Override
public void joinTransaction() {
getEM().joinTransaction();
}
@Override
public void lock(Object arg0, LockModeType arg1, Map<String, Object> arg2) {
getEM().lock(arg0, arg1, arg2);
}
@Override
public void lock(Object arg0, LockModeType arg1) {
getEM().lock(arg0, arg1);
}
@Override
public <T> T merge(T arg0) {
return getEM().merge(arg0);
}
@Override
public void persist(Object arg0) {
getEM().persist(arg0);
}
@Override
public void refresh(Object arg0, LockModeType arg1, Map<String, Object> arg2) {
getEM().refresh(arg0, arg1, arg2);
}
@Override
public void refresh(Object arg0, LockModeType arg1) {
getEM().refresh(arg0, arg1);
}
@Override
public void refresh(Object arg0, Map<String, Object> arg1) {
getEM().refresh(arg0, arg1);
}
@Override
public void refresh(Object arg0) {
getEM().refresh(arg0);
}
@Override
public void remove(Object arg0) {
getEM().remove(arg0);
}
@Override
public void setFlushMode(FlushModeType arg0) {
getEM().setFlushMode(arg0);
}
@Override
public void setProperty(String arg0, Object arg1) {
getEM().setProperty(arg0, arg1);
}
@Override
public <T> T unwrap(Class<T> arg0) {
return getEM().unwrap(arg0);
}
@Override
public Query createQuery(CriteriaUpdate updateQuery) {
return getEM().createQuery(updateQuery);
}
@Override
public Query createQuery(CriteriaDelete deleteQuery) {
return getEM().createQuery(deleteQuery);
}
@Override
public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
return getEM().createNamedStoredProcedureQuery(name);
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
return getEM().createStoredProcedureQuery(procedureName);
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
return getEM().createStoredProcedureQuery(procedureName, resultClasses);
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
return getEM().createStoredProcedureQuery(procedureName, resultSetMappings);
}
@Override
public boolean isJoinedToTransaction() {
return getEM().isJoinedToTransaction();
}
@Override
public <T> EntityGraph<T> createEntityGraph(Class<T> rootType) {
return getEM().createEntityGraph(rootType);
}
@Override
public EntityGraph<?> createEntityGraph(String graphName) {
return getEM().createEntityGraph(graphName);
}
@Override
public EntityGraph<?> getEntityGraph(String graphName) {
return getEM().getEntityGraph(graphName);
}
@Override
public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass) {
return getEM().getEntityGraphs(entityClass);
}
}