tomee-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Svetlin Zarev (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (TOMEE-2043) Thread local transactions are left open across requests
Date Thu, 01 Jun 2017 11:53:05 GMT

    [ https://issues.apache.org/jira/browse/TOMEE-2043?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16032853#comment-16032853
] 

Svetlin Zarev commented on TOMEE-2043:
--------------------------------------

Your test case passes because the transaction is started in the TxRequired policy. While may
sample app fails, because the transaction is ".begin()"ed manually in the servlet:

{code}
 transaction.begin();
 transactionalBean.withDontRollbackOn();
 transaction.commit();
{code}

ThRequired policy: 
{code}
    public void commit() throws ApplicationException, SystemException {
        // only commit if we started the transaction
        if (clientTx == null) {
            completeTransaction(currentTx);
        } else {
            fireNonTransactionalCompletion();
        }
    }
{code}

So in the junit test case, the execution goes to the "clientTx == null" branch, while in my
case it goes to  " fireNonTransactionalCompletion();" IMO that's fine in all cases except
for if this is the outermost interceptor, because in that case there is no one to complete
the transaction.

> Thread local transactions are left open across requests
> -------------------------------------------------------
>
>                 Key: TOMEE-2043
>                 URL: https://issues.apache.org/jira/browse/TOMEE-2043
>             Project: TomEE
>          Issue Type: Bug
>          Components: TomEE Core Server
>    Affects Versions: 7.0.3
>            Reporter: Svetlin Zarev
>         Attachments: sample.zip
>
>
> @Transactional CDI bean methods annotated with     @Transactional(dontRollbackOn = SomeException.class)
do not commit the transaction at the end of the request/response cycle when the SomeException
exception is thrown. As a result the thread local transaction object is preserved across requests
which makes unrelated requests to fail with "Nested transactions are not supported".
> Sample application that reproduces the issue is attached to the ticket. Just request
the application several times and observe how the output is changing.
> Sample valve that can be used to demonstrate the issue: 
> {code}
> public final class LeakedTransactionDetectionValve extends ValveBase {
>     private static final Logger logger = Logger.getLogger(LeakedTransactionDetectionValve.class.getName());
>     @Override
>     public void invoke(Request request, Response response) throws IOException, ServletException
{
>         boolean hasActiveTransaction = false;
>         try {
>             final Collection<Transaction> transactionsBeforeRequest = getTransactions();
>             for (Transaction transaction : transactionsBeforeRequest) {
>                 if (transaction.getStatus() == Status.STATUS_ACTIVE) {
>                     hasActiveTransaction = true;
>                     break;
>                 }
>             }
>         } catch (Exception ex) {
>             //no-op
>         }
>         getNext().invoke(request, response);
>         if (!hasActiveTransaction) {
>             try {
>                 final Collection<Transaction> transactionsAfterRequest = getTransactions();
>                 for (Transaction transaction : transactionsAfterRequest) {
>                     if (transaction.getStatus() == Status.STATUS_ACTIVE) {
>                         logger.log(Level.SEVERE, "Found active transaction: "
>                                 + request.getRequestURI()
>                                 + "?"
>                                 + request.getQueryString()
>                         );
>                     }
>                 }
>             } catch (Exception ex) {
>                 logger.log(Level.SEVERE, "Failed to determine thread local transaction
status.", ex);
>             }
>         }
>     }
>     Collection<Transaction> getTransactions() throws NoSuchFieldException, IllegalAccessException
{
>         final Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
>         threadLocalsField.setAccessible(true);
>         final Object threadLocals = threadLocalsField.get(Thread.currentThread());
>         final Field tableField = threadLocals.getClass().getDeclaredField("table");
>         tableField.setAccessible(true);
>         final Object table = tableField.get(threadLocals);
>         final Collection<Transaction> transactions = new LinkedList<>();
>         for (int i = 0; i < Array.getLength(table); i++) {
>             final Object entry = Array.get(table, i);
>             if (null != entry) {
>                 final Field valueField = entry.getClass().getDeclaredField("value");
>                 valueField.setAccessible(true);
>                 final Object value = valueField.get(entry);
>                 if (value instanceof Transaction) {
>                     transactions.add((Transaction) value);
>                 }
>             }
>         }
>         return transactions;
>     }
> {code}
> PS: in addition to the issue above, the org.apache.openejb.cdi.transactional.InterceptorBase
must not wrap the exception specified in the "donotRollbackOn" attribute inside TransactionalException



--
This message was sent by Atlassian JIRA
(v6.3.15#6346)

Mime
View raw message