From 7093bc82c7efa7976f1bb727a980a0457be8cbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=BCth?= <git@jotschi.de> Date: Mon, 28 Aug 2017 20:50:17 +0200 Subject: [PATCH] Bump gremlin version and add TxHandlers --- CHANGELOG.md | 15 ++- pom.xml | 6 +- .../syncleus/ferma/AbstractVertexFrame.java | 2 - .../syncleus/ferma/DelegatingFramedGraph.java | 1 - .../syncleus/ferma/DelegatingTransaction.java | 31 +++-- .../syncleus/ferma/WrappedTransaction.java | 16 ++- .../ferma/annotations/GraphElement.java | 30 +++++ .../com/syncleus/ferma/tx/AbstractTx.java | 65 +++++++++ .../com/syncleus/ferma/tx/FramedTxGraph.java | 34 +++++ src/main/java/com/syncleus/ferma/tx/Tx.java | 86 ++++++++++++ .../java/com/syncleus/ferma/tx/TxAction.java | 8 ++ .../java/com/syncleus/ferma/tx/TxAction0.java | 8 ++ .../java/com/syncleus/ferma/tx/TxAction1.java | 8 ++ .../java/com/syncleus/ferma/tx/TxAction2.java | 8 ++ .../java/com/syncleus/ferma/tx/TxFactory.java | 104 ++++++++++++++ .../ferma/tx/WrappedFramedTxGraph.java | 9 ++ .../com/syncleus/ferma/tx/DummyGraph.java | 22 +++ .../syncleus/ferma/tx/DummyTransaction.java | 14 ++ .../com/syncleus/ferma/tx/TxFactoryTest.java | 127 ++++++++++++++++++ 19 files changed, 576 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/syncleus/ferma/annotations/GraphElement.java create mode 100644 src/main/java/com/syncleus/ferma/tx/AbstractTx.java create mode 100644 src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java create mode 100644 src/main/java/com/syncleus/ferma/tx/Tx.java create mode 100644 src/main/java/com/syncleus/ferma/tx/TxAction.java create mode 100644 src/main/java/com/syncleus/ferma/tx/TxAction0.java create mode 100644 src/main/java/com/syncleus/ferma/tx/TxAction1.java create mode 100644 src/main/java/com/syncleus/ferma/tx/TxAction2.java create mode 100644 src/main/java/com/syncleus/ferma/tx/TxFactory.java create mode 100644 src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java create mode 100644 src/test/java/com/syncleus/ferma/tx/DummyGraph.java create mode 100644 src/test/java/com/syncleus/ferma/tx/DummyTransaction.java create mode 100644 src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 136fcd6e..24af3d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ # Ferma Changelog -## 3.0.4 +## 3.1.0 + +* Added abstraction code for tinkerpop transactions which ease usage of transactions within ferma. + The new code provides functional interfaces for transactions and methods which allow + access to transactions from nested functions without the need to pass the original transaction object along. + Once a transaction has been opened it can be accessed from anywhere within the same thread + via the `Tx.getActive()` method. + + The `TxFactoryTest` class and the [ferma-orientdb extension](https://github.com/syncleus/ferma-orientdb) + contain examples how these classes and methods can be used. + +* Updated the following dependencies + * tinkergraph-gremlin: 3.2.4 -> 3.2.5 + ## 3.0.3 diff --git a/pom.xml b/pom.xml index 7e409225..03d1646b 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ <groupId>com.syncleus.ferma</groupId> <artifactId>ferma</artifactId> <packaging>jar</packaging> - <version>3.0.4-SNAPSHOT</version> + <version>3.1.0-SNAPSHOT</version> <name>Ferma</name> <description>An ORM for the Tinkerpop3 graph stack.</description> @@ -158,12 +158,12 @@ <dependency> <groupId>org.apache.tinkerpop</groupId> <artifactId>gremlin-core</artifactId> - <version>3.2.4</version> + <version>3.2.5</version> </dependency> <dependency> <groupId>org.apache.tinkerpop</groupId> <artifactId>tinkergraph-gremlin</artifactId> - <version>3.2.4</version> + <version>3.2.5</version> <scope>test</scope> </dependency> <dependency> diff --git a/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java b/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java index 3b2c1ca1..f00568b7 100644 --- a/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java +++ b/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java @@ -28,10 +28,8 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedElement; -import javax.annotation.Nullable; import java.util.Iterator; import java.util.function.Consumer; diff --git a/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java b/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java index d9a4d554..8a12aec5 100644 --- a/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java +++ b/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java @@ -36,7 +36,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSo import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Graph; -import javax.annotation.Nullable; import java.io.IOException; import java.util.Collection; import java.util.Iterator; diff --git a/src/main/java/com/syncleus/ferma/DelegatingTransaction.java b/src/main/java/com/syncleus/ferma/DelegatingTransaction.java index b998c07b..b1254852 100644 --- a/src/main/java/com/syncleus/ferma/DelegatingTransaction.java +++ b/src/main/java/com/syncleus/ferma/DelegatingTransaction.java @@ -31,51 +31,62 @@ public class DelegatingTransaction implements WrappedTransaction { @Override public void open() { - this.delegate.open(); + this.getDelegate().open(); } @Override public void commit() { - this.delegate.commit(); + this.getDelegate().commit(); } @Override public void rollback() { - this.delegate.rollback(); + this.getDelegate().rollback(); } @Override public WrappedFramedGraph<? extends Graph> createThreadedTx() { - return new DelegatingFramedGraph<>(this.delegate.createThreadedTx(), this.parentGraph.getBuilder(), this.parentGraph.getTypeResolver()); + return new DelegatingFramedGraph<>(this.getDelegate().createThreadedTx(), this.getGraph().getBuilder(), this.getGraph().getTypeResolver()); } @Override public boolean isOpen() { - return this.delegate.isOpen(); + return this.getDelegate().isOpen(); } @Override public void readWrite() { - this.delegate.readWrite(); + this.getDelegate().readWrite(); } @Override public void close() { - this.delegate.close(); + this.getDelegate().close(); } @Override public void addTransactionListener(final Consumer<Transaction.Status> listener) { - this.delegate.addTransactionListener(listener); + this.getDelegate().addTransactionListener(listener); } @Override public void removeTransactionListener(final Consumer<Transaction.Status> listener) { - this.delegate.removeTransactionListener(listener); + this.getDelegate().removeTransactionListener(listener); } @Override public void clearTransactionListeners() { - this.delegate.clearTransactionListeners(); + this.getDelegate().clearTransactionListeners(); } + + @Override + public Transaction getDelegate() { + return delegate; + } + + @Override + public WrappedFramedGraph<? extends Graph> getGraph() { + return parentGraph; + } + } diff --git a/src/main/java/com/syncleus/ferma/WrappedTransaction.java b/src/main/java/com/syncleus/ferma/WrappedTransaction.java index 58f7087f..adc77940 100644 --- a/src/main/java/com/syncleus/ferma/WrappedTransaction.java +++ b/src/main/java/com/syncleus/ferma/WrappedTransaction.java @@ -15,9 +15,11 @@ */ package com.syncleus.ferma; +import java.util.function.Consumer; + import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; import org.apache.tinkerpop.gremlin.structure.util.AbstractTransaction; -import java.util.function.Consumer; /** * A set of methods that allow for control of transactional behavior of a {@link WrappedFramedGraph} instance. Providers may @@ -95,4 +97,16 @@ public interface WrappedTransaction extends AutoCloseable { * Removes all transaction listeners. */ public void clearTransactionListeners(); + + /** + * Returns the raw wrapped tinkerpop transaction. + * @return + */ + Transaction getDelegate(); + + /** + * Returns the parent graph for the transaction. + * @return + */ + WrappedFramedGraph<? extends Graph> getGraph(); } diff --git a/src/main/java/com/syncleus/ferma/annotations/GraphElement.java b/src/main/java/com/syncleus/ferma/annotations/GraphElement.java new file mode 100644 index 00000000..fa9e20a7 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/annotations/GraphElement.java @@ -0,0 +1,30 @@ +/** + * Copyright 2004 - 2016 Syncleus, Inc. + * + * Licensed 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 com.syncleus.ferma.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation which is used to identify classes which represent graph elements. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface GraphElement { + +} diff --git a/src/main/java/com/syncleus/ferma/tx/AbstractTx.java b/src/main/java/com/syncleus/ferma/tx/AbstractTx.java new file mode 100644 index 00000000..e58f3e3f --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/AbstractTx.java @@ -0,0 +1,65 @@ +/** + * Copyright 2004 - 2016 Syncleus, Inc. + * + * Licensed 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 com.syncleus.ferma.tx; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; + +import com.syncleus.ferma.DelegatingTransaction; +import com.syncleus.ferma.WrappedFramedGraph; + +/** + * An abstract class that can be used to implement vendor specific graph database Tx classes. + */ +public abstract class AbstractTx<G extends FramedTxGraph> extends DelegatingTransaction implements Tx { + + private boolean isSuccess = false; + + public AbstractTx(Transaction delegate, WrappedFramedGraph<? extends Graph> parentGraph) { + super(delegate, parentGraph); + } + + @Override + public void success() { + isSuccess = true; + } + + @Override + public void failure() { + isSuccess = false; + } + + /** + * Return the state of the success status flag. + * + * @return + */ + protected boolean isSuccess() { + return isSuccess; + } + + @Override + public void close() { + Tx.setActive(null); + if (isSuccess()) { + commit(); + } else { + rollback(); + } + getDelegate().close(); + } + +} diff --git a/src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java b/src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java new file mode 100644 index 00000000..5a327b52 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java @@ -0,0 +1,34 @@ +package com.syncleus.ferma.tx; + +import com.syncleus.ferma.FramedGraph; +import com.syncleus.ferma.WrappedTransaction; + +/** + * Adapted flavor of the {@link FramedGraph}. This interface will return a {@link Tx} instead of a {@link WrappedTransaction}. The {@link Tx} interface contains + * some useful methods which makes it easier is some cases to work with transactions. This includes automatic rollback within the autoclosable and transaction + * reference handling. + */ +public interface FramedTxGraph extends FramedGraph { + + /** + * Return an active transaction or create a new transaction if no active could be found. + */ + @Override + default Tx tx() { + if (Tx.getActive() != null) { + return Tx.getActive(); + } else { + Tx tx = createTx(); + Tx.setActive(tx); + return tx; + } + } + + /** + * Create a new transaction. + * + * @return + */ + Tx createTx(); + +} diff --git a/src/main/java/com/syncleus/ferma/tx/Tx.java b/src/main/java/com/syncleus/ferma/tx/Tx.java new file mode 100644 index 00000000..acbcff73 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/Tx.java @@ -0,0 +1,86 @@ +/** + * Copyright 2004 - 2016 Syncleus, Inc. + * + * Licensed 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 com.syncleus.ferma.tx; + +import java.io.IOException; + +import com.syncleus.ferma.WrappedTransaction; + +/** + * A {@link Tx} is an extended flavor of the {@link WrappedTransaction}. This interface provides methods to store and retrieve transaction references. This is + * useful if you want to access a running transaction without passing along the transaction object in your implementation. Additionally this interface provides + * the {@link #success()} and {@link #failure()} methods which can be used within the {@link #close()} method in order to rollback or commit the transaction + * according to the state of the flag which is set using these methods. The {@link AbstractTx} class contains an abstract implementation of this mechanism. + */ +public interface Tx extends WrappedTransaction { + + /** + * Thread local that is used to store references to the used graph. + */ + public static ThreadLocal<Tx> threadLocalGraph = new ThreadLocal<>(); + + /** + * Set the nested active transaction for the current thread. + * + * @param tx + * Transaction + */ + public static void setActive(Tx tx) { + Tx.threadLocalGraph.set(tx); + } + + /** + * Return the current active graph. A transaction should be the only place where this threadlocal is updated. + * + * @return Currently active transaction + */ + public static Tx getActive() { + return Tx.threadLocalGraph.get(); + } + + /** + * Mark the transaction as succeeded. The autoclosable will invoke a commit when completing. + */ + void success(); + + /** + * Mark the transaction as failed. The autoclosable will invoke a rollback when completing. + */ + void failure(); + + /** + * Invoke rollback or commit when closing the autoclosable. By default a rollback will be invoked. + * + * @throws IOException + */ + @Override + void close(); + + /** + * Add new isolated vertex to the graph. + * + * @param <T> + * The type used to frame the element. + * @param kind + * The kind of the vertex + * @return The framed vertex + * + */ + default <T> T addVertex(Class<T> kind) { + return getGraph().addFramedVertex(kind); + } + +} diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction.java b/src/main/java/com/syncleus/ferma/tx/TxAction.java new file mode 100644 index 00000000..f946e58e --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/TxAction.java @@ -0,0 +1,8 @@ +package com.syncleus.ferma.tx; + +@FunctionalInterface +public interface TxAction<T> { + + T handle(Tx tx) throws Exception; + +} diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction0.java b/src/main/java/com/syncleus/ferma/tx/TxAction0.java new file mode 100644 index 00000000..e354e990 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/TxAction0.java @@ -0,0 +1,8 @@ +package com.syncleus.ferma.tx; + +@FunctionalInterface +public interface TxAction0 { + + void handle() throws Exception; + +} diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction1.java b/src/main/java/com/syncleus/ferma/tx/TxAction1.java new file mode 100644 index 00000000..75efc341 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/TxAction1.java @@ -0,0 +1,8 @@ +package com.syncleus.ferma.tx; + +@FunctionalInterface +public interface TxAction1<T> { + + T handle() throws Exception; + +} diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction2.java b/src/main/java/com/syncleus/ferma/tx/TxAction2.java new file mode 100644 index 00000000..c2afdf41 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/TxAction2.java @@ -0,0 +1,8 @@ +package com.syncleus.ferma.tx; + +@FunctionalInterface +public interface TxAction2 { + + void handle(Tx tx) throws Exception; + +} diff --git a/src/main/java/com/syncleus/ferma/tx/TxFactory.java b/src/main/java/com/syncleus/ferma/tx/TxFactory.java new file mode 100644 index 00000000..b93809d0 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/TxFactory.java @@ -0,0 +1,104 @@ +/** + * Copyright 2004 - 2016 Syncleus, Inc. + * + * Licensed 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 com.syncleus.ferma.tx; + +/** + * Interface which can be used for custom transaction factories in + * order to provide various ways of executing transaction handlers. + */ +public interface TxFactory { + + /** + * Return a new autoclosable transaction handler. This object should be used within a try-with-resource block. + * Return an active transaction or create a new transaction if no active could be found. + * + * <pre> + * { + * @code + * try(Tx tx = db.tx()) { + * // interact with graph db here + * } + * } + * </pre> + * + * @return Created transaction + */ + default Tx tx() { + if (Tx.getActive() != null) { + return Tx.getActive(); + } else { + Tx tx = createTx(); + Tx.setActive(tx); + return tx; + } + } + + /** + * Create a new transaction. + * + * @return + */ + Tx createTx(); + + /** + * Execute the txHandler within the scope of a transaction and call + * the result handler once the transaction handler code has finished. + * + * @param txHandler + * Handler that will be executed within the scope of the transaction. + * @return Object which was returned by the handler + */ + <T> T tx(TxAction<T> txHandler); + + /** + * Execute the txHandler within the scope of a transaction. + * + * @param txHandler + * Handler that will be executed within the scope of the transaction. + */ + default void tx(TxAction0 txHandler) { + tx((tx) -> { + txHandler.handle(); + }); + } + + /** + * Execute the txHandler within the scope of a transaction. + * + * @param txHandler + * Handler that will be executed within the scope of the transaction. + * @return Result of the handler + */ + default <T> T tx(TxAction1<T> txHandler) { + return tx((tx) -> { + return txHandler.handle(); + }); + } + + /** + * Execute the txHandler within the scope of a transaction. + * + * @param txHandler + * Handler that will be executed within the scope of the transaction. + */ + default void tx(TxAction2 txHandler) { + tx((tx) -> { + txHandler.handle(tx); + return null; + }); + } + +} diff --git a/src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java b/src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java new file mode 100644 index 00000000..384d01e9 --- /dev/null +++ b/src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java @@ -0,0 +1,9 @@ +package com.syncleus.ferma.tx; + +import org.apache.tinkerpop.gremlin.structure.Graph; + +import com.syncleus.ferma.WrappedFramedGraph; + +public interface WrappedFramedTxGraph<G extends Graph> extends WrappedFramedGraph<G>, FramedTxGraph { + +} diff --git a/src/test/java/com/syncleus/ferma/tx/DummyGraph.java b/src/test/java/com/syncleus/ferma/tx/DummyGraph.java new file mode 100644 index 00000000..a14e3ea6 --- /dev/null +++ b/src/test/java/com/syncleus/ferma/tx/DummyGraph.java @@ -0,0 +1,22 @@ +package com.syncleus.ferma.tx; + +import org.apache.tinkerpop.gremlin.structure.Graph; + +import com.syncleus.ferma.DelegatingFramedGraph; + +public class DummyGraph extends DelegatingFramedGraph<Graph> implements FramedTxGraph { + + public DummyGraph(Graph delegate) { + super(delegate); + } + + @Override + public Tx tx() { + return FramedTxGraph.super.tx(); + } + + @Override + public Tx createTx() { + return new DummyTransaction(null, this); + } +} diff --git a/src/test/java/com/syncleus/ferma/tx/DummyTransaction.java b/src/test/java/com/syncleus/ferma/tx/DummyTransaction.java new file mode 100644 index 00000000..4924d0be --- /dev/null +++ b/src/test/java/com/syncleus/ferma/tx/DummyTransaction.java @@ -0,0 +1,14 @@ +package com.syncleus.ferma.tx; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; + +import com.syncleus.ferma.WrappedFramedGraph; + +public class DummyTransaction extends AbstractTx<FramedTxGraph>{ + + public DummyTransaction(Transaction delegate, WrappedFramedGraph<? extends Graph> parentGraph) { + super(delegate, parentGraph); + } + +} diff --git a/src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java b/src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java new file mode 100644 index 00000000..d79f4611 --- /dev/null +++ b/src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java @@ -0,0 +1,127 @@ +package com.syncleus.ferma.tx; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.verify; + +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TxFactoryTest implements TxFactory { + + private DummyTransaction mock = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS); + + @Before + public void setupMocks() { + Transaction rawTx = Mockito.mock(Transaction.class); + Mockito.when(mock.getDelegate()).thenReturn(rawTx); + } + + @Test + public void testTx0() { + try (Tx tx = tx()) { + + } + verify(mock).close(); + } + + @Test + public void testTx1() { + tx(() -> { + + }); + verify(mock).close(); + } + + @Test + public void testTx2() { + assertEquals("test", tx(() -> { + return "test"; + })); + verify(mock).close(); + } + + @Test + public void testTx3() { + assertEquals("test", tx((tx) -> { + tx.failure(); + tx.success(); + return "test"; + })); + verify(mock).close(); + } + + @Test + public void testAbstractTxSucceeding() { + DummyTransaction tx = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS); + Transaction rawTx = Mockito.mock(Transaction.class); + Mockito.when(tx.getDelegate()).thenReturn(rawTx); + DummyGraph graphMock = Mockito.mock(DummyGraph.class, Mockito.CALLS_REAL_METHODS); + Mockito.when(graphMock.createTx()).thenReturn(tx); + + try (Tx tx2 = graphMock.tx()) { + assertNotNull(Tx.getActive()); + tx2.success(); + } + assertNull(Tx.getActive()); + verify(tx).commit(); + verify(tx).close(); + verify(tx, Mockito.never()).rollback(); + } + + @Test + public void testAbstractTxDefault() { + DummyTransaction tx = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS); + Transaction rawTx = Mockito.mock(Transaction.class); + Mockito.when(tx.getDelegate()).thenReturn(rawTx); + DummyGraph graphMock = Mockito.mock(DummyGraph.class, Mockito.CALLS_REAL_METHODS); + Mockito.when(graphMock.tx()).thenReturn(tx); + try (Tx tx2 = tx) { + assertNotNull(Tx.getActive()); + // Don't call tx2.success() or tx2.failure() + } + assertNull(Tx.getActive()); + verify(tx).close(); + verify(graphMock.tx()).rollback(); + verify(graphMock.tx()).close(); + verify(graphMock.tx(), Mockito.never()).commit(); + } + + @Test + public void testAbstractTxFailing() { + DummyTransaction tx = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS); + Transaction rawTx = Mockito.mock(Transaction.class); + Mockito.when(tx.getDelegate()).thenReturn(rawTx); + DummyGraph graphMock = Mockito.mock(DummyGraph.class, Mockito.CALLS_REAL_METHODS); + Mockito.when(graphMock.tx()).thenReturn(tx); + try (Tx tx2 = tx) { + assertNotNull(Tx.getActive()); + tx2.failure(); + } + assertNull(Tx.getActive()); + verify(tx).close(); + verify(graphMock.tx()).rollback(); + verify(graphMock.tx()).close(); + verify(graphMock.tx(), Mockito.never()).commit(); + } + + @Override + public Tx createTx() { + return mock; + } + + @Override + public <T> T tx(TxAction<T> txHandler) { + try (Tx tx = tx()) { + try { + return txHandler.handle(mock); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + +} -- GitLab