You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2022/05/13 08:19:19 UTC

[camel-spring-boot-examples] 01/02: Add Narayana example

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

acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git

commit 3aa966e26653c947712fdb90e9f3f622fb5523ab
Author: Federico Valeri <fe...@gmail.com>
AuthorDate: Mon May 9 10:53:20 2022 +0200

    Add Narayana example
    
    Signed-off-by: Federico Valeri <fe...@gmail.com>
---
 README.adoc                                        |   4 +-
 pom.xml                                            |   2 +
 spring-boot-jta-jpa/pom.xml                        | 145 +++++++++++++++++++++
 spring-boot-jta-jpa/readme.adoc                    |  82 ++++++++++++
 .../src/main/java/sample/camel/Application.java    |  50 +++++++
 .../src/main/java/sample/camel/AuditLog.java       |  54 ++++++++
 .../src/main/java/sample/camel/CamelRoutes.java    |  66 ++++++++++
 .../src/main/java/sample/camel/RMFactory.java      |  27 ++++
 .../src/main/resources/application.properties      |  27 ++++
 .../src/main/resources/jbossts-properties.xml      |  45 +++++++
 spring-boot-jta-jpa/src/main/resources/logback.xml |  35 +++++
 .../src/main/resources/spring-camel.xml            | 113 ++++++++++++++++
 12 files changed, 649 insertions(+), 1 deletion(-)

diff --git a/README.adoc b/README.adoc
index 975403b..58da1c5 100644
--- a/README.adoc
+++ b/README.adoc
@@ -27,7 +27,7 @@ readme's instructions.
 === Examples
 
 // examples: START
-Number of Examples: 55 (0 deprecated)
+Number of Examples: 56 (0 deprecated)
 
 [width="100%",cols="4,2,4",options="header"]
 |===
@@ -35,6 +35,8 @@ Number of Examples: 55 (0 deprecated)
 
 | link:health-checks/readme.adoc[Health Checks] (health-checks) |  | 
 
+| link:spring-boot-jta-jpa/readme.adoc[Spring Boot Jta Jpa] (spring-boot-jta-jpa) |  | An example showing JTA with Spring Boot
+
 | link:undertow-spring-security/readme.adoc[Undertow Spring Security] (undertow-spring-security) | Advanced | Example on how to use the Camel Undertow component with spring security and Keycloak
 
 | link:webhook/readme.adoc[Webhook] (webhook) | Advanced | Example on how to use the Camel Webhook component
diff --git a/pom.xml b/pom.xml
index c52d5ad..8e6d9b7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,7 @@
 
 	<modules>
 		<module>spring-boot</module>
+		<module>spring-boot-jta-jpa</module>
 		<module>activemq</module>
 		<module>actuator-http-metrics</module>
 		<module>amqp</module>
@@ -101,6 +102,7 @@
 		<reactor-version>3.2.16.RELEASE</reactor-version>
 		<testcontainers-version>1.16.3</testcontainers-version>
 		<hapi-structures-v24-version>2.3</hapi-structures-v24-version>
+		<narayana-spring-boot-version>2.6.3</narayana-spring-boot-version>
 	</properties>
 
 	<repositories>
