[
https://issues.apache.org/jira/browse/CAMEL-24128?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen resolved CAMEL-24128.
---------------------------------
Resolution: Fixed
> 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
> Fix For: 4.22.0
>
>
> 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)