public Object execute()

in deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/ResourceLocalTransactionStrategy.java [80:275]


    public Object execute(InvocationContext invocationContext) throws Exception
    {
        EntityManagerMetadata metadata = transactionHelper.createEntityManagerMetadata(invocationContext);
        Transactional transactionalAnnotation = transactionHelper.extractTransactionalAnnotation(invocationContext);

        Class targetClass = ProxyUtils.getUnproxiedClass(invocationContext.getTarget().getClass()); //see DELTASPIKE-517

        // all the configured qualifier keys
        Set<Class<? extends Annotation>> emQualifiers = emHolder.isSet() ?
                new HashSet<Class<? extends Annotation>>(Arrays.asList(Default.class)) :
                transactionHelper.resolveEntityManagerQualifiers(metadata, targetClass);

        TransactionBeanStorage transactionBeanStorage = TransactionBeanStorage.getInstance();

        boolean isOutermostInterceptor = transactionBeanStorage.isEmpty();
        boolean startedTransaction = 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();
                    startedTransaction = 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;

            // 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)
            {
                Set<EntityManagerEntry> entityManagerEntryList =
                    transactionBeanStorage.getUsedEntityManagerEntries();

                if (startedTransaction)
                {
                    // 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;

            // commit all open transactions in the outermost interceptor!
            // For Resource-local this is a 'JTA for poor men' only, and will not guaranty
            // commit stability over various databases!
            // In case of JTA we will just commit the UserTransaction.
            if (isOutermostInterceptor)
            {
                if (startedTransaction)
                {
                    // 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.

                    if (firstException == null)
                    {
                        // only commit all transactions if we didn't rollback
                        // them already
                        Set<EntityManagerEntry> entityManagerEntryList =
                            transactionBeanStorage.getUsedEntityManagerEntries();

                        boolean rollbackOnly = metadata.isReadOnly() || 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);
            }
        }
    }