You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by "Svetlin Zarev (JIRA)" <ji...@apache.org> on 2017/06/01 13:00:07 UTC

[jira] [Resolved] (TOMEE-2043) Thread local transactions are left open across requests

     [ https://issues.apache.org/jira/browse/TOMEE-2043?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Svetlin Zarev resolved TOMEE-2043.
----------------------------------
    Resolution: Not A Problem

> 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)