[ https://issues.apache.org/jira/browse/TOMEE-2043?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16032900#comment-16032900 ]
Svetlin Zarev commented on TOMEE-2043: -------------------------------------- I agree. Do you think that clearing the thread local (and not committing the transaction) and logging an error is OK ? My point is that such state should not be randomly carried across requests - a bug in one request should not make other requests to fail. > 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)