Search in sources :

Example 1 with TransactionBeanStorage

use of org.apache.deltaspike.jpa.impl.transaction.context.TransactionBeanStorage in project deltaspike by apache.

the class ResourceLocalTransactionStrategy method execute.

@Override
public Object execute(InvocationContext invocationContext) throws Exception {
    Transactional transactionalAnnotation = transactionHelper.extractTransactionalAnnotation(invocationContext);
    //see DELTASPIKE-517
    Class targetClass = ProxyUtils.getUnproxiedClass(invocationContext.getTarget().getClass());
    // all the configured qualifier keys
    Set<Class<? extends Annotation>> emQualifiers = emHolder.isSet() ? new HashSet<Class<? extends Annotation>>(Arrays.asList(Default.class)) : transactionHelper.resolveEntityManagerQualifiers(transactionalAnnotation, targetClass);
    TransactionBeanStorage transactionBeanStorage = TransactionBeanStorage.getInstance();
    boolean isOutermostInterceptor = transactionBeanStorage.isEmpty();
    boolean outermostTransactionAlreadyExisted = false;
    if (isOutermostInterceptor) {
        // a new Context needs to get started
        transactionBeanStorage.startTransactionScope();
    }
    // the 'layer' of the transactional invocation, aka the refCounter
    @SuppressWarnings("UnusedDeclaration") int transactionLayer = transactionBeanStorage.incrementRefCounter();
    Exception firstException = null;
    try {
        for (Class<? extends Annotation> emQualifier : emQualifiers) {
            EntityManager entityManager = resolveEntityManagerForQualifier(emQualifier);
            EntityManagerEntry entityManagerEntry = createEntityManagerEntry(entityManager, emQualifier);
            transactionBeanStorage.storeUsedEntityManager(entityManagerEntry);
            EntityTransaction transaction = getTransaction(entityManagerEntry);
            if (!transaction.isActive()) {
                beforeBegin(invocationContext, entityManagerEntry, transaction);
                transaction.begin();
            } else if (isOutermostInterceptor) {
                outermostTransactionAlreadyExisted = true;
            }
            //don't move it before EntityTransaction#begin() and invoke it in any case
            beforeProceed(invocationContext, entityManagerEntry, transaction);
        }
        return invocationContext.proceed();
    } catch (Exception e) {
        firstException = e;
        // this way, we allow inner functions to catch and handle exceptions properly.
        if (isOutermostInterceptor) {
            Set<EntityManagerEntry> entityManagerEntryList = transactionBeanStorage.getUsedEntityManagerEntries();
            if (!outermostTransactionAlreadyExisted) {
                // We only commit transactions we opened ourselfs.
                // If the transaction got opened outside of our interceptor chain
                // we must not handle it.
                // This e.g. happens if a Stateless EJB invokes a Transactional CDI bean
                // which uses the BeanManagedUserTransactionStrategy.
                rollbackAllTransactions(entityManagerEntryList);
            }
            // drop all EntityManagers from the request-context cache
            transactionBeanStorage.cleanUsedEntityManagers();
        }
        // 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;
        // In case of JTA we will just commit the UserTransaction.
        if (isOutermostInterceptor) {
            if (!outermostTransactionAlreadyExisted) {
                if (firstException == null) {
                    // only commit all transactions if we didn't rollback
                    // them already
                    Set<EntityManagerEntry> entityManagerEntryList = transactionBeanStorage.getUsedEntityManagerEntries();
                    boolean rollbackOnly = isRollbackOnly(transactionalAnnotation);
                    if (!rollbackOnly && entityManagerEntryList.size() > 1) {
                        // but first try to flush all the transactions and write the updates to the database
                        for (EntityManagerEntry currentEntityManagerEntry : entityManagerEntryList) {
                            EntityTransaction transaction = getTransaction(currentEntityManagerEntry);
                            if (transaction != null && transaction.isActive()) {
                                try {
                                    if (!commitFailed) {
                                        currentEntityManagerEntry.getEntityManager().flush();
                                        if (!rollbackOnly && transaction.getRollbackOnly()) {
                                            // don't set commitFailed to true directly
                                            // (the order of the entity-managers isn't deterministic
                                            //  -> tests would break)
                                            rollbackOnly = true;
                                        }
                                    }
                                } catch (Exception e) {
                                    firstException = e;
                                    commitFailed = true;
                                    break;
                                }
                            }
                        }
                    }
                    if (rollbackOnly) {
                        commitFailed = true;
                    }
                    // and now either commit or rollback all transactions
                    for (EntityManagerEntry currentEntityManagerEntry : entityManagerEntryList) {
                        EntityTransaction transaction = getTransaction(currentEntityManagerEntry);
                        if (transaction != null && transaction.isActive()) {
                            try {
                                // last chance to check it (again)
                                if (commitFailed || transaction.getRollbackOnly()) {
                                    beforeRollback(invocationContext, currentEntityManagerEntry, transaction);
                                    transaction.rollback();
                                } else {
                                    beforeCommit(invocationContext, currentEntityManagerEntry, transaction);
                                    transaction.commit();
                                }
                            } catch (Exception e) {
                                firstException = e;
                                commitFailed = true;
                            } finally {
                                afterProceed(invocationContext, currentEntityManagerEntry, firstException);
                            }
                        }
                    }
                }
            }
            // and now we close the open transaction scope
            transactionBeanStorage.endTransactionScope();
            onCloseTransactionScope();
        }
        transactionBeanStorage.decrementRefCounter();
        if (commitFailed && firstException != null) /*null if just #getRollbackOnly is true*/
        {
            throwException(firstException);
        }
    }
}
Also used : EntityTransaction(javax.persistence.EntityTransaction) Set(java.util.Set) HashSet(java.util.HashSet) TransactionBeanStorage(org.apache.deltaspike.jpa.impl.transaction.context.TransactionBeanStorage) Annotation(java.lang.annotation.Annotation) EntityManager(javax.persistence.EntityManager) EntityManagerEntry(org.apache.deltaspike.jpa.impl.transaction.context.EntityManagerEntry) Transactional(org.apache.deltaspike.jpa.api.transaction.Transactional)

Aggregations

Annotation (java.lang.annotation.Annotation)1 HashSet (java.util.HashSet)1 Set (java.util.Set)1 EntityManager (javax.persistence.EntityManager)1 EntityTransaction (javax.persistence.EntityTransaction)1 Transactional (org.apache.deltaspike.jpa.api.transaction.Transactional)1 EntityManagerEntry (org.apache.deltaspike.jpa.impl.transaction.context.EntityManagerEntry)1 TransactionBeanStorage (org.apache.deltaspike.jpa.impl.transaction.context.TransactionBeanStorage)1