Federico Mariani created CAMEL-24132:
----------------------------------------
Summary: camel-jpa: medium-severity findings from July 2026
component review (umbrella)
Key: CAMEL-24132
URL: https://issues.apache.org/jira/browse/CAMEL-24132
Project: Camel
Issue Type: Bug
Components: camel-jpa
Reporter: Federico Mariani
h3. Overview
This is an umbrella issue tracking 7 medium-severity findings from a broader
code review of {{camel-jpa}} (July 2026), grouped here rather than filed
individually since none is independently critical. High-severity findings from
the same review were filed separately as standalone issues.
h3. M1 -- JpaPollingConsumer.receiveNoWait() can block indefinitely
JpaPollingConsumer.java:186-189 implements {{receiveNoWait()}} by simply
delegating to {{receive()}}, which runs a full transaction and acquires a
{{PESSIMISTIC_WRITE}} lock by default -- it can block on row locks for an
unbounded time. This violates the {{PollingConsumer}} contract that
{{receiveNoWait()}} returns immediately. At minimum it should apply a NOWAIT
lock hint (or no lock) rather than reusing {{receive()}} as-is.
h3. M2 -- receive(timeout) never cancels the background task on timeout
JpaPollingConsumer.java:192-212: on {{TimeoutException}}, the method returns
{{null}} but never calls {{future.cancel(true)}}. The submitted task keeps
running in the background -- holding/awaiting DB locks -- and, once it
eventually completes, leaks its {{EntityManager}} (see the related EM-leak
issue) and its {{Exchange}}. Under repeated timeouts (e.g. {{pollEnrich}}
against a locked table with a short timeout), abandoned tasks accumulate in the
shared, bounded thread pool, and later polls start timing out purely from
queueing.
h3. M3 -- JpaPollingConsumer + nativeQuery + default consumeLockEntity always
fails
JpaPollingConsumer.java:143-145 calls {{innerQuery.setLockMode(...)}} whenever
{{consumeLockEntity}} is {{true}} (the default). Per the JPA spec,
{{Query.setLockMode}} throws {{IllegalStateException}} for anything other than
a JPQL SELECT / CriteriaQuery -- i.e. for every native query (and for
update-style named queries). {{pollEnrich("jpa:X?nativeQuery=...")}} can never
succeed without the caller explicitly setting {{consumeLockEntity=false}}. The
call should be guarded (skipped for native queries), or the requirement should
be documented prominently.
h3. M4 -- JpaMessageIdRepository.add() turns a concurrent-insert race into a
failed exchange instead of a duplicate signal
JpaMessageIdRepository.java:102-111 is check-then-insert. {{MessageProcessed}}
has a unique constraint on {{(processorName, messageId)}}, so when two
exchanges race on the same id (parallel threads or a clustered deployment
sharing the table), the loser hits a constraint violation at {{flush()}}, which
the {{catch}} block wraps and rethrows as a {{PersistenceException}} --
*failing the exchange* -- instead of treating it as "duplicate detected" and
returning {{false}}. For a repository whose entire purpose is cluster-safe
idempotency, the constraint-violation race should be caught and mapped to
{{return false}}.
h3. M5 -- the CAMEL-22534 parallel-EntityManager fix only covers the Splitter
CAMEL-22534 made the Splitter strip the {{CamelEntityManager}} exchange
property from parallel sub-exchange copies. {{multicast(parallelProcessing)}},
{{recipientList(parallel)}} and {{wireTap}} still copy exchange properties by
default, so parallel branches through those EIPs can share the same
non-thread-safe {{HashMap<String, EntityManager>}}
({{JpaHelper.getEntityManagerMap}}) *and* the same {{EntityManager}} instances
across threads -- both concurrent EM use and map corruption are possible.
Consider moving the property-strip into a shared place (e.g.
{{MulticastProcessor}}), or making the JPA helper's map thread-safe /
per-thread.
h3. M6 -- JpaProducer.isUseExecuteUpdate() misclassifies legitimate SELECTs
JpaProducer.java:145-164 decides between {{executeUpdate()}} and
{{getResultList()}} by testing whether the raw query string starts with
{{"select"}} (case-insensitive, offset 0, no trimming). A query with leading
whitespace/newline, a native CTE ({{WITH x AS (...) SELECT ...}}), or a
parenthesized select is misclassified as an update and routed to
{{executeUpdate()}}, which the JPA provider rejects. The string should be
trimmed first, and {{WITH}} should also be recognized as a select-style query
(or, more robustly, only auto-detect the update keywords
{{insert|update|delete}} and default to select otherwise).
h3. Note
A 7th medium finding from the same review (header-supplied query parameters
being auto-evaluated as a Simple expression) is *not* included here: it falls
under the project's documented in-scope security vulnerability classes
(expression injection where the framework itself evaluates untrusted input) and
is being routed through the private SECURITY.md disclosure process instead of a
public issue.
See also the four related high-severity issues filed from the same review.
_Claude Code on behalf of Croway_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)