in jee-modules/jpa-module/impl/src/main/java/org/apache/myfaces/extensions/cdi/jpa/impl/transaction/TransactionalInterceptorStrategy.java [80:262]
public Object execute(InvocationContext invocationContext) throws Exception
{
Transactional transactionalAnnotation = extractTransactionalAnnotation(invocationContext);
Class<? extends Annotation> qualifierClass = getTransactionQualifier(transactionalAnnotation);
String qualifierKey = qualifierClass.getName();
// the 'layer' of the transactional invocation, aka the refCounter for the current qualifier
int transactionLayer = incrementRefCounter(qualifierKey);
if (transactionLayer == 0)
{
if (TransactionBeanStorage.getStorage() == null)
{
TransactionBeanStorage.activateNewStorage();
}
// 0 indicates that a new Context needs to get started
TransactionBeanStorage.getStorage().startTransactionScope(qualifierKey);
}
String previousTransactionKey = TransactionBeanStorage.getStorage().activateTransactionScope(qualifierKey);
EntityManager entityManager = resolveEntityManagerForQualifier(qualifierClass);
if(entityManager == null)
{
//fallback to support direct injection via @PersistenceContext
entityManager = PersistenceHelper.tryToFindEntityManagerReference(invocationContext.getTarget());
}
storeEntityManagerForQualifier(qualifierKey, entityManager);
EntityTransaction transaction = entityManager.getTransaction();
// used to store any exception we get from the services
Exception firstException = null;
try
{
if(!transaction.isActive())
{
transaction.begin();
}
return invocationContext.proceed();
}
catch(Exception e)
{
firstException = e;
// we only cleanup and rollback all open transactions in the outermost interceptor!
// this way, we allow inner functions to catch and handle exceptions properly.
if (isOutermostInterceptor())
{
HashMap<String, EntityManager> emsEntries = ems.get();
for (Map.Entry<String, EntityManager> emsEntry: emsEntries.entrySet())
{
EntityManager em = emsEntry.getValue();
transaction = em.getTransaction();
if (transaction != null && transaction.isActive())
{
try
{
transaction.rollback();
}
catch (Exception eRollback)
{
if(LOGGER.isLoggable(Level.SEVERE))
{
LOGGER.log(Level.SEVERE,
"Got additional Exception while subsequently " +
"rolling back other SQL transactions", eRollback);
}
}
}
}
// drop all EntityManagers from the ThreadLocal
ems.remove();
}
// give any extensions a chance to supply a better error message
e = prepareException(e);
// rethrow the exception
throw e;
}
finally
{
// will get set if we got an Exception while committing
// in this case, we rollback all later transactions too.
boolean commitFailed = false;
// commit all open transactions in the outermost interceptor!
// this is a 'JTA for poor men' only, and will not guaranty
// commit stability over various databases!
if (isOutermostInterceptor())
{
// only commit all transactions if we didn't rollback
// them already
if (firstException == null)
{
// but first try to flush all the transactions and write the updates to the database
for (EntityManager em: ems.get().values())
{
transaction = em.getTransaction();
if(transaction != null && transaction.isActive())
{
try
{
if (!commitFailed)
{
em.flush();
}
}
catch (Exception e)
{
firstException = e;
commitFailed = true;
break;
}
}
}
// and now either commit or rollback all transactions
for (Map.Entry<String, EntityManager> emEntry: ems.get().entrySet())
{
EntityManager em = emEntry.getValue();
transaction = em.getTransaction();
if(transaction != null && transaction.isActive())
{
try
{
if (!commitFailed)
{
transaction.commit();
}
else
{
transaction.rollback();
}
}
catch (Exception e)
{
firstException = e;
commitFailed = true;
}
}
}
ems.remove();
ems.set(null);
refCounterMaps.set(null);
refCounterMaps.remove();
// and now we close all open transactionscopes and reset the storage
TransactionBeanStorage oldStorage = TransactionBeanStorage.getStorage();
TransactionBeanStorage.resetStorage();
// we do this delayed to allow new transactions in a PreDestroy method
oldStorage.endAllTransactionScopes();
}
}
else
{
// we are NOT the outermost TransactionInterceptor
// so we have to re-activate the previous transaction
TransactionBeanStorage.getStorage().activateTransactionScope(previousTransactionKey);
}
decrementRefCounter(qualifierKey);
if (commitFailed)
{
//noinspection ThrowFromFinallyBlock
throw firstException;
}
}
}