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)