diff --git a/src/main/java/com/github/sttk/sabi/DataHub.java b/src/main/java/com/github/sttk/sabi/DataHub.java index e9a2ee8..981179b 100644 --- a/src/main/java/com/github/sttk/sabi/DataHub.java +++ b/src/main/java/com/github/sttk/sabi/DataHub.java @@ -88,14 +88,6 @@ public record CreatedDataConnIsNull(String name, String dataConnType) {} */ public record FailToCastDataConn(String name, String castToType) {} - /** - * Represents an error reason that occurred when failing to cast the {@code DataHub} instance - * itself to the expected data access interface type for a {@link Logic}. - * - * @param castFromType The actual type of the {@code DataHub} instance that failed to cast. - */ - public record FailToCastDataHub(String castFromType) {} - /** * Represents an unexpected {@link RuntimeException} that occurred during pre-commit or commit * operations. @@ -128,71 +120,6 @@ public void disuses(String name) { inner.disuses(name); } - /** - * Executes the provided application {@link Logic} without transactional boundaries. The {@code - * DataHub} instance itself is passed as the data access object {@code D} to the {@link Logic}'s - * {@code run} method. - * - * @param The type of the data access object, which typically is {@code DataHub} or an - * interface implemented by {@code DataHub} that {@link Logic} expects. - * @param logic The application logic to execute. - * @throws Exc if an {@link Exc} or {@link RuntimeException} occurs during logic execution or if - * the {@code DataHub} cannot be cast to the expected data access type. - */ - public void run(Logic logic) throws Exc { - D data; - try { - @SuppressWarnings("unchecked") - D d = (D) this; - data = d; - } catch (Exception e) { - throw new Exc(new FailToCastDataHub(this.getClass().getName())); - } - try { - inner.begin(); - logic.run(data); - } catch (Exc | RuntimeException e) { - throw e; - } finally { - inner.end(); - } - } - - /** - * Executes the provided application {@link Logic} within a transactional context. The {@code - * DataHub} instance is passed as the data access object {@code D} to the {@link Logic}'s {@code - * run} method. If the logic completes successfully, a commit operation is attempted. If any - * {@link Exc}, {@link RuntimeException}, or {@link Error} occurs, a rollback operation is - * performed. - * - * @param The type of the data access object, which typically is {@code DataHub} or an - * interface implemented by {@code DataHub} that {@link Logic} expects. - * @param logic The application logic to execute transactionally. - * @throws Exc if an {@link Exc}, {@link RuntimeException}, or {@link Error} occurs during logic - * execution, pre-commit, or commit. The original exception is re-thrown after rollback. - */ - public void txn(Logic logic) throws Exc { - D data; - try { - @SuppressWarnings("unchecked") - D d = (D) this; - data = d; - } catch (Exception e) { - throw new Exc(new FailToCastDataHub(this.getClass().getName())); - } - try { - inner.begin(); - logic.run(data); - inner.commit(); - inner.postCommit(); - } catch (Exc | RuntimeException | Error e) { - inner.rollback(); - throw e; - } finally { - inner.end(); - } - } - /** * Retrieves a {@link DataConn} instance from the managed data sources. This method is part of the * {@link DataAcc} interface implementation. @@ -218,4 +145,20 @@ public C getDataConn(String name, Class cls) throws Exc public void close() { inner.close(); } + + void begin() throws Exc { + inner.begin(); + } + + void commit() throws Exc { + inner.commit(); + } + + void rollback() { + inner.rollback(); + } + + void end() { + inner.end(); + } } diff --git a/src/main/java/com/github/sttk/sabi/Sabi.java b/src/main/java/com/github/sttk/sabi/Sabi.java index 6a4c2fc..022c042 100644 --- a/src/main/java/com/github/sttk/sabi/Sabi.java +++ b/src/main/java/com/github/sttk/sabi/Sabi.java @@ -34,6 +34,14 @@ * } */ public final class Sabi { + /** + * Represents an error reason that occurred when failing to cast the {@code DataHub} instance + * itself to the expected data access interface type for a {@link Logic}. + * + * @param castFromType The actual type of the {@code DataHub} instance that failed to cast. + */ + public record FailToCastDataHub(String castFromType) {} + private Sabi() {} /** @@ -63,4 +71,72 @@ public static void uses(String name, DataSrc ds) { public static AutoCloseable setup() throws Exc { return DataHubInner.setupGlobals(); } + + /** + * Executes the provided application {@link Logic} without transactional boundaries. The {@code + * DataHub} instance in the parameters is passed as the data access object {@code D} to the {@link + * Logic}'s {@code run} method. + * + * @param The type of the data access object, which typically is {@code DataHub} or an + * interface implemented by {@code DataHub} that {@link Logic} expects. + * @param logic The application logic to execute. + * @param hub An instance of a DataHub subclass that inherits the data interface for logic + * arguments. + * @throws Exc if an {@link Exc} or {@link RuntimeException} occurs during logic execution or if + * the {@code DataHub} cannot be cast to the expected data access type. + */ + public static void run(Logic logic, DataHub hub) throws Exc { + D data; + try { + @SuppressWarnings("unchecked") + D d = (D) hub; + data = d; + } catch (Exception e) { + throw new Exc(new FailToCastDataHub(hub.getClass().getName())); + } + try { + hub.begin(); + logic.run(data); + } catch (Exc | RuntimeException e) { + throw e; + } finally { + hub.end(); + } + } + + /** + * Executes the provided application {@link Logic} within a transactional context. The {@code + * DataHub} instance in the parameter is passed as the data access object {@code D} to the {@link + * Logic}'s {@code run} method. If the logic completes successfully, a commit operation is + * attempted. If any {@link Exc}, {@link RuntimeException}, or {@link Error} occurs, a rollback + * operation is performed. + * + * @param The type of the data access object, which typically is {@code DataHub} or an + * interface implemented by {@code DataHub} that {@link Logic} expects. + * @param logic The application logic to execute transactionally. + * @param hub An instance of a DataHub subclass that inherits the data interface for logic + * arguments. + * @throws Exc if an {@link Exc}, {@link RuntimeException}, or {@link Error} occurs during logic + * execution, pre-commit, or commit. The original exception is re-thrown after rollback. + */ + public static void txn(Logic logic, DataHub hub) throws Exc { + D data; + try { + @SuppressWarnings("unchecked") + D d = (D) hub; + data = d; + } catch (Exception e) { + throw new Exc(new FailToCastDataHub(hub.getClass().getName())); + } + try { + hub.begin(); + logic.run(data); + hub.commit(); + } catch (Exc | RuntimeException | Error e) { + hub.rollback(); + throw e; + } finally { + hub.end(); + } + } } diff --git a/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java b/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java index e29f1f3..369a533 100644 --- a/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java +++ b/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java @@ -138,30 +138,28 @@ public void commit() throws Exc { if (!excMap.isEmpty()) { throw new Exc(new DataHub.FailToCommitDataConn(excMap)); } - } - public void rollback() { - var ag = new AsyncGroupImpl(); - var ptr = this.dataConnList.head; + ag = new AsyncGroupImpl(); + ptr = this.dataConnList.head; while (ptr != null) { ag.name = ptr.name; - if (ptr.conn.shouldForceBack()) { - ptr.conn.forceBack(ag); - } else { - ptr.conn.rollback(ag); - } + ptr.conn.postCommit(ag); ptr = ptr.next; } ag.joinAndIgnoreExcs(); } - public void postCommit() { + public void rollback() { var ag = new AsyncGroupImpl(); var ptr = this.dataConnList.head; while (ptr != null) { ag.name = ptr.name; - ptr.conn.postCommit(ag); + if (ptr.conn.shouldForceBack()) { + ptr.conn.forceBack(ag); + } else { + ptr.conn.rollback(ag); + } ptr = ptr.next; } diff --git a/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java b/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java index 97cef1b..91ee6ff 100644 --- a/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java +++ b/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java @@ -316,8 +316,8 @@ void test() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.run(new SampleLogic()); + try (var data = new SampleDataHub()) { + Sabi.run(new SampleLogic(), data); } catch (Exception e) { fail(e); } @@ -358,11 +358,11 @@ void test() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(1, "hello", logger, false)); - hub.uses("bar", new BarDataSrc(2, logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(1, "hello", logger, false)); + data.uses("bar", new BarDataSrc(2, logger, false)); - hub.run(new SampleLogic()); + Sabi.run(new SampleLogic(), data); } catch (Exception e) { fail(e); } @@ -390,11 +390,11 @@ void test_not_run_logic_if_fail_to_setup_local_data_src() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(1, "hello", logger, true)); - hub.uses("bar", new BarDataSrc(2, logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(1, "hello", logger, true)); + data.uses("bar", new BarDataSrc(2, logger, false)); - hub.run(new SampleLogic()); + Sabi.run(new SampleLogic(), data); } catch (Exc e) { switch (e.getReason()) { case DataHub.FailToSetupLocalDataSrcs r -> { @@ -434,10 +434,10 @@ void test() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(2, "Hello", logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(2, "Hello", logger, false)); - hub.run(new SampleLogic()); + Sabi.run(new SampleLogic(), data); } catch (Exception e) { fail(e); } @@ -481,8 +481,8 @@ void test() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.txn(new SampleLogic()); + try (var data = new SampleDataHub()) { + Sabi.txn(new SampleLogic(), data); } catch (Exception e) { fail(e); } @@ -529,11 +529,11 @@ void test() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(1, "Hello", logger, false)); - hub.uses("bar", new BarDataSrc(2, logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(1, "Hello", logger, false)); + data.uses("bar", new BarDataSrc(2, logger, false)); - hub.txn(new SampleLogic()); + Sabi.txn(new SampleLogic(), data); } catch (Exception e) { fail(e); } @@ -567,11 +567,11 @@ void test_not_run_logic_if_fail_to_setup_local_data_src() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(1, "Hello", logger, true)); - hub.uses("bar", new BarDataSrc(2, logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(1, "Hello", logger, true)); + data.uses("bar", new BarDataSrc(2, logger, false)); - hub.txn(new SampleLogic()); + Sabi.txn(new SampleLogic(), data); } catch (Exc e) { switch (e.getReason()) { case DataHub.FailToSetupLocalDataSrcs r -> { @@ -596,11 +596,11 @@ void test_not_run_logic_in_txn_and_rollback() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(1, "Hello", logger, false)); - hub.uses("bar", new BarDataSrc(2, logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(1, "Hello", logger, false)); + data.uses("bar", new BarDataSrc(2, logger, false)); - hub.txn(new FailingLogic()); + Sabi.txn(new FailingLogic(), data); } catch (Exc e) { assertThat(e.getReason()).isEqualTo("ZZZ"); } catch (Exception e) { @@ -640,10 +640,10 @@ void test() { try (var ac = Sabi.setup()) { suppressWarnings_unused(ac); - try (var hub = new SampleDataHub()) { - hub.uses("foo", new FooDataSrc(2, "Hello", logger, false)); + try (var data = new SampleDataHub()) { + data.uses("foo", new FooDataSrc(2, "Hello", logger, false)); - hub.txn(new SampleLogic()); + Sabi.txn(new SampleLogic(), data); } catch (Exception e) { fail(e); } diff --git a/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java b/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java index a85dc09..acc8aa9 100644 --- a/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java +++ b/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java @@ -1038,6 +1038,10 @@ void commit() { "SyncDataConn 2 committed", "AsyncDataConn 3 committed", "SyncDataConn 4 committed", + "AsyncDataConn 1 post committed", + "SyncDataConn 2 post committed", + "AsyncDataConn 3 post committed", + "SyncDataConn 4 post committed", "SyncDataConn 4 closed", "AsyncDataConn 3 closed", "SyncDataConn 2 closed", @@ -2055,70 +2059,14 @@ void force_back() { "SyncDataConn 2 committed", "AsyncDataConn 3 committed", "SyncDataConn 4 committed", - "AsyncDataConn 1 forced back", - "SyncDataConn 2 forced back", - "AsyncDataConn 3 forced back", - "SyncDataConn 4 forced back", - "SyncDataConn 4 closed", - "AsyncDataConn 3 closed", - "SyncDataConn 2 closed", - "AsyncDataConn 1 closed", - "SyncDataSrc 4 closed", - "AsyncDataSrc 3 closed", - "SyncDataSrc 2 closed", - "AsyncDataSrc 1 closed"); - } - - @Test - void post_commit() { - var logger = new ArrayList(); - - DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger)); - DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger)); - - try (var ac = DataHubInner.setupGlobals()) { - suppressWarnings_unused(ac); - var hub = new DataHubInner(); - - hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger)); - hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger)); - - hub.begin(); - - var conn1 = hub.getDataConn("foo", AsyncDataConn.class); - assertThat(conn1).isNotNull(); - - var conn2 = hub.getDataConn("bar", SyncDataConn.class); - assertThat(conn2).isNotNull(); - - var conn3 = hub.getDataConn("baz", AsyncDataConn.class); - assertThat(conn3).isNotNull(); - - var conn4 = hub.getDataConn("qux", SyncDataConn.class); - assertThat(conn4).isNotNull(); - - hub.postCommit(); - hub.end(); - - hub.close(); - } catch (Exception e) { - fail(e); - } - - assertThat(logger) - .containsExactly( - "SyncDataSrc 2 setupped", - "AsyncDataSrc 1 setupped", - "SyncDataSrc 4 setupped", - "AsyncDataSrc 3 setupped", - "AsyncDataSrc 1 created DataConn", - "SyncDataSrc 2 created DataConn", - "AsyncDataSrc 3 created DataConn", - "SyncDataSrc 4 created DataConn", "AsyncDataConn 1 post committed", "SyncDataConn 2 post committed", "AsyncDataConn 3 post committed", "SyncDataConn 4 post committed", + "AsyncDataConn 1 forced back", + "SyncDataConn 2 forced back", + "AsyncDataConn 3 forced back", + "SyncDataConn 4 forced back", "SyncDataConn 4 closed", "AsyncDataConn 3 closed", "SyncDataConn 2 closed",