This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 3480b7cea781 CAMEL-21438: Fix racy JpaPollingConsumerLockEntityTest 
and fact-check jpa-component.adoc
3480b7cea781 is described below

commit 3480b7cea781cb4f3ca6ece52597a893a021e130
Author: Adriano Machado <[email protected]>
AuthorDate: Thu Jul 9 11:39:24 2026 -0400

    CAMEL-21438: Fix racy JpaPollingConsumerLockEntityTest and fact-check 
jpa-component.adoc
    
    Closes #24566
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 .../camel-jpa/src/main/docs/jpa-component.adoc     | 82 ++++++++++++++++++----
 .../jpa/JpaPollingConsumerLockEntityTest.java      | 11 ++-
 2 files changed, 77 insertions(+), 16 deletions(-)

diff --git a/components/camel-jpa/src/main/docs/jpa-component.adoc 
b/components/camel-jpa/src/main/docs/jpa-component.adoc
index d01cbc0a6294..43627d4dff20 100644
--- a/components/camel-jpa/src/main/docs/jpa-component.adoc
+++ b/components/camel-jpa/src/main/docs/jpa-component.adoc
@@ -37,7 +37,7 @@ jpa:entityClassName[?options]
 ----
 
 For sending to the endpoint, the _entityClassName_ is optional. If
-specified, it helps the http://camel.apache.org/type-converter.html[Type 
Converter] to
+specified, it helps the xref:manual::type-converter.adoc[Type Converter] to
 ensure the body is of the correct type.
 
 For consuming, the _entityClassName_ is mandatory.
@@ -56,7 +56,7 @@ include::partial$component-endpoint-headers.adoc[]
 You can store a Java entity bean in a database by sending it to a JPA
 producer endpoint. The body of the _In_ message is assumed to be an
 entity bean (that is a POJO with an
-https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entity[@Entity]
+https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/entity[@Entity]
 annotation on it) or a collection or array of entity beans.
 
 If the body is a List of entities, make sure to use
@@ -150,6 +150,45 @@ explicitly configure a JPA component that references the
 </bean>
 ----
 
+=== Entity enhancement
+
+JPA providers such as OpenJPA need to enhance (byte-code weave) your `@Entity` 
classes so they
+can track field access and manage lazy loading. Without enhancement, you may 
see errors such as
+`ArgumentException: ... were not enhanced at build time or at class load time 
with a javaagent`
+when the entity is first used.
+
+OpenJPA supports enhancing entities either dynamically at runtime (for example 
through a
+`-javaagent`, or automatic class-loading hooks in a Jakarta EE/OSGi container) 
or at build time.
+https://openjpa.apache.org/entity-enhancement.html[Build-time enhancement] is 
recommended: it is
+faster and more reliable than dynamic enhancement, which OpenJPA's own 
documentation discourages
+for production use due to known performance and functional issues.
+
+To enable build-time enhancement with Maven, add the `openjpa-maven-plugin` to 
your `pom.xml`
+bound to the `process-classes` phase:
+
+[source,xml]
+----
+<plugin>
+    <groupId>org.apache.openjpa</groupId>
+    <artifactId>openjpa-maven-plugin</artifactId>
+    <!-- use the same version as your OpenJPA runtime dependency -->
+    <version>4.1.1</version>
+    <executions>
+        <execution>
+            <id>enhancer</id>
+            <phase>process-classes</phase>
+            <goals>
+                <goal>enhance</goal>
+            </goals>
+        </execution>
+    </executions>
+</plugin>
+----
+
+See https://openjpa.apache.org/enhancement-with-maven.html[Entity Enhancement 
with Maven] for the
+full set of plugin options (such as scoping enhancement to specific packages 
with
+`includes`/`excludes`) and for the `maven-antrun-plugin` alternative.
+
 === Using a consumer with a named query
 
 For consuming only selected entities, you can use the
@@ -431,23 +470,40 @@ the message body.
 
 === Using the JPA-Based Idempotent Repository
 
-The Idempotent Consumer from the 
http://camel.apache.org/enterprise-integration-patterns.html[EIP patterns] is 
used to filter out duplicate messages. A JPA-based idempotent repository is 
provided.
+Camel supports the Idempotent Consumer EIP, which is used to filter out 
duplicate messages. A JPA-based idempotent repository is provided.
 
 To use the JPA based idempotent repository.
 
 .Procedure
 
 . Set up a `persistence-unit` in the persistence.xml file:
