You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Claus Ibsen (Jira)" <ji...@apache.org> on 2026/07/16 11:35:00 UTC

[jira] [Assigned] (CAMEL-24128) camel-jpa: JpaPollingConsumer.receive() leaks an EntityManager on every call

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

Claus Ibsen reassigned CAMEL-24128:
-----------------------------------

    Assignee: Claus Ibsen

> camel-jpa: JpaPollingConsumer.receive() leaks an EntityManager on every call
> ----------------------------------------------------------------------------
>
>                 Key: CAMEL-24128
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24128
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-jpa
>            Reporter: Federico Mariani
>            Assignee: Claus Ibsen
>            Priority: Major
>
> h3. Problem
> {{JpaPollingConsumer.receive()}} (components/camel-jpa/src/main/java/org/apache/camel/component/jpa/JpaPollingConsumer.java:126-132) resolves its {{EntityManager}} via:
> {code:java}
> final EntityManager entityManager = getTargetEntityManager(null, entityManagerFactory,
>         getEndpoint().isUsePassedInEntityManager(), getEndpoint().isSharedEntityManager(), true);
> ...
> exchange.getIn().setHeader(JpaConstants.ENTITY_MANAGER, entityManager);
> {code}
> Because the {{exchange}} argument passed to {{JpaHelper.getTargetEntityManager}} is {{null}}, {{JpaHelper.createEntityManager()}} (JpaHelper.java:101-119) takes the {{exchange == null}} branch and skips registering a {{JpaCloseEntityManagerOnCompletion}} synchronization -- that registration only happens when an exchange is supplied.
> {{JpaPollingConsumer}} itself never closes the {{EntityManager}} it created: the only close call in {{receive()}} is on the {{PersistenceException}} catch path (line 172). On the success path the EM is simply left open and referenced only via the {{CamelEntityManager}} header on the returned exchange.
> h3. Impact
> Every successful call to a JPA polling consumer (e.g. {{pollEnrich("jpa:...")}}, or a direct {{PollingConsumer.receive()}}/{{receiveNoWait()}}/{{receive(timeout)}}) leaks one {{EntityManager}}. Each leaked EM retains its persistence context (all entities loaded during the query) until garbage collected, and depending on the JPA provider / connection-release-mode may also pin a JDBC connection. In a route that polls frequently this is a slow, silent resource leak.
> h3. How to reproduce
> # Configure a {{JpaComponent}} with a real {{EntityManagerFactory}}.
> # Call {{context.createPollingConsumer("jpa:com.foo.MyEntity").receive()}} (or use {{pollEnrich}}) repeatedly.
> # Observe (e.g. via a custom {{EntityManagerFactory}} wrapper, or provider statistics) that {{EntityManager#close()}} is never invoked for the EMs created by {{receive()}} on the success path.
> h3. Suggested fix
> Pass the created {{exchange}} into {{getTargetEntityManager}} before calling it (so the on-completion synchronization gets registered), or explicitly close the EM (and remove the header) once the transaction/exchange life-cycle in {{receive()}} completes.
> Found during a broader camel-jpa code review (July 2026).
> _Claude Code on behalf of Croway_



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