Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 16 additions & 73 deletions src/main/java/com/github/sttk/sabi/DataHub.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 <D> 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 <D> void run(Logic<D> 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 <D> 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 <D> void txn(Logic<D> 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.
Expand All @@ -218,4 +145,20 @@ public <C extends DataConn> C getDataConn(String name, Class<C> 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();
}
}
76 changes: 76 additions & 0 deletions src/main/java/com/github/sttk/sabi/Sabi.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
* }</code></pre>
*/
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() {}

/**
Expand Down Expand Up @@ -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 <D> 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 <D> void run(Logic<D> 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 <D> 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 <D> void txn(Logic<D> 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();
}
}
}
20 changes: 9 additions & 11 deletions src/main/java/com/github/sttk/sabi/internal/DataHubInner.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
60 changes: 30 additions & 30 deletions src/test/java/com/github/sttk/sabi/internal/DataAccTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 -> {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 -> {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down
Loading
Loading