++
+[source,xml]
+----
+<persistence-unit name="idempotentDb" transaction-type="RESOURCE_LOCAL">
+    <class>org.apache.camel.processor.idempotent.jpa.MessageProcessed</class>
 
-. Set up a `org.springframework.orm.jpa.JpaTemplate`
-which is used by the
-`org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository`:
-
-. Configure the error formatting macro: snippet: 
java.lang.IndexOutOfBoundsException:
-Index: 20, Size: 20
+    <properties>
+        <property name="openjpa.ConnectionURL" 
value="jdbc:h2:./target/idempotentTest;DB_CLOSE_DELAY=-1"/>
+        <property name="openjpa.ConnectionDriverName" value="org.h2.Driver"/>
+        <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
+    </properties>
+</persistence-unit>
+----
 
-. Configure the idempotent repository:
-`org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository`:
+. Configure the 
`org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository` bean,
+referencing the `EntityManagerFactory` built from that persistence unit:
++
+[source,xml]
+----
+<bean id="jpaStore" 
class="org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository">
+    <!-- Here we refer to the entityManagerFactory -->
+    <constructor-arg index="0" ref="entityManagerFactory"/>
+    <!-- This 2nd parameter is the name (a category name).
+         You can have different repositories with different names -->
+    <constructor-arg index="1" value="myProcessorName"/>
+</bean>
+----
 
 . Create the JPA idempotent repository in the Spring XML file:
 
@@ -520,10 +576,10 @@ 
https://github.com/apache/camel/blob/main/components/camel-jpa/pom.xml[enhance
 the byte-code at build time]. To overcome this, you need to enable
 http://openjpa.apache.org/entity-enhancement.html#dynamic-enhancement[dynamic
 byte-code enhancement of OpenJPA]. For example, assuming the current
-OpenJPA version being used in Camel is 2.2.1, to run the
+OpenJPA version being used in Camel is 4.1.1, to run the
 tests inside your IDE, you would need to pass the following
 argument to the JVM:
 
 ----
--javaagent:<path_to_your_local_m2_cache>/org/apache/openjpa/openjpa/2.2.1/openjpa-2.2.1.jar
+-javaagent:<path_to_your_local_m2_cache>/org/apache/openjpa/openjpa/4.1.1/openjpa-4.1.1.jar
 ----
diff --git 
a/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaPollingConsumerLockEntityTest.java
 
b/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaPollingConsumerLockEntityTest.java
index 3cc1bff20dbd..5e6126cb88dd 100644
--- 
a/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaPollingConsumerLockEntityTest.java
+++ 
b/components/camel-jpa/src/test/java/org/apache/camel/processor/jpa/JpaPollingConsumerLockEntityTest.java
@@ -50,7 +50,10 @@ public class JpaPollingConsumerLockEntityTest extends 
AbstractJpaTest {
     public void testPollingConsumerWithLock() throws Exception {
 
         MockEndpoint mock = getMockEndpoint("mock:locked");
-        mock.expectedBodiesReceived(
+        // The two concurrent requests race for the optimistic lock, so 
whichever one wins the
+        // uncontended read (and thus produces "orders: 1") is not 
deterministic, and the winner's
+        // exchange is not guaranteed to reach the mock endpoint first under 
CI-level thread contention.
+        mock.expectedBodiesReceivedInAnyOrder(
                 "orders: 1",
                 "orders: 2");
 
@@ -104,8 +107,10 @@ public class JpaPollingConsumerLockEntityTest extends 
AbstractJpaTest {
 
                 from("direct:locked")
                         .onException(OptimisticLockException.class)
-                        .redeliveryDelay(60)
-                        .maximumRedeliveries(2)
+                        // Generous budget so the losing side of the 
optimistic-lock race has enough
+                        // room to succeed even under heavy CI host contention.
+                        .redeliveryDelay(100)
+                        .maximumRedeliveries(10)
                         .end()
                         .pollEnrich()
                         .simple("jpa://" + Customer.class.getName()

Reply via email to