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.git
The following commit(s) were added to refs/heads/main by this push:
new e46b803 Add tests and documentation for transaction policies
e46b803 is described below
commit e46b8030e256491bb80a7c1ca7ba0c5c3e7609a7
Author: James Netherton <[email protected]>
AuthorDate: Thu May 27 10:12:34 2021 +0100
Add tests and documentation for transaction policies
---
.../ROOT/pages/reference/extensions/jta.adoc | 33 +++++-
extensions/jta/deployment/pom.xml | 10 ++
.../jta/MandatoryJtaTransactionPolicyTest.java | 89 ++++++++++++++
.../quarkus/component/jta/MockTransaction.java | 60 ++++++++++
.../jta/MockTransactionManagerProducer.java | 38 ++++++
.../jta/NeverJtaTransactionPolicyTest.java | 79 ++++++++++++
.../jta/NotSupportedJtaTransactionPolicyTest.java | 72 +++++++++++
.../jta/RequiredJtaTransactionPolicyTest.java | 132 +++++++++++++++++++++
.../jta/RequiresNewJtaTransactionPolicyTest.java | 92 ++++++++++++++
.../jta/SupportsJtaTransactionPolicyTest.java | 66 +++++++++++
extensions/jta/runtime/src/main/doc/usage.adoc | 33 +++++-
.../quarkus/component/jta/it/JtaResource.java | 3 +-
.../camel/quarkus/component/jta/it/JtaRoutes.java | 4 +-
.../camel/quarkus/component/jta/it/JtaTest.java | 46 +++++--
14 files changed, 739 insertions(+), 18 deletions(-)
diff --git a/docs/modules/ROOT/pages/reference/extensions/jta.adoc
b/docs/modules/ROOT/pages/reference/extensions/jta.adoc
index 3ec503b..f1f213b 100644
--- a/docs/modules/ROOT/pages/reference/extensions/jta.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/jta.adoc
@@ -43,7 +43,6 @@ Check the xref:user-guide/index.adoc[User guide] for more
information about writ
This extension should be added when you need to use the `transacted()` EIP in
the router. It leverages the transaction capabilities provided by the
narayana-jta extension in Quarkus.
-
Refer to the https://quarkus.io/guides/transaction[Quarkus Transaction guide]
for the more details about transaction support. For a simple usage:
[source,java]
@@ -55,3 +54,35 @@ from("direct:transaction")
.log("all data are in the ds1 and ds2")
----
+Support is provided for various transaction policies.
+
+[cols="50,.^50]
+|===
+|Policy | Description
+
+| `PROPAGATION_MANDATORY`
+
+| Support a current transaction; throw an exception if no current transaction
exists.
+
+| `PROPAGATION_NEVER`
+
+| Do not support a current transaction; throw an exception if a current
transaction exists.
+
+| `PROPAGATION_NOT_SUPPORTED`
+
+| Do not support a current transaction; rather always execute
non-transactionally.
+
+| `PROPAGATION_REQUIRED`
+
+| Support a current transaction; create a new one if none exists.
+
+| `PROPAGATION_REQUIRES_NEW`
+
+| Create a new transaction, suspending the current transaction if one exists.
+
+| `PROPAGATION_SUPPORTS`
+
+| Support a current transaction; execute non-transactionally if none exists.
+
+|===
+
diff --git a/extensions/jta/deployment/pom.xml
b/extensions/jta/deployment/pom.xml
index 1f41b53..e845f68 100644
--- a/extensions/jta/deployment/pom.xml
+++ b/extensions/jta/deployment/pom.xml
@@ -42,6 +42,16 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-narayana-jta-deployment</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5-internal</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5-mockito</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MandatoryJtaTransactionPolicyTest.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MandatoryJtaTransactionPolicyTest.java
new file mode 100644
index 0000000..4ac9c59
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MandatoryJtaTransactionPolicyTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.jsoup.helper.Validate.fail;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+public class MandatoryJtaTransactionPolicyTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClass(MockTransactionManagerProducer.class));
+
+ @Inject
+ TransactionManager transactionManager;
+
+ @Inject
+ @Named("PROPAGATION_MANDATORY")
+ MandatoryJtaTransactionPolicy transactionPolicy;
+
+ @AfterEach
+ public void afterEach() {
+ reset(transactionManager);
+ }
+
+ @Test
+ public void runTransactionPolicyNoTransaction() throws Exception {
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
+ try {
+ transactionPolicy.run(() -> fail("Transaction policy should not
run"));
+ } catch (Throwable throwable) {
+ assertTrue(throwable instanceof IllegalStateException);
+ }
+ }
+
+ @Test
+ public void runTransactionPolicyMarkedRollback() throws Exception {
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_MARKED_ROLLBACK);
+ try {
+ transactionPolicy.run(() -> fail("Transaction policy should not
run"));
+ } catch (Throwable throwable) {
+ assertTrue(throwable instanceof IllegalStateException);
+ }
+ }
+
+ @Test
+ public void runTransactionPolicyActiveTransaction() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ when(transactionManager.getStatus()).thenReturn(Status.STATUS_ACTIVE);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MockTransaction.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MockTransaction.java
new file mode 100644
index 0000000..b947b8b
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MockTransaction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+
+public class MockTransaction implements Transaction {
+
+ @Override
+ public void commit() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException,
+ IllegalStateException, SystemException {
+ }
+
+ @Override
+ public boolean delistResource(XAResource xaRes, int flag) throws
IllegalStateException, SystemException {
+ return false;
+ }
+
+ @Override
+ public boolean enlistResource(XAResource xaRes) throws RollbackException,
IllegalStateException, SystemException {
+ return false;
+ }
+
+ @Override
+ public int getStatus() throws SystemException {
+ return 0;
+ }
+
+ @Override
+ public void registerSynchronization(Synchronization sync) throws
RollbackException, IllegalStateException, SystemException {
+ }
+
+ @Override
+ public void rollback() throws IllegalStateException, SystemException {
+ }
+
+ @Override
+ public void setRollbackOnly() throws IllegalStateException,
SystemException {
+ }
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MockTransactionManagerProducer.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MockTransactionManagerProducer.java
new file mode 100644
index 0000000..ff31495
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/MockTransactionManagerProducer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Alternative;
+import javax.enterprise.inject.Produces;
+import javax.inject.Singleton;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.arc.AlternativePriority;
+import org.mockito.Mockito;
+
+@ApplicationScoped
+public class MockTransactionManagerProducer {
+
+ @Alternative
+ @AlternativePriority(1)
+ @Produces
+ @Singleton
+ public TransactionManager transactionManager() {
+ return Mockito.mock(TransactionManager.class);
+ }
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/NeverJtaTransactionPolicyTest.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/NeverJtaTransactionPolicyTest.java
new file mode 100644
index 0000000..ae2f2f5
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/NeverJtaTransactionPolicyTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.jsoup.helper.Validate.fail;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+public class NeverJtaTransactionPolicyTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClass(MockTransactionManagerProducer.class));
+
+ @Inject
+ TransactionManager transactionManager;
+
+ @Inject
+ @Named("PROPAGATION_NEVER")
+ NeverJtaTransactionPolicy transactionPolicy;
+
+ @AfterEach
+ public void afterEach() {
+ reset(transactionManager);
+ }
+
+ @Test
+ public void runTransactionPolicyActiveTransaction() throws Exception {
+ when(transactionManager.getStatus()).thenReturn(Status.STATUS_ACTIVE);
+ try {
+ transactionPolicy.run(() -> fail("Transaction policy should not
run"));
+ } catch (Throwable throwable) {
+ assertTrue(throwable instanceof IllegalStateException);
+ }
+ }
+
+ @Test
+ public void runTransactionPolicyNoTransaction() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/NotSupportedJtaTransactionPolicyTest.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/NotSupportedJtaTransactionPolicyTest.java
new file mode 100644
index 0000000..bde99c1
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/NotSupportedJtaTransactionPolicyTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.jsoup.helper.Validate.fail;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class NotSupportedJtaTransactionPolicyTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClasses(MockTransactionManagerProducer.class,
MockTransaction.class));
+
+ @Inject
+ TransactionManager transactionManager;
+
+ @Inject
+ @Named("PROPAGATION_NOT_SUPPORTED")
+ NotSupportedJtaTransactionPolicy transactionPolicy;
+
+ @AfterEach
+ public void afterEach() {
+ reset(transactionManager);
+ }
+
+ @Test
+ public void runTransactionPolicy() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ MockTransaction transaction = new MockTransaction();
+ when(transactionManager.suspend()).thenReturn(transaction);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).resume(transaction);
+ }
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/RequiredJtaTransactionPolicyTest.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/RequiredJtaTransactionPolicyTest.java
new file mode 100644
index 0000000..3686226
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/RequiredJtaTransactionPolicyTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.jsoup.helper.Validate.fail;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class RequiredJtaTransactionPolicyTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClass(MockTransactionManagerProducer.class));
+
+ @Inject
+ TransactionManager transactionManager;
+
+ @Inject
+ @Named("PROPAGATION_REQUIRED")
+ RequiredJtaTransactionPolicy transactionPolicy;
+
+ @AfterEach
+ public void afterEach() {
+ reset(transactionManager);
+ }
+
+ @Test
+ public void runTransactionPolicyNoTransaction() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).begin();
+ verify(transactionManager, times(1)).commit();
+ }
+
+ @Test
+ public void runTransactionPolicyMarkedRollback() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_MARKED_ROLLBACK);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).begin();
+ verify(transactionManager, times(1)).commit();
+ }
+
+ @Test
+ public void runTransactionPolicyActiveTransaction() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ when(transactionManager.getStatus()).thenReturn(Status.STATUS_ACTIVE);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void runTransactionPolicyWithException() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
+ try {
+ transactionPolicy.run(() -> {
+ latch.countDown();
+ throw new Exception("Simulated exception");
+ });
+ } catch (Throwable throwable) {
+ // Expected
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).rollback();
+ }
+
+ @Test
+ public void runTransactionPolicyWithExceptionActiveTransaction() throws
Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ when(transactionManager.getStatus()).thenReturn(Status.STATUS_ACTIVE);
+ try {
+ transactionPolicy.run(() -> {
+ latch.countDown();
+ throw new Exception("Simulated exception");
+ });
+ } catch (Throwable throwable) {
+ // Expected
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).setRollbackOnly();
+ }
+
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/RequiresNewJtaTransactionPolicyTest.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/RequiresNewJtaTransactionPolicyTest.java
new file mode 100644
index 0000000..64a3d16
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/RequiresNewJtaTransactionPolicyTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.apache.camel.quarkus.component.jta;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.jsoup.helper.Validate.fail;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class RequiresNewJtaTransactionPolicyTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClasses(MockTransactionManagerProducer.class,
MockTransaction.class));
+
+ @Inject
+ TransactionManager transactionManager;
+
+ @Inject
+ @Named("PROPAGATION_REQUIRES_NEW")
+ RequiresNewJtaTransactionPolicy transactionPolicy;
+
+ @AfterEach
+ public void afterEach() {
+ reset(transactionManager);
+ }
+
+ @Test
+ public void runTransactionPolicy() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ MockTransaction transaction = new MockTransaction();
+ when(transactionManager.suspend()).thenReturn(transaction);
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).begin();
+ verify(transactionManager, times(1)).commit();
+ verify(transactionManager, times(1)).resume(transaction);
+ }
+
+ @Test
+ public void runTransactionPolicyWithException() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
when(transactionManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION);
+ try {
+ transactionPolicy.run(() -> {
+ latch.countDown();
+ throw new Exception("Simulated exception");
+ });
+ } catch (Throwable throwable) {
+ // Expected
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ verify(transactionManager, times(1)).rollback();
+ }
+}
diff --git
a/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/SupportsJtaTransactionPolicyTest.java
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/SupportsJtaTransactionPolicyTest.java
new file mode 100644
index 0000000..8b43402
--- /dev/null
+++
b/extensions/jta/deployment/src/test/java/org/apache/camel/quarkus/component/jta/SupportsJtaTransactionPolicyTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.jta;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.transaction.TransactionManager;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.jsoup.helper.Validate.fail;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.reset;
+
+public class SupportsJtaTransactionPolicyTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClass(MockTransactionManagerProducer.class));
+
+ @Inject
+ TransactionManager transactionManager;
+
+ @Inject
+ @Named("PROPAGATION_SUPPORTS")
+ SupportsJtaTransactionPolicy transactionPolicy;
+
+ @AfterEach
+ public void afterEach() {
+ reset(transactionManager);
+ }
+
+ @Test
+ public void runTransactionPolicy() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ try {
+ transactionPolicy.run(() -> latch.countDown());
+ } catch (Throwable throwable) {
+ fail("Expected transaction policy to run successfully");
+ }
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+}
diff --git a/extensions/jta/runtime/src/main/doc/usage.adoc
b/extensions/jta/runtime/src/main/doc/usage.adoc
index 1aadb4a..56c279f 100644
--- a/extensions/jta/runtime/src/main/doc/usage.adoc
+++ b/extensions/jta/runtime/src/main/doc/usage.adoc
@@ -1,6 +1,5 @@
This extension should be added when you need to use the `transacted()` EIP in
the router. It leverages the transaction capabilities provided by the
narayana-jta extension in Quarkus.
-
Refer to the https://quarkus.io/guides/transaction[Quarkus Transaction guide]
for the more details about transaction support. For a simple usage:
[source,java]
@@ -11,3 +10,35 @@ from("direct:transaction")
.to("sql:INSERT INTO A TABLE ...?dataSource=ds2")
.log("all data are in the ds1 and ds2")
----
+
+Support is provided for various transaction policies.
+
+[cols="50,.^50]
+|===
+|Policy | Description
+
+| `PROPAGATION_MANDATORY`
+
+| Support a current transaction; throw an exception if no current transaction
exists.
+
+| `PROPAGATION_NEVER`
+
+| Do not support a current transaction; throw an exception if a current
transaction exists.
+
+| `PROPAGATION_NOT_SUPPORTED`
+
+| Do not support a current transaction; rather always execute
non-transactionally.
+
+| `PROPAGATION_REQUIRED`
+
+| Support a current transaction; create a new one if none exists.
+
+| `PROPAGATION_REQUIRES_NEW`
+
+| Create a new transaction, suspending the current transaction if one exists.
+
+| `PROPAGATION_SUPPORTS`
+
+| Support a current transaction; execute non-transactionally if none exists.
+
+|===
diff --git
a/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaResource.java
b/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaResource.java
index 7fc6035..588f8d9 100644
---
a/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaResource.java
+++
b/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaResource.java
@@ -67,7 +67,8 @@ public class JtaResource {
statement.execute("drop table example");
} catch (Exception ignored) {
}
- statement.execute("create table example (id serial primary
key, message varchar(255) not null)");
+ statement.execute(
+ "create table example (id serial primary key, message
varchar(255) not null, origin varchar(5) not null)");
}
}
}
diff --git
a/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaRoutes.java
b/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaRoutes.java
index a39d446..2828635 100644
---
a/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaRoutes.java
+++
b/integration-tests/jta/src/main/java/org/apache/camel/quarkus/component/jta/it/JtaRoutes.java
@@ -52,7 +52,7 @@ public class JtaRoutes extends RouteBuilder {
.transacted()
.setHeader("message", body())
.to("jms:queue:txTest?connectionFactory=#xaConnectionFactory&disableReplyTo=true")
- .transform().simple("insert into example(message) values
('${body}')")
+ .transform().simple("insert into example(message, origin)
values ('${body}', 'jdbc')")
.to("jdbc:camel-ds?resetAutoCommit=false")
.choice()
.when(header("message").startsWith("fail"))
@@ -68,7 +68,7 @@ public class JtaRoutes extends RouteBuilder {
.transacted()
.setHeader("message", body())
.to("jms:queue:txTest?connectionFactory=#xaConnectionFactory&disableReplyTo=true")
- .to("sql:insert into example(message) values (:#message)")
+ .to("sql:insert into example(message, origin) values
(:#message, 'sqltx')")
.choice()
.when(header("message").startsWith("fail"))
.log("Failing forever with exception")
diff --git
a/integration-tests/jta/src/test/java/org/apache/camel/quarkus/component/jta/it/JtaTest.java
b/integration-tests/jta/src/test/java/org/apache/camel/quarkus/component/jta/it/JtaTest.java
index b518438..af1defc 100644
---
a/integration-tests/jta/src/test/java/org/apache/camel/quarkus/component/jta/it/JtaTest.java
+++
b/integration-tests/jta/src/test/java/org/apache/camel/quarkus/component/jta/it/JtaTest.java
@@ -16,12 +16,21 @@
*/
package org.apache.camel.quarkus.component.jta.it;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.h2.H2DatabaseTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.Matchers.is;
@@ -136,34 +145,32 @@ class JtaTest {
.body(is("not_supported"));
}
- @Test
- public void testJdbcInTx() {
- testTx("/jta/jdbc");
- }
-
- @Test
- public void testSqlInTx() {
- testTx("/jta/sqltx");
- }
-
- private void testTx(String url) {
+ @ParameterizedTest
+ @ValueSource(strings = { "jdbc", "sqltx" })
+ public void testTx(String url) throws SQLException {
final String msg = java.util.UUID.randomUUID().toString().replace("-",
"");
RestAssured.given()
.contentType(ContentType.TEXT)
.body(msg)
- .post(url)
+ .post("/jta/" + url)
.then()
.statusCode(201)
.body(is(msg + " added"));
+ // One row inserted
+ assertDBRowCount(url, 1);
+
RestAssured.given()
.contentType(ContentType.TEXT)
.body("fail")
- .post(url)
+ .post("/jta/" + url)
.then()
.statusCode(500);
+ // Should still have the original row as the other insert attempt was
rolled back
+ assertDBRowCount(url, 1);
+
RestAssured.given()
.contentType(ContentType.TEXT)
.get("/jta/mock")
@@ -172,4 +179,17 @@ class JtaTest {
.body(is("empty"))
.log();
}
+
+ private void assertDBRowCount(String source, int expectedRowCount) throws
SQLException {
+ try (Connection connection =
DriverManager.getConnection("jdbc:h2:tcp://localhost/mem:test")) {
+ try (Statement statement = connection.createStatement()) {
+ try (ResultSet resultSet = statement
+ .executeQuery("SELECT count(*) FROM example WHERE
origin = '" + source + "'")) {
+ if (resultSet.next()) {
+ Assertions.assertEquals(expectedRowCount,
resultSet.getInt(1));
+ }
+ }
+ }
+ }
+ }
}