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

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

commit 1df69f5ff32662c358648bca6c85b7d29dc291c6
Author: Andrej Vano <av...@redhat.com>
AuthorDate: Fri Oct 6 15:19:13 2023 +0200

    [Messaging] Add IBMMQ-AMQ example
---
 docs/modules/ROOT/attachments/examples.json        |   5 +
 message-bridge/.gitignore                          |   3 +
 message-bridge/README.adoc                         | 205 ++++++++++++
 message-bridge/eclipse-formatter-config.xml        | 276 +++++++++++++++
 message-bridge/pom.xml                             | 371 +++++++++++++++++++++
 .../org/acme/message/bridge/DummyXAResource.java   | 205 ++++++++++++
 .../message/bridge/DummyXAResourceRecovery.java    |  83 +++++
 .../acme/message/bridge/MessageBridgeRoute.java    |  76 +++++
 .../java/org/acme/message/bridge/Producers.java    |  99 ++++++
 .../src/main/resources/application.properties      |  42 +++
 .../acme/message/bridge/MessageBridgeITCase.java   |  99 ++++++
 .../message/bridge/resource/IBMMQTestResource.java |  63 ++++
 .../src/test/resources/application.properties      |  19 ++
 message-bridge/src/test/resources/broker.xml       |  41 +++
 14 files changed, 1587 insertions(+)

diff --git a/docs/modules/ROOT/attachments/examples.json 
b/docs/modules/ROOT/attachments/examples.json
index 7e39740..89dbc38 100644
--- a/docs/modules/ROOT/attachments/examples.json
+++ b/docs/modules/ROOT/attachments/examples.json
@@ -69,6 +69,11 @@
     "description": "Shows how to use Camel master component.",
     "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/cluster-leader-election";
   },
+  {
+    "title": "Message Bridge",
+    "description": "Shows how to configure AMQ and IBM MQ clients to use the 
connection pooling and XA transactions.",
+    "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/message-bridge";
+  },
   {
     "title": "Observability",
     "description": "Demonstrates how to add support for metrics, health checks 
and distributed tracing",
diff --git a/message-bridge/.gitignore b/message-bridge/.gitignore
new file mode 100644
index 0000000..0d67901
--- /dev/null
+++ b/message-bridge/.gitignore
@@ -0,0 +1,3 @@
+# IBM MQ client files
+FFDC/
+mqjms*
diff --git a/message-bridge/README.adoc b/message-bridge/README.adoc
new file mode 100644
index 0000000..9234749
--- /dev/null
+++ b/message-bridge/README.adoc
@@ -0,0 +1,205 @@
+= Message Bridge: A Camel Quarkus example
+:cq-example-description: An example that shows how to configure AMQ and IBM MQ 
clients to use the connection pooling and XA transactions.
+
+{cq-description}
+
+TIP: Check the 
https://camel.apache.org/camel-quarkus/latest/first-steps.html[Camel Quarkus 
User guide] for prerequisites
+and other general information.
+
+== Overview
+
+In this example, a basic REST endpoint is provided for users to dispatch a 
message to the IBM MQ queue. Subsequently, all messages from the IBM MQ are 
relayed to an ActiveMQ queue within an XA transaction. To showcase the 
transaction functionality, a message containing the keyword "rollback" will 
initiate a transaction rollback. To demonstrate the process of a transaction 
recovery after a crash, send a message with they keyword "crash".
+
+Details regarding client configurations can be located in the 
`src/main/resources/application.properties` file.
+
+== Prerequisites
+
+First start the ActiveMQ broker:
+----
+docker run \
+  -d \
+  -e AMQ_USER=admin \
+  -e AMQ_PASSWORD=admin \
+  -p 61616:61616 \
+  quay.io/artemiscloud/activemq-artemis-broker
+----
+
+Then start the IBM MQ broker:
+----
+docker run \
+  -d \
+  -e LICENSE=accept \
+  -e MQ_QMGR_NAME=QM1 \
+  -e MQ_APP_PASSWORD=passw0rd \
+  -p 1414:1414 \
+  icr.io/ibm-messaging/mq:9.3.2.1-r1
+----
+
+== Start in the Development mode
+
+[source,shell]
+----
+$ mvn clean compile quarkus:dev
+----
+
+The above command compiles the project, starts the application and lets the 
Quarkus tooling watch for changes in your
+workspace. Any modifications in your project will automatically take effect in 
the running application.
+
+TIP: Please refer to the Development mode section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode[Camel
 Quarkus User guide] for more details.
