shashank created CAMEL-25698:
--------------------------------

             Summary: camel-jta - markRollbackOnlyLast() in a transacted() 
route commits the transaction instead of rolling it back
                 Key: CAMEL-25698
                 URL: https://issues.apache.org/jira/browse/CAMEL-25698
             Project: Camel
          Issue Type: Bug
            Reporter: shashank
            Assignee: shashank


In a {{transacted()}} route that runs with a JTA transaction manager, the 
transaction error handler of camel-jta 
({{org.apache.camel.jta.TransactionErrorHandler}}) decides after routing 
whether the transaction is rolled back. At main 7d31f3b0d8c7 
({{doInTransactionTemplate}}, line 215) it rolls back only when
{code:java}
exchange.getException() != null || exchange.isRollbackOnly()
{code}
{{markRollbackOnlyLast()}} sets only the rollback-only-last flag 
({{RollbackProcessor}}: {{exchange.setRollbackOnlyLast(true)}}, no exception), 
and {{Exchange.isRollbackOnly()}} has returned only the rollback-only flag 
since Camel 3. So the transaction policy sees a normal return and commits. 
Afterwards {{processInTransaction}} clears the flag (and the exception) as it 
should for rollback only last, so nothing on the exchange shows that the 
rollback was skipped. The database changes and the messages sent inside the 
transaction are committed, although the route asked for them to be rolled back.

CAMEL-17105 fixed exactly this in the camel-spring {{TransactionErrorHandler}} 
(its condition is {{exchange.getException() != null || 
exchange.isRollbackOnly() || exchange.isRollbackOnlyLast()}}, line 224 on 
main); the camel-jta copy of the class was not changed. CAMEL-25588 (merged on 
2026-10-15) makes the SQL, MyBatis and jOOQ consumers honor rollback only last, 
on the stated assumption that a {{transacted()}} block has consumed the flag; 
with camel-jta it consumes the flag without rolling back.

Trigger: any {{transacted()}} route with a JTA transaction manager (camel-jta 
on the classpath) that calls {{markRollbackOnlyLast()}}, the use its javadoc 
describes: nested transactions where only the last (inner) transaction is 
rolled back and the outer one still completes. {{markRollbackOnly()}} and 
exceptions are not affected.

h3. Reproduction

New {{TransactionErrorHandlerRollbackOnlyLastTest}} in camel-jta: a 
{{TransactionErrorHandler}} with a {{JtaTransactionPolicy}} that records 
whether its {{Runnable}} returned (commit) or threw (rollback), once around a 
processor that calls {{exchange.setRollbackOnlyLast(true)}} (what 
{{markRollbackOnlyLast()}} does), and once as a route 
{{from("direct:start").transacted("recordingPolicy").setBody(constant("done")).markRollbackOnlyLast()}}.
 On main the policy commits in both ({{The transaction should be rolled back 
==> expected: <1> but was: <0>}}).

h3. Proposed fix

Add {{exchange.isRollbackOnlyLast()}} to the rollback condition, as in 
camel-spring. The flag and the exception are still cleared after the 
transaction, so an outer {{transacted()}} block does not see them. Upgrade 
guide 4.24: a short note, as routes that used {{markRollbackOnlyLast()}} with 
JTA now roll back.

camel-jta unit tests with the fix: 7 run, 0 failures, 0 skipped. Not run: the 
module's integration test (it needs PostgreSQL), a real JTA transaction manager.

How it was found: reviewing the rollback-only changes merged this week 
(CAMEL-25550, CAMEL-25588) and the transaction error handlers they rely on; the 
camel-spring and camel-jta copies differ in this condition. A small probe and 
the new test confirmed it.

Not changed: other consumers that commit on their own (camel-jms and camel-sjms 
with {{transacted=true}}, camel-spring-rabbitmq, camel-nats, the camel-kafka 
transaction synchronization) check {{isRollbackOnly()}} but not 
{{isRollbackOnlyLast()}}; whether they should follow CAMEL-25588 is a separate 
question.

Affected: main (7d31f3b0d8c7; tested). camel-4.23.x (line 215), camel-4.22.x 
(line 213), camel-4.18.x and camel-4.14.x (line 201) have the same condition 
(read, not run).

Duplicate check (2026-10-16): JIRA text "rollbackOnlyLast", 
"markRollbackOnlyLast", "rollback only last" (CAMEL-17105 camel-spring only, 
CAMEL-25588, CAMEL-25550, CAMEL-3371, CAMEL-3373), "camel-jta" with "rollback" 
(CAMEL-25504, CAMEL-23234 shutdown, CAMEL-17443 markRollbackOnly does not 
abort), "JtaTransactionPolicy"; GitHub pull requests "CAMEL-17105" (#6300, 
camel-spring only), "TransactionErrorHandler jta", "rollbackOnlyLast jta": 
nothing about the camel-jta condition.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to