diff --git a/spring-boot-jta-jpa/pom.xml b/spring-boot-jta-jpa/pom.xml
new file mode 100644
index 0000000..768bf33
--- /dev/null
+++ b/spring-boot-jta-jpa/pom.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel.springboot.example</groupId>
+        <artifactId>examples</artifactId>
+        <version>3.17.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>spring-boot-jta-jpa</artifactId>
+    <name>Camel SB Examples :: JTA</name>
+    <description>An example showing JTA with Spring Boot</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <node.identifier>${project.artifactId}-0</node.identifier>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot-version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.springboot</groupId>
+                <artifactId>camel-spring-boot-bom</artifactId>
+                <version>${camel-version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>me.snowdrop</groupId>
+            <artifactId>narayana-spring-boot-starter</artifactId>
+            <version>${narayana-spring-boot-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>me.snowdrop</groupId>
+            <artifactId>narayana-spring-boot-recovery-controller</artifactId>
+            <version>${narayana-spring-boot-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-servlet-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-jpa-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-jms-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.messaginghub</groupId>
+            <artifactId>pooled-jms</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.activemq</groupId>
+            <artifactId>artemis-jms-client</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot-version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/spring-boot-jta-jpa/readme.adoc b/spring-boot-jta-jpa/readme.adoc
new file mode 100644
index 0000000..f9f8a9a
--- /dev/null
+++ b/spring-boot-jta-jpa/readme.adoc
@@ -0,0 +1,82 @@
+== Camel Example Spring Boot JTA
+
+This example demonstrates how to run a Camel Service on Spring Boot that supports JTA transactions on two external transactional resources: a database (MySQL) and a message broker (Artemis).
+
+We use Narayana as standalone JTA Transaction Manager implementation, and Hibernate as JPA Adapter.
+Most of the configuration is in `src/main/resources/spring-camel.xml` with no auto-configuration magic, in order to show all components needed to support distributed transactions without a full blown application server.
+
+=== External systems
+
+Start MySQL:
+
+----
+WORK_DIR="$HOME/.local/mysql"
+docker run --name db-mysql \
+  -e MYSQL_ROOT_PASSWORD=root \
+  -v $WORK_DIR:/var/lib/mysql \
+  -d -p 3306:3306 mysql
+docker exec -it db-mysql mysql -uroot -proot -e \
+  "CREATE DATABASE testdb CHARACTER SET utf8mb4;
+   CREATE USER 'admin'@'%' IDENTIFIED WITH mysql_native_password BY 'admin';
+   GRANT CREATE,SELECT,INSERT,UPDATE,DELETE ON testdb.* TO 'admin'@'%';
+   GRANT XA_RECOVER_ADMIN on *.* to 'admin'@'%';
+   FLUSH PRIVILEGES;"
+docker exec -it db-mysql mysql testdb -uadmin -padmin -e \
+  "CREATE TABLE IF NOT EXISTS audit_log (
+    id SERIAL PRIMARY KEY,
+    message VARCHAR(255) NOT NULL
+  );"
+----
+
+Start Artemis:
+
+----
+ARTEMIS_URL="https://archive.apache.org/dist/activemq/activemq-artemis/2.22.0/apache-artemis-2.22.0-bin.tar.gz"
+ARTEMIS_HOME="/tmp/artemis" && mkdir -p $ARTEMIS_HOME $ARTEMIS_HOME/servers/server0
+curl -sL $ARTEMIS_URL | tar xz -C $ARTEMIS_HOME --strip-components 1
+export PATH="$ARTEMIS_HOME/bin:$ARTEMIS_HOME/servers/server0/bin:$PATH"
+artemis create $ARTEMIS_HOME/servers/server0 --name server0 --user admin --password admin --require-login
+artemis-service start
+----
+
+=== How to run
+
+You can run this example using (every instance must have a unique identifier):
+
+----
+mvn spring-boot:run -Dnode.identifier="server0"
+----
+
+Test the service endpoint from another terminal:
+
+----
+ADDRESS="http://localhost:8080"
+curl -X POST $ADDRESS/api/message/hello
+curl $ADDRESS/api/messages
+----
+
+Test rollback by calling the service with "fail" content:
+
+----
+curl -X POST $ADDRESS/api/message/fail
+----
+
+You should not find any trace of the message in the `audit_log` table.
+
+=== To get health check
+
+To show a summary of spring boot health check:
+
+----
+curl http://localhost:8080/actuator/health
+----
+
+See the `application.properties` to control what information to present in actuator.
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please https://camel.apache.org/support.html[let us know].
+
+We also love contributors, so https://camel.apache.org/contributing.html[get involved] :-)
+
+The Camel riders!
diff --git a/spring-boot-jta-jpa/src/main/java/sample/camel/Application.java b/spring-boot-jta-jpa/src/main/java/sample/camel/Application.java
new file mode 100644
index 0000000..cde4ccf
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/java/sample/camel/Application.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.camel;
+
+import me.snowdrop.boot.narayana.autoconfigure.NarayanaConfiguration;
+import org.apache.camel.component.jms.springboot.JmsComponentAutoConfiguration;
+import org.apache.camel.component.jpa.springboot.JpaComponentAutoConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration;
+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.ImportResource;
+
+@SpringBootConfiguration
+@ComponentScan
+@EnableAutoConfiguration(exclude = {
+		NarayanaConfiguration.class,
+		JtaAutoConfiguration.class,
+		DataSourceAutoConfiguration.class,
+		DataSourceTransactionManagerAutoConfiguration.class,
+		HibernateJpaAutoConfiguration.class,
+		JpaComponentAutoConfiguration.class,
+		ArtemisAutoConfiguration.class,
+		JmsComponentAutoConfiguration.class
+})
+@ImportResource("classpath:spring-camel.xml")
+public class Application {
+	public static void main(String[] args) {
+		SpringApplication.run(Application.class, args);
+	}
+}
diff --git a/spring-boot-jta-jpa/src/main/java/sample/camel/AuditLog.java b/spring-boot-jta-jpa/src/main/java/sample/camel/AuditLog.java
new file mode 100644
index 0000000..a3ebe24
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/java/sample/camel/AuditLog.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.camel;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "audit_log")
+@NamedQueries({
+	@NamedQuery(name = "getAuditLog", query = "select al from AuditLog al")
+})
+public class AuditLog {
+	@Id
+	@GeneratedValue (strategy = GenerationType.IDENTITY)
+	@Column(name = "id")
+	private String id;
+	private String message;
+
+	public String getMessage() {
+		return message;
+	}
+
+	public AuditLog createAuditLog(String message) {
+		AuditLog auditLog = new AuditLog();
+		auditLog.message = message;
+		return auditLog;
+	}
+
+	@Override
+	public String toString() {
+		return String.format("{message=%s}", message);
+	}
+}
diff --git a/spring-boot-jta-jpa/src/main/java/sample/camel/CamelRoutes.java b/spring-boot-jta-jpa/src/main/java/sample/camel/CamelRoutes.java
new file mode 100644
index 0000000..302eb99
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/java/sample/camel/CamelRoutes.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.camel;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.RestParamType;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CamelRoutes extends RouteBuilder {
+    @Value("${camel.servlet.mapping.context-path}")
+    public String contextPath;
+
+    @Override
+    public void configure() {
+        restConfiguration()
+            .contextPath(contextPath.substring(0, contextPath.length() - 2));
+
+        rest("/messages")
+            .produces("text/plain")
+            .get()
+                .to("direct:messages")
+            .post("/{message}")
+                .param().name("message").type(RestParamType.path).dataType("string").endParam()
+                .to("direct:trans");
+
+        from("direct:messages")
+            .to("jpa:it.fvaleri.integ.AuditLog?namedQuery=getAuditLog")
+            .convertBodyTo(String.class);
+
+        from("direct:trans")
+            .transacted()
+            .setBody(simple("${headers.message}"))
+            .to("bean:auditLog?method=createAuditLog(${body})")
+            .to("jpa:it.fvaleri.integ.AuditLog")
+            .setBody(simple("${headers.message}"))
+            .to("jms:outbound?disableReplyTo=true")
+            .choice()
+                .when(body().startsWith("fail"))
+                    .log("Forced exception")
+                        .process(x -> {throw new RuntimeException("fail");})
+                .otherwise()
+                    .log("Message added: ${body}")
+            .endChoice();
+
+        from("jms:outbound")
+            .log("Message out: ${body}")
+            .to("bean:auditLog?method=createAuditLog(${body}-ok)")
+            .to("jpa:it.fvaleri.integ.AuditLog");
+    }
+}
diff --git a/spring-boot-jta-jpa/src/main/java/sample/camel/RMFactory.java b/spring-boot-jta-jpa/src/main/java/sample/camel/RMFactory.java
new file mode 100644
index 0000000..0b8b5eb
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/java/sample/camel/RMFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.camel;
+
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+
+public class RMFactory {
+    public RecoveryManager createInstance() {
+        RecoveryManager recoveryManager = RecoveryManager.manager(RecoveryManager.INDIRECT_MANAGEMENT);
+        recoveryManager.startRecoveryManagerThread();
+        return recoveryManager;
+    }
+}
diff --git a/spring-boot-jta-jpa/src/main/resources/application.properties b/spring-boot-jta-jpa/src/main/resources/application.properties
new file mode 100644
index 0000000..6ade52a
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/resources/application.properties
@@ -0,0 +1,27 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+server.port = 8080
+server.address = 0.0.0.0
+logging.config = classpath:logback.xml
+spring.main.banner-mode = off
+spring.jmx.enabled = false
+# http://localhost:8080/actuator/health
+management.endpoints.web.exposure.include = health,beans,loggers
+spring.main.allow-bean-definition-overriding = true
+camel.springboot.jmxEnabled = false
+camel.servlet.mapping.context-path = /api/*
diff --git a/spring-boot-jta-jpa/src/main/resources/jbossts-properties.xml b/spring-boot-jta-jpa/src/main/resources/jbossts-properties.xml
new file mode 100644
index 0000000..8fc515f
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/resources/jbossts-properties.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<properties>
+    <!-- must be unique across all instances to ensure TMs can coordinate the same RMs -->
+    <entry key="CoreEnvironmentBean.nodeIdentifier">${node.identifier}</entry>
+
+    <!-- by default the ObjectStore is crated under user.home -->
+    <entry key="ObjectStoreEnvironmentBean.objectStoreDir">target/store/${node.identifier}</entry>
+
+    <!-- transaction timeout in seconds after which it is automatically rolled back -->
+    <entry key="CoordinatorEnvironmentBean.defaultTimeout">120</entry>
+    <entry key="CoordinatorEnvironmentBean.asyncCommit">NO</entry>
+    <entry key="CoordinatorEnvironmentBean.transactionStatusManagerEnable">NO</entry>
+
+    <!-- transaction recovery periods in seconds (modules executed sequentially) -->
+    <entry key="RecoveryEnvironmentBean.periodicRecoveryPeriod">120</entry>
+    <entry key="RecoveryEnvironmentBean.recoveryBackoffPeriod">10</entry>
+    <entry key="RecoveryEnvironmentBean.recoveryListener">NO</entry>
+    <entry key="RecoveryEnvironmentBean.recoveryModuleClassNames">
+        com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule
+    </entry>
+
+    <!-- expiry period in hours for recovery ObjectStore entries (modules executed randomly) -->
+    <entry key="RecoveryEnvironmentBean.expiryScanInterval">12</entry>
+    <entry key="RecoveryEnvironmentBean.expiryScannerClassNames">
+        com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner
+    </entry>
+</properties>
diff --git a/spring-boot-jta-jpa/src/main/resources/logback.xml b/spring-boot-jta-jpa/src/main/resources/logback.xml
new file mode 100644
index 0000000..528b952
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/resources/logback.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d %highlight(%p) [%t] %c %m%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+    </root>
+
+    <logger name="sample.camel" level="INFO"/>
+    <logger name="com.arjuna" level="INFO"/>
+    <logger name="org.apache.camel" level="INFO"/>
+    <logger name="org.hibernate" level="INFO"/>
+</configuration>
diff --git a/spring-boot-jta-jpa/src/main/resources/spring-camel.xml b/spring-boot-jta-jpa/src/main/resources/spring-camel.xml
new file mode 100644
index 0000000..8424b3a
--- /dev/null
+++ b/spring-boot-jta-jpa/src/main/resources/spring-camel.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
+    <bean id="auditLog" class="sample.camel.AuditLog"/>
+    <bean id="transactionManagerImple"
+          class="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"/>
+    <bean id="userTransactionImple" class="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
+    <bean id="recoveryManagerFactory" class="sample.camel.RMFactory"/>
+    <bean id="recoveryManager" factory-bean="recoveryManagerFactory" factory-method="createInstance"/>
+    <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
+        <property name="transactionManager" ref="transactionManagerImple"/>
+        <property name="userTransaction" ref="userTransactionImple"/>
+    </bean>
+    <bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
+        <property name="transactionManager" ref="jtaTransactionManager"/>
+        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
+    </bean>
+
+    <bean id="jtaDataSource" class="com.mysql.cj.jdbc.MysqlXADataSource">
+        <property name="url" value="jdbc:mysql://localhost:3306/testdb"/>
+        <property name="user" value="admin"/>
+        <property name="password" value="admin"/>
+    </bean>
+
+    <!-- Pooled JTA aware data source -->
+    <bean id="pooledJtaDataSource" class="org.apache.commons.dbcp2.managed.BasicManagedDataSource">
+        <property name="transactionManager" ref="transactionManagerImple"/>
+        <property name="xaDataSourceInstance" ref="jtaDataSource"/>
+        <property name="initialSize" value="5"/>
+        <property name="maxIdle" value="5"/>
+    </bean>
+
+    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
+        <property name="packagesToScan" value="sample.camel"/>
+        <property name="jtaDataSource" ref="pooledJtaDataSource"/>
+        <property name="persistenceUnitName" value="camel"/>
+        <property name="jpaDialect">
+            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
+        </property>
+        <property name="jpaVendorAdapter">
+            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
+                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
+            </bean>
+        </property>
+        <property name="jpaProperties">
+            <props>
+                <prop key="hibernate.hbm2ddl.auto">none</prop>
+                <prop key="hibernate.id.new_generator_mappings">false</prop>
+                <prop key="hibernate.transaction.coordinator_class">jta</prop>
+                <prop key="hibernate.transaction.jta.platform">
+                    org.hibernate.engine.transaction.jta.platform.internal.JBossStandAloneJtaPlatform
+                </prop>
+                <prop key="hibernate.show_sql">false</prop>
+                <prop key="hibernate.format_sql">true"</prop>
+            </props>
+        </property>
+    </bean>
+
+    <bean id="jpa" class="org.apache.camel.component.jpa.JpaComponent">
+        <property name="transactionManager" ref="jtaTransactionManager"/>
+        <property name="entityManagerFactory" ref="entityManagerFactory"/>
+        <property name="joinTransaction" value="true"/>
+        <property name="sharedEntityManager" value="false"/>
+    </bean>
+
+    <bean id="jtaConnectionFactory" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory">
+        <constructor-arg value="tcp://localhost:61616"/>
+        <constructor-arg value="admin"/>
+        <constructor-arg value="admin"/>
+    </bean>
+
+    <!-- Pooled JTA aware connection factory -->
+    <bean id="pooledJtaConnectionFactory" class="org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory">
+        <property name="transactionManager" ref="transactionManagerImple"/>
+        <property name="connectionFactory" ref="jtaConnectionFactory"/>
+        <property name="maxConnections" value="1"/>
+        <property name="maxSessionsPerConnection" value="100"/>
+        <property name="useAnonymousProducers" value="false"/>
+    </bean>
+
+    <bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
+        <property name="configuration">
+            <bean class="org.apache.camel.component.jms.JmsConfiguration">
+                <property name="transactionManager" ref="jtaTransactionManager"/>
+                <property name="connectionFactory" ref="pooledJtaConnectionFactory"/>
+                <!-- disable local transactions as JTA TM will take care of enrolling -->
+                <property name="transacted" value="false"/>
+                <!-- caching does not work with distributed transactions -->
+                <property name="cacheLevelName" value="CACHE_NONE"/>
+                <property name="maxConcurrentConsumers" value="1"/>
+                <property name="testConnectionOnStartup" value="true"/>
+            </bean>
+        </property>
+    </bean>
+</beans>