+
+After the application is started, you can send messages to the IBMMQ queue 
using the rest endpoint `/message`:
+
+----
+curl -X POST -H "Content-Type: text/plain" http://localhost:8080/message -d 
'Hello World'
+----
+
+In the application logs you will see:
+
+----
+2023-10-11 08:10:52,782 INFO  [ibmmq] (executor-thread-1) Sending message to 
IBMMQ: Hello World
+2023-10-11 08:10:52,903 INFO  [ibmmq-amq] (Camel (camel-4) thread #7 - 
JmsConsumer[DEV.QUEUE.1]) Sending message from IBMMQ to ActiveMQ: Hello World
+2023-10-11 08:10:52,927 INFO  [amq] (Camel (camel-4) thread #8 - 
JmsConsumer[in]) ActiveMQ received: Hello World
+----
+
+The message is initially dispatched to an IBMMQ queue, then relayed to an 
ActiveMQ queue, and ultimately retrieved from the ActiveMQ queue and printed to 
the log without any issues.
+
+=== Transaction rollback
+
+If you wish to illustrate a rollback, simply send a message containing the 
`rollback` keyword.
+
+----
+curl -X POST -H "Content-Type: text/plain" http://localhost:8080/message -d 
'Hello rollback'
+----
+The log could be divided into two segments. In this demonstration, the IBMMQ 
to ActiveMQ route is configured to simulate a transaction rollback following 
the dispatch of a message to the ActiveMQ queue. Due to the rollback, you'll 
notice that the message isn't fetched from the ActiveMQ queue and isn't logged:
+
+----
+2023-10-11 08:12:46,314 INFO  [ibmmq] (executor-thread-1) Sending message to 
IBMMQ: Hello rollback
+2023-10-11 08:12:46,453 INFO  [ibmmq-amq] (Camel (camel-4) thread #7 - 
JmsConsumer[DEV.QUEUE.1]) Sending message from IBMMQ to ActiveMQ: Hello rollback
+2023-10-11 08:12:46,457 WARN  [org.apa.cam.jta.TransactionErrorHandler] (Camel 
(camel-4) thread #7 - JmsConsumer[DEV.QUEUE.1]) Transaction rollback 
(0xea2b886) redelivered(false) for (MessageId: 
ID:414d5120514d312020202020202020206437266503e30040 on ExchangeId: 
01C264F444F3A96-0000000000000003) caught: Simulated rollback
+2023-10-11 08:12:46,458 ERROR [org.apa.cam.jta.TransactionErrorHandler] (Camel 
(camel-4) thread #7 - JmsConsumer[DEV.QUEUE.1]) Failed delivery for (MessageId: 
ID:414d5120514d312020202020202020206437266503e30040 on ExchangeId: 
01C264F444F3A96-0000000000000003). Exhausted after delivery attempt: 1 caught: 
java.lang.RuntimeException: Simulated rollback
+...
+<truncated exception from the simulated rollback>
+----
+
+Because the transaction was rolled back, the message wasn't successfully 
processed and stays in the input broker and is subsequently redelivered. In 
this instance, the delivery is successful and logged accordingly:
+
+----
+2023-10-11 08:12:46,561 INFO  [ibmmq-amq] (Camel (camel-4) thread #7 - 
JmsConsumer[DEV.QUEUE.1]) Redelivering message after rollback to ActiveMQ: 
Hello rollback
+2023-10-11 08:12:46,567 INFO  [org.apa.cam.jta.TransactionErrorHandler] (Camel 
(camel-4) thread #7 - JmsConsumer[DEV.QUEUE.1]) Transaction commit (0xea2b886) 
redelivered(true) for (MessageId: 
ID:414d5120514d312020202020202020206437266503e30040 on ExchangeId: 
01C264F444F3A96-0000000000000004))
+2023-10-11 08:12:46,585 INFO  [amq] (Camel (camel-4) thread #8 - 
JmsConsumer[in]) ActiveMQ received: Hello rollback
+----
+
+In this example, a local file transaction storage is used, which is set up 
through the `quarkus.transaction-manager.object-store.directory` property. If 
you prefer to utilize a database for configuring the storage, please refer to 
the Quarkus transactions guide available at 
https://quarkus.io/guides/transaction#jdbcstore.
+
+=== Transaction recovery
+
+To trigger a JVM crash during the transaction, send a message containing the 
word `crash`.
+
+----
+curl -X POST -H "Content-Type: text/plain" http://localhost:8080/message -d 
'Hello crash'
+----
+
+This will result in the JVM stopping abruptly:
+
+----
+2023-10-13 15:21:03,277 INFO  [ibmmq] (executor-thread-1) Sending message to 
IBMMQ: crash
+2023-10-13 15:21:03,429 INFO  [ibmmq-amq] (Camel (camel-1) thread #1 - 
JmsConsumer[DEV.QUEUE.1]) Sending message from IBMMQ to ActiveMQ: crash
+2023-10-13 15:21:03,449 INFO  [org.acm.mes.bri.DummyXAResource] (Camel 
(camel-1) thread #1 - JmsConsumer[DEV.QUEUE.1]) Preparing DummyXAResource
+2023-10-13 15:21:03,461 INFO  [org.acm.mes.bri.DummyXAResource] (Camel 
(camel-1) thread #1 - JmsConsumer[DEV.QUEUE.1]) Committing DummyXAResource
+2023-10-13 15:21:03,461 INFO  [org.acm.mes.bri.DummyXAResource] (Camel 
(camel-1) thread #1 - JmsConsumer[DEV.QUEUE.1]) Crashing the system
+----
+
+NOTE: If you previously used `mvn clean compile quarkus:dev` to run the 
application, when restarting, omit the `clean` step from the command. This is 
because the default location for transaction manager persistence is located in 
the `target` directory.
+
+Upon restarting the application, after a few seconds you will observe that the 
transaction has been restored, and the message has been successfully delivered 
to the ActiveMQ broker.
+
+----
+2023-10-13 15:21:36,458 INFO  [io.quarkus] (main) 
camel-quarkus-examples-message-bridge 3.5.0-SNAPSHOT on JVM (powered by Quarkus 
3.5.0.CR1) started in 0.893s. Listening on: http://0.0.0.0:8080
+2023-10-13 15:21:36,459 INFO  [io.quarkus] (main) Profile prod activated.
+2023-10-13 15:21:36,459 INFO  [io.quarkus] (main) Installed features: 
[artemis-jms, camel-attachments, camel-core, camel-direct, camel-jms, 
camel-jta, camel-platform-http, camel-rest, cdi, messaginghub-pooled-jms, 
narayana-jta, smallrye-context-propagation, vertx]
+2023-10-13 15:21:46,386 INFO  [org.acm.mes.bri.DummyXAResourceRecovery] 
(Periodic Recovery) DummyXAResourceRecovery returning list of resources: 
[org.acme.message.bridge.DummyXAResource@3739f1f4, 
org.acme.message.bridge.DummyXAResource@7d2113fb]
+2023-10-13 15:21:46,391 WARN  [com.arj.ats.jta] (Periodic Recovery) 
ARJUNA016037: Could not find new XAResource to use for recovering 
non-serializable XAResource XAResourceRecord < resource:null, txid:< 
formatId=131077, gtrid_length=35, bqual_length=36, 
tx_uid=0:ffffc0a80136:8e8b:65294417:31, node_name=quarkus, 
branch_uid=0:ffffc0a80136:8e8b:65294417:33, subordinatenodename=null, 
eis_name=0 >, heuristic: TwoPhaseOutcome.FINISH_OK 
com.arjuna.ats.internal.jta.resources.arjunacore.XAResourc [...]
+2023-10-13 15:21:46,483 INFO  [org.acm.mes.bri.DummyXAResourceRecovery] 
(Periodic Recovery) DummyXAResourceRecovery returning list of resources: 
[org.acme.message.bridge.DummyXAResource@24a51dd0, 
org.acme.message.bridge.DummyXAResource@4fc1b14d]
+2023-10-13 15:21:46,484 WARN  [com.arj.ats.jta] (Periodic Recovery) 
ARJUNA016038: No XAResource to recover < formatId=131077, gtrid_length=35, 
bqual_length=36, tx_uid=0:ffffc0a80136:8e8b:65294417:31, node_name=quarkus, 
branch_uid=0:ffffc0a80136:8e8b:65294417:33, subordinatenodename=null, 
eis_name=0 >
+2023-10-13 15:21:46,485 INFO  [org.acm.mes.bri.DummyXAResource] (Periodic 
Recovery) Committing DummyXAResource
+2023-10-13 15:21:46,512 INFO  [amq] (Camel (camel-1) thread #2 - 
JmsConsumer[in]) ActiveMQ received: crash
+----
+
+You may observe the warning messages related to the XAResource recovery in the 
logs. This is a known issue related to how we simulate the crash of the system 
and can be ignored.
+
+=== Package and run the application
+
+Once you are done with developing you may want to package and run the 
application.
+
+TIP: Find more details about the JVM mode and Native mode in the Package and 
run section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_package_and_run_the_application[Camel
 Quarkus User guide]
+
+==== JVM mode
+
+[source,shell]
+----
+$ mvn clean package
+$ java -jar target/quarkus-app/quarkus-run.jar
+...
+[io.quarkus] (main) camel-quarkus-examples-... started in 1.163s.
+----
+
+=== Running on OpenShift
+
+You can also deploy this example as an OpenShift pod using the capabilities of 
the quarkus-maven-plugin.
+
+Before doing so, make sure to deploy the AMQ and IBMMQ brokers:
+
+----
+oc new-app quay.io/artemiscloud/activemq-artemis-broker -e AMQ_USER=admin -e 
AMQ_PASSWORD=admin
+oc patch service/activemq-artemis-broker -p 
'{"spec":{"ports":[{"name":"61616-tcp", "port": 61616, "protocol": "TCP", 
"targetPort": 61616}]}}'
+----
+
+----
+oc new-app icr.io/ibm-messaging/mq:9.3.2.1-r1 -e MQ_QMGR_NAME=QM1 -e 
LICENSE=accept -e MQ_APP_PASSWORD=passw0rd
+----
+
+Next, create a `PersistentVolumeClaim` to serve as the storage for the 
transaction manager's object store. This example assumes that a persistent 
volume claim named `message-bridge` has already been set up. Keep in mind that 
the configuration of the persistent volume may require adjustments based on 
your specific OpenShift setup:
+
+----
+oc create -f - <<EOF
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+  name: message-bridge
+spec:
+  accessModes:
+    - ReadWriteOnce
+  resources:
+    requests:
+      storage: 1Gi
+EOF
+----
+
+Then deploy this example using the `openshift` profile specified within this 
project:
+
+----
+mvn clean package -DskipTests -Popenshift
+----
+
+Once the pod is up and running successfully, you can send messages in a manner 
similar to local deployment:
+
+----
+curl -X POST -H "Content-Type: text/plain" http://$(oc get route 
camel-quarkus-examples-message-bridge -o jsonpath='{.spec.host}')/message -d 
'Hello world'
+----
+
+==== Clean up
+
+If you'd like to clean up the namespace, you can use the following commands to 
do that:
+
+----
+oc delete all --selector 
app.kubernetes.io/name=camel-quarkus-examples-message-bridge
+oc delete all --selector app=mq
+oc delete all --selector app=activemq-artemis-broker
+oc delete pvc message-bridge
+----
+
+== Feedback
+
+Please report bugs and propose improvements via 
https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus] 
project.
diff --git a/message-bridge/eclipse-formatter-config.xml 
b/message-bridge/eclipse-formatter-config.xml
new file mode 100644
index 0000000..2248b2b
--- /dev/null
+++ b/message-bridge/eclipse-formatter-config.xml
@@ -0,0 +1,276 @@
+<?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.
+
+-->
+<profiles version="8">
+    <profile name="Camel Java Conventions" version="8" 
kind="CodeFormatterProfile">
+        <setting 
id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration"
 value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" 
value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" 
value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" 
value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration"
 value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration"
 value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration"
 value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" 
value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" 
value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_block_comments" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_comments" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_header" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_html" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.indent_return_description" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" 
value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.line_length" 
value="120"/>
+        <setting id="org.eclipse.jdt.core.formatter.compact_else_if" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation" 
value="2"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer"
 value="2"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.indentation.size" 
value="8"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do 
not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do 
not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not 
insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not 
insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation"
 value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.lineSplit" value="128"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body"
 value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.char" 
value="space"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.size" 
value="4"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.disabling_tag" 
value="CHECKSTYLE:OFF"/>
+        <setting id="org.eclipse.jdt.core.formatter.enabling_tag" 
value="CHECKSTYLE:ON"/>
+    </profile>
+</profiles>
diff --git a/message-bridge/pom.xml b/message-bridge/pom.xml
new file mode 100644
index 0000000..d7046ce
--- /dev/null
+++ b/message-bridge/pom.xml
@@ -0,0 +1,371 @@
+<?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/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-quarkus-examples-message-bridge</artifactId>
+    <groupId>org.apache.camel.quarkus.examples</groupId>
+    <version>3.5.0-SNAPSHOT</version>
+
+    <name>Camel Quarkus :: Examples :: Message Bridge</name>
+    <description>Camel Quarkus Example :: Configure XA Transactions and 
connection pooling</description>
+
+    <properties>
+        <quarkus.platform.version>3.5.0.CR1</quarkus.platform.version>
+        
<camel-quarkus.platform.version>3.5.0-SNAPSHOT</camel-quarkus.platform.version>
+
+        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+        
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        
<camel-quarkus.platform.group-id>org.apache.camel.quarkus</camel-quarkus.platform.group-id>
+        
<camel-quarkus.platform.artifact-id>camel-quarkus-bom</camel-quarkus.platform.artifact-id>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.target>17</maven.compiler.target>
+        <maven.compiler.source>17</maven.compiler.source>
+        
<maven.compiler.testTarget>${maven.compiler.target}</maven.compiler.testTarget>
+        
<maven.compiler.testSource>${maven.compiler.source}</maven.compiler.testSource>
+
+        <formatter-maven-plugin.version>2.23.0</formatter-maven-plugin.version>
+        <impsort-maven-plugin.version>1.9.0</impsort-maven-plugin.version>
+        <license-maven-plugin.version>4.2</license-maven-plugin.version>
+        <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
+        <maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
+        <maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
+        <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
+
+        <quarkus-artemis-jms.version>3.1.1</quarkus-artemis-jms.version>
+        <assertj.version>3.24.2</assertj.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Import BOM -->
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>${camel-quarkus.platform.group-id}</groupId>
+                <artifactId>${camel-quarkus.platform.artifact-id}</artifactId>
+                <version>${camel-quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- Camel -->
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+
+        <!-- REST endpoint -->
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-rest</artifactId>
+        </dependency>
+
+        <!-- Transactions -->
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-jta</artifactId>
+        </dependency>
+
+        <!-- Messaging -->
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-jms</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.messaginghub</groupId>
+            <artifactId>quarkus-pooled-jms</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ibm.mq</groupId>
+            <artifactId>com.ibm.mq.jakarta.client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.artemis</groupId>
+            <artifactId>quarkus-artemis-jms</artifactId>
+            <version>${quarkus-artemis-jms.version}</version>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.artemis</groupId>
+            <artifactId>quarkus-test-artemis</artifactId>
+            <version>${quarkus-artemis-jms.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5-internal</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>openshift</id>
+            <activation>
+                <property>
+                    <name>openshift</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-openshift</artifactId>
+                </dependency>
+            </dependencies>
+
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>${quarkus.platform.group-id}</groupId>
+                        <artifactId>quarkus-maven-plugin</artifactId>
+                        <version>${quarkus.platform.version}</version>
+                        <configuration>
+                            <systemProperties>
+                                <quarkus.profile>openshift</quarkus.profile>
+                                
<quarkus.kubernetes.deploy>true</quarkus.kubernetes.deploy>
+                                
<quarkus.openshift.route.expose>true</quarkus.openshift.route.expose>
+                                
<quarkus.openshift.pvc-volumes.storage.claim-name>message-bridge</quarkus.openshift.pvc-volumes.storage.claim-name>
+                                
<quarkus.openshift.mounts.storage.path>/storage</quarkus.openshift.mounts.storage.path>
+                            </systemProperties>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>skip-testcontainers-tests</id>
+            <activation>
+                <property>
+                    <name>skip-testcontainers-tests</name>
+                </property>
+            </activation>
+            <properties>
+                <skipTests>true</skipTests>
+            </properties>
+        </profile>
+    </profiles>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+
+                <plugin>
+                    <groupId>net.revelc.code.formatter</groupId>
+                    <artifactId>formatter-maven-plugin</artifactId>
+                    <version>${formatter-maven-plugin.version}</version>
+                    <configuration>
+                        
<configFile>${maven.multiModuleProjectDirectory}/eclipse-formatter-config.xml</configFile>
+                        <lineEnding>LF</lineEnding>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>net.revelc.code</groupId>
+                    <artifactId>impsort-maven-plugin</artifactId>
+                    <version>${impsort-maven-plugin.version}</version>
+                    <configuration>
+                        <groups>java.,javax.,org.w3c.,org.xml.,junit.</groups>
+                        <removeUnused>true</removeUnused>
+                        <staticAfter>true</staticAfter>
+                        
<staticGroups>java.,javax.,org.w3c.,org.xml.,junit.</staticGroups>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <showDeprecation>true</showDeprecation>
+                        <showWarnings>true</showWarnings>
+                        <compilerArgs>
+                            <arg>-Xlint:unchecked</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <configuration>
+                        <failIfNoTests>false</failIfNoTests>
+                        <systemPropertyVariables>
+                            
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        </systemPropertyVariables>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>${quarkus.platform.group-id}</groupId>
+                    <artifactId>quarkus-maven-plugin</artifactId>
+                    <version>${quarkus.platform.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>integration-test</goal>
+                                <goal>verify</goal>
+                            </goals>
+                            <configuration>
+                                <systemPropertyVariables>
+                                    
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                                </systemPropertyVariables>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>${maven-jar-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>com.mycila</groupId>
+                    <artifactId>license-maven-plugin</artifactId>
+                    <version>${license-maven-plugin.version}</version>
+                    <configuration>
+                        <failIfUnknown>true</failIfUnknown>
+                        
<header>${maven.multiModuleProjectDirectory}/header.txt</header>
+                        <excludes>
+                            <exclude>**/*.adoc</exclude>
+                            <exclude>**/*.txt</exclude>
+                            <exclude>**/LICENSE.txt</exclude>
+                            <exclude>**/LICENSE</exclude>
+                            <exclude>**/NOTICE.txt</exclude>
+                            <exclude>**/NOTICE</exclude>
+                            <exclude>**/README</exclude>
+                            <exclude>**/pom.xml.versionsBackup</exclude>
+                        </excludes>
+                        <mapping>
+                            <java>SLASHSTAR_STYLE</java>
+                            <properties>CAMEL_PROPERTIES_STYLE</properties>
+                            <kt>SLASHSTAR_STYLE</kt>
+                        </mapping>
+                        <headerDefinitions>
+                            
<headerDefinition>${maven.multiModuleProjectDirectory}/license-properties-headerdefinition.xml</headerDefinition>
+                        </headerDefinitions>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build</id>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code</groupId>
+                <artifactId>impsort-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>sort-imports</id>
+                        <goals>
+                            <goal>sort</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>${maven-surefire-plugin.version}</version>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git 
a/message-bridge/src/main/java/org/acme/message/bridge/DummyXAResource.java 
b/message-bridge/src/main/java/org/acme/message/bridge/DummyXAResource.java
new file mode 100644
index 0000000..1a0dfba
--- /dev/null
+++ b/message-bridge/src/main/java/org/acme/message/bridge/DummyXAResource.java
@@ -0,0 +1,205 @@
+/*
+ * 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 org.acme.message.bridge;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+
+/**
+ * This class is used solely for simulating system crash.
+ *
+ */
+public class DummyXAResource implements XAResource {
+    private static final Logger LOG = Logger.getLogger(DummyXAResource.class);
+
+    private final boolean shouldCrash;
+
+    private Xid xid;
+
+    private File file;
+
+    public DummyXAResource(boolean shouldCrash) {
+        this.shouldCrash = shouldCrash;
+    }
+
+    /**
+     * Constructor used by recovery manager to recreate XAResource
+     *
+     * @param file File where Xid of the XAResource is stored
+     */
+    public DummyXAResource(File file) throws IOException {
+        this.shouldCrash = false;
+        this.file = file;
+        this.xid = getXidFromFile(file);
+    }
+
+    public int prepare(final Xid xid) throws XAException {
+        LOG.info("Preparing " + DummyXAResource.class.getSimpleName());
+
+        this.file = writeXidToFile(xid, 
ConfigProvider.getConfig().getValue("dummy.resource.directory", String.class));
+
+        return XA_OK;
+    }
+
+    public void commit(final Xid xid, final boolean arg1) throws XAException {
+        LOG.info("Committing " + DummyXAResource.class.getSimpleName());
+
+        if (shouldCrash) {
+            LOG.info("Crashing the system");
+            Runtime.getRuntime().halt(1);
+        }
+
+        removeFile(file);
+        this.file = null;
+        this.xid = null;
+    }
+
+    public void rollback(final Xid xid) throws XAException {
+        LOG.info("Rolling back " + DummyXAResource.class.getSimpleName());
+
+        removeFile(file);
+        this.file = null;
+        this.xid = null;
+    }
+
+    public boolean isSameRM(XAResource xaResource) throws XAException {
+        if (!(xaResource instanceof DummyXAResource)) {
+            return false;
+        }
+
+        DummyXAResource other = (DummyXAResource) xaResource;
+
+        return xid != null && other.xid != null && xid.getFormatId() == 
other.xid.getFormatId()
+                && Arrays.equals(xid.getGlobalTransactionId(), 
other.xid.getGlobalTransactionId())
+                && Arrays.equals(xid.getBranchQualifier(), 
other.xid.getBranchQualifier());
+    }
+
+    public Xid[] recover(int flag) throws XAException {
+        return new Xid[] { xid };
+    }
+
+    public void start(Xid xid, int flags) throws XAException {
+
+    }
+
+    public void end(Xid xid, int flags) throws XAException {
+
+    }
+
+    public void forget(Xid xid) throws XAException {
+
+    }
+
+    public int getTransactionTimeout() throws XAException {
+        return 0;
+    }
+
+    public boolean setTransactionTimeout(final int seconds) throws XAException 
{
+        return true;
+    }
+
+    private Xid getXidFromFile(File file) throws IOException {
+        try (DataInputStream inputStream = new DataInputStream(new 
FileInputStream(file))) {
+            int formatId = inputStream.readInt();
+
+            int globalTransactionIdLength = inputStream.readInt();
+            byte[] globalTransactionId = new byte[globalTransactionIdLength];
+            inputStream.read(globalTransactionId, 0, 
globalTransactionIdLength);
+
+            int branchQualifierLength = inputStream.readInt();
+            byte[] branchQualifier = new byte[branchQualifierLength];
+            inputStream.read(branchQualifier, 0, branchQualifierLength);
+
+            return new XidImpl(formatId, globalTransactionId, branchQualifier);
+        }
+    }
+
+    private File writeXidToFile(Xid xid, String directory) throws XAException {
+        File dir = new File(directory);
+
+        if (!dir.exists() && !dir.mkdirs()) {
+            throw new XAException(XAException.XAER_RMERR);
+        }
+
+        File xidFile = new File(dir, new Uid().fileStringForm() + "_");
+
+        try (DataOutputStream outputStream = new DataOutputStream(new 
FileOutputStream(xidFile))) {
+            outputStream.writeInt(xid.getFormatId());
+            outputStream.writeInt(xid.getGlobalTransactionId().length);
+            outputStream.write(xid.getGlobalTransactionId(), 0, 
xid.getGlobalTransactionId().length);
+            outputStream.writeInt(xid.getBranchQualifier().length);
+            outputStream.write(xid.getBranchQualifier(), 0, 
xid.getBranchQualifier().length);
+            outputStream.flush();
+        } catch (IOException e) {
+            throw new XAException(XAException.XAER_RMERR);
+        }
+
+        return xidFile;
+    }
+
+    private void removeFile(File file) throws XAException {
+        if (file != null) {
+            if (!file.delete()) {
+                throw new XAException(XAException.XA_RETRY);
+            }
+        }
+    }
+
+    private static class XidImpl implements Xid {
+
+        private final int formatId;
+
+        private final byte[] globalTransactionId;
+
+        private final byte[] branchQualifier;
+
+        XidImpl(int formatId, byte[] globalTransactionId, byte[] 
branchQualifier) {
+            this.formatId = formatId;
+            this.globalTransactionId = globalTransactionId;
+            this.branchQualifier = branchQualifier;
+        }
+
+        @Override
+        public int getFormatId() {
+            return formatId;
+        }
+
+        @Override
+        public byte[] getGlobalTransactionId() {
+            return globalTransactionId;
+        }
+
+        @Override
+        public byte[] getBranchQualifier() {
+            return branchQualifier;
+        }
+
+    }
+}
diff --git 
a/message-bridge/src/main/java/org/acme/message/bridge/DummyXAResourceRecovery.java
 
b/message-bridge/src/main/java/org/acme/message/bridge/DummyXAResourceRecovery.java
new file mode 100644
index 0000000..3df391d
--- /dev/null
+++ 
b/message-bridge/src/main/java/org/acme/message/bridge/DummyXAResourceRecovery.java
@@ -0,0 +1,83 @@
+/*
+ * 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 org.acme.message.bridge;
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.transaction.xa.XAResource;
+
+import io.quarkus.runtime.Startup;
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.logging.Logger;
+import org.jboss.tm.XAResourceRecovery;
+import org.jboss.tm.XAResourceRecoveryRegistry;
+
+/**
+ * This class is used solely for simulating system crash.
+ *
+ */
+@Startup
+public class DummyXAResourceRecovery implements XAResourceRecovery {
+    private static final Logger LOG = 
Logger.getLogger(DummyXAResourceRecovery.class);
+
+    @Inject
+    XAResourceRecoveryRegistry xaResourceRecoveryRegistry;
+
+    @PostConstruct
+    void init() {
+        LOG.info("Registering DummyXAResourceRecovery");
+        xaResourceRecoveryRegistry.addXAResourceRecovery(this);
+    }
+
+    @Override
+    public XAResource[] getXAResources() throws RuntimeException {
+        List<DummyXAResource> resources = Collections.emptyList();
+        try {
+            resources = getXAResourcesFromDirectory(
+                    
ConfigProvider.getConfig().getValue("dummy.resource.directory", String.class));
+        } catch (IOException ignored) {
+        }
+
+        if (!resources.isEmpty()) {
+            LOG.info(DummyXAResourceRecovery.class.getSimpleName() + " 
returning list of resources: " + resources);
+        }
+
+        return resources.toArray(new XAResource[] {});
+    }
+
+    private List<DummyXAResource> getXAResourcesFromDirectory(String 
directory) throws IOException {
+        List<DummyXAResource> resources = new ArrayList<>();
+
+        Files.newDirectoryStream(FileSystems.getDefault().getPath(directory), 
"*_").forEach(path -> {
+            try {
+                resources.add(new DummyXAResource(path.toFile()));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        return resources;
+    }
+
+}
diff --git 
a/message-bridge/src/main/java/org/acme/message/bridge/MessageBridgeRoute.java 
b/message-bridge/src/main/java/org/acme/message/bridge/MessageBridgeRoute.java
new file mode 100644
index 0000000..f10a4ec
--- /dev/null
+++ 
b/message-bridge/src/main/java/org/acme/message/bridge/MessageBridgeRoute.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.acme.message.bridge;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.transaction.TransactionManager;
+import org.apache.camel.builder.RouteBuilder;
+
+@ApplicationScoped
+public class MessageBridgeRoute extends RouteBuilder {
+    @Inject
+    TransactionManager transactionManager;
+
+    @Override
+    public void configure() throws Exception {
+        rest()
+                .post("/message")
+                .id("rest")
+                .to("direct:publish");
+
+        from("direct:publish")
+                .id("ibmmq")
+                .transacted()
+                .log("Sending message to IBMMQ: ${body}")
+                .to("ibmmq:queue:{{ibm.mq.queue}}?disableReplyTo=true");
+
+        from("ibmmq:queue:{{ibm.mq.queue}}")
+                .id("ibmmq-amq")
+                .transacted()
+                .process(ex -> {
+                    // Enlist our custom XAResource
+                    // if the body contains "crash", this resource will kill 
the JVM during transaction commit
+                    // this resource is then used to recover the message after 
the crash
+                    DummyXAResource xaResource = new 
DummyXAResource(ex.getIn().getBody(String.class).contains("crash"));
+                    
transactionManager.getTransaction().enlistResource(xaResource);
+                })
+                .choice()
+                .when(simple("${header.JMSRedelivered}"))
+                .log("Redelivering message after rollback to ActiveMQ: 
${body}")
+                .otherwise()
+                .log("Sending message from IBMMQ to ActiveMQ: ${body}")
+                .end()
+                .to("amq:queue:{{amq.queue}}")
+                .process(ex -> {
+                    if 
(ex.getIn().getBody(String.class).toLowerCase().contains("rollback")) {
+                        // To simulate the rollback just once, we examine the 
value of the JMSRedelivered flag in the message
+                        // if the value is "false", we initiate the rollback
+                        // if the value is "true", it indicates that the 
rollback has already occurred,
+                        // so we allow the message to proceed through the 
route successfully
+                        if (!ex.getIn().getHeader("JMSRedelivered", 
Boolean.class)) {
+                            // Simulate rollback
+                            throw new RuntimeException("Simulated rollback");
+                        }
+                    }
+                });
+
+        from("amq:queue:{{amq.queue}}")
+                .id("amq")
+                .log("ActiveMQ received: ${body}");
+    }
+}
diff --git 
a/message-bridge/src/main/java/org/acme/message/bridge/Producers.java 
b/message-bridge/src/main/java/org/acme/message/bridge/Producers.java
new file mode 100644
index 0000000..f9e51a3
--- /dev/null
+++ b/message-bridge/src/main/java/org/acme/message/bridge/Producers.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.acme.message.bridge;
+
+import com.ibm.mq.jakarta.jms.MQXAConnectionFactory;
+import com.ibm.msg.client.jakarta.wmq.WMQConstants;
+import io.quarkiverse.messaginghub.pooled.jms.PooledJmsWrapper;
+import io.smallrye.common.annotation.Identifier;
+import jakarta.inject.Singleton;
+import jakarta.jms.ConnectionFactory;
+import jakarta.jms.JMSException;
+import jakarta.transaction.TransactionManager;
+import org.apache.camel.component.jms.JmsComponent;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.springframework.transaction.jta.JtaTransactionManager;
+
+public class Producers {
+    /**
+     * Create a connection factory for IBM MQ.
+     * <p/>
+     * Since there is no IBM MQ extension for quarkus, we need to create the 
connection factory manually
+     *
+     * @param  wrapper wrapper that is used to add pooling capabilities to the 
connection factory
+     * @return         a new connection factory instance
+     */
+    @Identifier("ibmConnectionFactory")
+    public ConnectionFactory createXAConnectionFactory(PooledJmsWrapper 
wrapper) {
+        MQXAConnectionFactory mq = new MQXAConnectionFactory();
+        try {
+            mq.setHostName(ConfigProvider.getConfig().getValue("ibm.mq.host", 
String.class));
+            mq.setPort(ConfigProvider.getConfig().getValue("ibm.mq.port", 
Integer.class));
+            
mq.setChannel(ConfigProvider.getConfig().getValue("ibm.mq.channel", 
String.class));
+            
mq.setQueueManager(ConfigProvider.getConfig().getValue("ibm.mq.queueManagerName",
 String.class));
+            mq.setTransportType(WMQConstants.WMQ_CM_CLIENT);
+            mq.setStringProperty(WMQConstants.USERID,
+                    ConfigProvider.getConfig().getValue("ibm.mq.user", 
String.class));
+            mq.setStringProperty(WMQConstants.PASSWORD,
+                    ConfigProvider.getConfig().getValue("ibm.mq.password", 
String.class));
+        } catch (JMSException e) {
+            throw new RuntimeException("Unable to create IBM MQ Connection 
Factory", e);
+        }
+        return wrapper.wrapConnectionFactory(mq);
+    }
+
+    /**
+     * Define the JtaTransactionManager instance that is used in jms 
components.
+     *
+     * @param  transactionManager transaction manager
+     * @return                    JtaTransactionManager instance
+     */
+    @Singleton
+    JtaTransactionManager manager(TransactionManager transactionManager) {
+        return new JtaTransactionManager(transactionManager);
+    }
+
+    /**
+     * Define the "ibmmq" jms component.
+     *
+     * @param  cf ibm mq connection factory that is automatically injected by 
Quarkus based on the given identifier
+     * @param  tm transaction manager to use
+     * @return    a new JmsComponent instance
+     */
+    @Identifier("ibmmq")
+    JmsComponent ibmmq(@Identifier("ibmConnectionFactory") ConnectionFactory 
cf, JtaTransactionManager tm) {
+        JmsComponent ibmmq = new JmsComponent();
+        ibmmq.setConnectionFactory(cf);
+        ibmmq.setTransactionManager(tm);
+        return ibmmq;
+    }
+
+    /**
+     * Define the "amq" jms component.
+     *
+     * @param  cf activemq connection factory that is automatically injected 
by Quarkus based on the given identifier
+     * @param  tm transaction manager to use
+     * @return    a new JmsComponent instance
+     */
+    @Identifier("amq")
+    JmsComponent amq(@Identifier("amqConnectionFactory") ConnectionFactory cf, 
JtaTransactionManager tm) {
+        JmsComponent amq = new JmsComponent();
+        amq.setConnectionFactory(cf);
+        amq.setTransactionManager(tm);
+        return amq;
+    }
+}
diff --git a/message-bridge/src/main/resources/application.properties 
b/message-bridge/src/main/resources/application.properties
new file mode 100644
index 0000000..1ae8258
--- /dev/null
+++ b/message-bridge/src/main/resources/application.properties
@@ -0,0 +1,42 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+quarkus.log.file.enable=true
+
+ibm.mq.host=localhost
+%openshift.ibm.mq.host=mq
+ibm.mq.port=1414
+ibm.mq.channel=DEV.APP.SVRCONN
+ibm.mq.queueManagerName=QM1
+ibm.mq.user=app
+ibm.mq.password=passw0rd
+ibm.mq.queue=DEV.QUEUE.1
+
+quarkus.artemis.amqConnectionFactory.url=tcp://localhost:61616
+%openshift.quarkus.artemis.amqConnectionFactory.url=tcp://activemq-artemis-broker:61616
+quarkus.artemis.amqConnectionFactory.username=admin
+quarkus.artemis.amqConnectionFactory.password=admin
+amq.queue=in
+
+quarkus.pooled-jms.transaction=xa
+quarkus.pooled-jms.pooling.enabled=true
+quarkus.pooled-jms.max-connections=8
+
+quarkus.transaction-manager.object-store.directory=target/narayana
+%openshift.quarkus.transaction-manager.object-store.directory=/storage/narayana
+dummy.resource.directory=target/DummyXAResource
+%openshift.dummy.resource.directory=/storage/DummyXAResource
+quarkus.transaction-manager.enable-recovery=true
diff --git 
a/message-bridge/src/test/java/org/acme/message/bridge/MessageBridgeITCase.java 
b/message-bridge/src/test/java/org/acme/message/bridge/MessageBridgeITCase.java
new file mode 100644
index 0000000..b83acf3
--- /dev/null
+++ 
b/message-bridge/src/test/java/org/acme/message/bridge/MessageBridgeITCase.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.acme.message.bridge;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.artemis.test.ArtemisTestResource;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.restassured.RestAssured;
+import org.acme.message.bridge.resource.IBMMQTestResource;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.awaitility.Awaitility;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@QuarkusIntegrationTest
+@QuarkusTestResource(ArtemisTestResource.class)
+@QuarkusTestResource(IBMMQTestResource.class)
+// The crash test will kill the app, so it must be executed last as there is 
no way to restart the application
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class MessageBridgeITCase {
+    private static final File LOG_FILE = new File("target/quarkus.log");
+
+    @Test
+    @Order(1)
+    public void shouldSendMessageToActiveMQTest() {
+        final String message = RandomStringUtils.randomAlphabetic(8);
+        RestAssured
+                .given()
+                .body(message)
+                .post("/message");
+
+        Awaitility.await().atMost(5, TimeUnit.SECONDS)
+                .untilAsserted(() -> 
assertThat(Files.readString(LOG_FILE.toPath())).contains("ActiveMQ received: " 
+ message));
+    }
+
+    @Test
+    @Order(2)
+    public void shouldRollbackMessageTest() {
+        final String message = RandomStringUtils.randomAlphabetic(8) + " 
rollback";
+        RestAssured
+                .given()
+                .body(message)
+                .post("/message");
+
+        Awaitility.await().atMost(5, TimeUnit.SECONDS)
+                .untilAsserted(() -> 
assertThat(Files.readString(LOG_FILE.toPath()))
+                        .containsSubsequence(
+                                "Sending message from IBMMQ to ActiveMQ: " + 
message,
+                                "Simulated rollback",
+                                "Redelivering message after rollback to 
ActiveMQ: " + message,
+                                "ActiveMQ received: " + message));
+    }
+
+    @Test
+    @Order(3)
+    public void shouldCrashTest() {
+        final String message = RandomStringUtils.randomAlphabetic(8) + " 
crash";
+        RestAssured
+                .given()
+                .body(message)
+                .post("/message");
+
+        Awaitility.await().atMost(5, TimeUnit.SECONDS)
+                .untilAsserted(() -> 
assertThat(Files.readString(LOG_FILE.toPath())).contains("Crashing the 
system"));
+        File tmDir = Paths
+                
.get(ConfigProvider.getConfig().getValue("quarkus.transaction-manager.object-store.directory",
 String.class),
+                        "ShadowNoFileLockStore", "defaultStore", 
"StateManager", "BasicAction", "TwoPhaseCoordinator",
+                        "AtomicAction")
+                .toFile();
+        File dummyXaDir = new 
File(ConfigProvider.getConfig().getValue("dummy.resource.directory", 
String.class));
+
+        assertThat(tmDir.list()).hasSize(1);
+        assertThat(dummyXaDir.list()).hasSize(1);
+    }
+}
diff --git 
a/message-bridge/src/test/java/org/acme/message/bridge/resource/IBMMQTestResource.java
 
b/message-bridge/src/test/java/org/acme/message/bridge/resource/IBMMQTestResource.java
new file mode 100644
index 0000000..d343037
--- /dev/null
+++ 
b/message-bridge/src/test/java/org/acme/message/bridge/resource/IBMMQTestResource.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.acme.message.bridge.resource;
+
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+public class IBMMQTestResource implements QuarkusTestResourceLifecycleManager {
+    private static final String IMAGE_NAME = 
"icr.io/ibm-messaging/mq:9.3.2.1-r1";
+    private static final int PORT = 1414;
+    private static final String QUEUE_MANAGER_NAME = "QM1";
+    private static final String USER = "app";
+    private static final String PASSWORD = "passw0rd";
+    private static final String MESSAGING_CHANNEL = "DEV.APP.SVRCONN";
+    private GenericContainer<?> container;
+
+    @Override
+    public Map<String, String> start() {
+        container = new GenericContainer<>(DockerImageName.parse(IMAGE_NAME))
+                .withExposedPorts(PORT)
+                .withEnv(Map.of(
+                        "LICENSE", 
ConfigProvider.getConfig().getValue("ibm.mq.container.license", String.class),
+                        "MQ_QMGR_NAME", QUEUE_MANAGER_NAME,
+                        "MQ_APP_PASSWORD", PASSWORD))
+                // AMQ5806I is a message code for queue manager start
+                .waitingFor(Wait.forLogMessage(".*AMQ5806I.*", 1));
+        container.start();
+
+        return Map.of(
+                "ibm.mq.host", container.getHost(),
+                "ibm.mq.port", container.getMappedPort(PORT).toString(),
+                "ibm.mq.user", USER,
+                "ibm.mq.password", PASSWORD,
+                "ibm.mq.queueManagerName", QUEUE_MANAGER_NAME,
+                "ibm.mq.channel", MESSAGING_CHANNEL);
+    }
+
+    @Override
+    public void stop() {
+        if (container != null) {
+            container.stop();
+        }
+    }
+}
diff --git a/message-bridge/src/test/resources/application.properties 
b/message-bridge/src/test/resources/application.properties
new file mode 100644
index 0000000..3d4bc37
--- /dev/null
+++ b/message-bridge/src/test/resources/application.properties
@@ -0,0 +1,19 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+# Silence the logs from the in memory activemq broker
+quarkus.log.category."org.apache.activemq".level=FATAL
+ibm.mq.container.license=accept
diff --git a/message-bridge/src/test/resources/broker.xml 
b/message-bridge/src/test/resources/broker.xml
new file mode 100644
index 0000000..d7b98e4
--- /dev/null
+++ b/message-bridge/src/test/resources/broker.xml
@@ -0,0 +1,41 @@
+<!--
+
+    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 xmlns="urn:activemq" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="urn:activemq 
../../../../src/schema/artemis-configuration.xsd">
+    <core xmlns="urn:activemq:core">
+
+        <bindings-directory>./target/messaging/bindings</bindings-directory>
+        <journal-directory>./target/messaging/journal</journal-directory>
+        
<large-messages-directory>./target/messaging/largemessages</large-messages-directory>
+        <paging-directory>./target/messaging/paging</paging-directory>
+
+        <security-enabled>false</security-enabled>
+
+        <acceptors>
+            <acceptor name="tcp">tcp://localhost:61616</acceptor>
+        </acceptors>
+
+        <addresses>
+            <address name="in">
+                <anycast>
+                    <queue name="in"/>
+                </anycast>
+            </address>
+        </addresses>
+    </core>
+</configuration>


Reply via email to