From 91ba5f901192d34ea5f6ab108254902a13879b07 Mon Sep 17 00:00:00 2001 From: sttk Date: Sat, 23 Sep 2023 10:55:19 +0900 Subject: [PATCH] update!: changed the whole of APIs --- .gitignore | 1 + .../java/com/github/sttk/sabi/AsyncGroup.java | 40 + src/main/java/com/github/sttk/sabi/Dax.java | 30 + .../java/com/github/sttk/sabi/DaxBase.java | 417 +++++++ .../java/com/github/sttk/sabi/DaxConn.java | 67 ++ .../java/com/github/sttk/sabi/DaxSrc.java | 46 + src/main/java/com/github/sttk/sabi/Logic.java | 31 + .../java/{ => com/github/sttk}/sabi/Para.java | 10 +- .../java/com/github/sttk/sabi/Runner.java | 23 + src/main/java/com/github/sttk/sabi/Sabi.java | 146 +++ .../java/{ => com/github/sttk}/sabi/Seq.java | 16 +- .../sttk/sabi/async/AsyncGroupAsync.java | 96 ++ .../sttk/sabi/async/AsyncGroupSync.java | 47 + .../github/sttk/sabi/errs}/Err.java | 192 ++-- .../com/github/sttk/sabi/errs/ErrHandler.java | 23 + .../com/github/sttk/sabi/errs/ErrOcc.java | 68 ++ .../sttk/sabi/errs}/notify/ErrNotifier.java | 65 +- .../github/sttk/sabi/errs/package-info.java | 6 + src/main/java/module-info.java | 7 +- src/main/java/sabi/Dax.java | 23 - src/main/java/sabi/DaxBase.java | 307 ----- src/main/java/sabi/DaxConn.java | 31 - src/main/java/sabi/DaxSrc.java | 38 - src/main/java/sabi/ErrHandler.java | 23 - src/main/java/sabi/ErrOccasion.java | 76 -- src/main/java/sabi/Logic.java | 28 - src/main/java/sabi/Runner.java | 27 - src/main/java/sabi/Txn.java | 75 -- src/main/java/sabi/package-info.java | 9 - .../com/github/sttk/sabi/DaxBaseTest.java | 1016 +++++++++++++++++ .../{ => com/github/sttk}/sabi/ParaTest.java | 5 +- .../java/com/github/sttk/sabi/SabiTest.java | 390 +++++++ .../{ => com/github/sttk}/sabi/SeqTest.java | 5 +- .../sttk/sabi/async/AsyncGroupAsyncTest.java | 134 +++ .../sttk/sabi/async/AsyncGroupSyncTest.java | 76 ++ .../github/sttk/sabi/errs/ErrHandlerTest.java | 79 ++ .../com/github/sttk/sabi/errs/ErrTest.java | 109 ++ .../sabi/errs}/notify/ErrNotifierTest.java | 146 ++- src/test/java/sabi/DaxAuxForTest.java | 54 - src/test/java/sabi/DaxDummyForTest.java | 366 ------ src/test/java/sabi/DaxTest.java | 778 ------------- src/test/java/sabi/ErrHandlerTest.java | 59 - src/test/java/sabi/ErrTest.java | 160 --- src/test/java/sabi/TxnTest.java | 78 -- 44 files changed, 3089 insertions(+), 2334 deletions(-) create mode 100644 src/main/java/com/github/sttk/sabi/AsyncGroup.java create mode 100644 src/main/java/com/github/sttk/sabi/Dax.java create mode 100644 src/main/java/com/github/sttk/sabi/DaxBase.java create mode 100644 src/main/java/com/github/sttk/sabi/DaxConn.java create mode 100644 src/main/java/com/github/sttk/sabi/DaxSrc.java create mode 100644 src/main/java/com/github/sttk/sabi/Logic.java rename src/main/java/{ => com/github/sttk}/sabi/Para.java (93%) create mode 100644 src/main/java/com/github/sttk/sabi/Runner.java create mode 100644 src/main/java/com/github/sttk/sabi/Sabi.java rename src/main/java/{ => com/github/sttk}/sabi/Seq.java (67%) create mode 100644 src/main/java/com/github/sttk/sabi/async/AsyncGroupAsync.java create mode 100644 src/main/java/com/github/sttk/sabi/async/AsyncGroupSync.java rename src/main/java/{sabi => com/github/sttk/sabi/errs}/Err.java (57%) create mode 100644 src/main/java/com/github/sttk/sabi/errs/ErrHandler.java create mode 100644 src/main/java/com/github/sttk/sabi/errs/ErrOcc.java rename src/main/java/{sabi => com/github/sttk/sabi/errs}/notify/ErrNotifier.java (52%) create mode 100644 src/main/java/com/github/sttk/sabi/errs/package-info.java delete mode 100644 src/main/java/sabi/Dax.java delete mode 100644 src/main/java/sabi/DaxBase.java delete mode 100644 src/main/java/sabi/DaxConn.java delete mode 100644 src/main/java/sabi/DaxSrc.java delete mode 100644 src/main/java/sabi/ErrHandler.java delete mode 100644 src/main/java/sabi/ErrOccasion.java delete mode 100644 src/main/java/sabi/Logic.java delete mode 100644 src/main/java/sabi/Runner.java delete mode 100644 src/main/java/sabi/Txn.java delete mode 100644 src/main/java/sabi/package-info.java create mode 100644 src/test/java/com/github/sttk/sabi/DaxBaseTest.java rename src/test/java/{ => com/github/sttk}/sabi/ParaTest.java (96%) create mode 100644 src/test/java/com/github/sttk/sabi/SabiTest.java rename src/test/java/{ => com/github/sttk}/sabi/SeqTest.java (94%) create mode 100644 src/test/java/com/github/sttk/sabi/async/AsyncGroupAsyncTest.java create mode 100644 src/test/java/com/github/sttk/sabi/async/AsyncGroupSyncTest.java create mode 100644 src/test/java/com/github/sttk/sabi/errs/ErrHandlerTest.java create mode 100644 src/test/java/com/github/sttk/sabi/errs/ErrTest.java rename src/test/java/{sabi => com/github/sttk/sabi/errs}/notify/ErrNotifierTest.java (57%) delete mode 100644 src/test/java/sabi/DaxAuxForTest.java delete mode 100644 src/test/java/sabi/DaxDummyForTest.java delete mode 100644 src/test/java/sabi/DaxTest.java delete mode 100644 src/test/java/sabi/ErrHandlerTest.java delete mode 100644 src/test/java/sabi/ErrTest.java delete mode 100644 src/test/java/sabi/TxnTest.java diff --git a/.gitignore b/.gitignore index c6f197c..45e5f9f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ hs_err_pid* # Directories target/ +test-results-native/ # OS generating files .DS_Store diff --git a/src/main/java/com/github/sttk/sabi/AsyncGroup.java b/src/main/java/com/github/sttk/sabi/AsyncGroup.java new file mode 100644 index 0000000..1fa8ac0 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/AsyncGroup.java @@ -0,0 +1,40 @@ +/* + * AsyncGroup class. + * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +import java.util.Map; +import java.util.HashMap; + +/** + * {@code AsyncGroup} is the inferface to execute added {@link Runner}(s) + * asynchronously. + *

+ * The method Add is to add target {@link Runner}(s). + * This interface is used as an argument of {@link DaxSrc#setup}, {@link + * DaxConn#commit}, and {@link DaxConn#rollback}. + */ +public interface AsyncGroup { + + /** + * {@code RunnerFailed} is an error reason which indicates that a {@link + * Runner} failed. + */ + record RunnerFailed() {} + + /** + * {@code RunnerInterrupted} is an error reason which indicates that a {@link + * Runner}'s thread is interrupted. + */ + record RunnerInterrupted() {} + + /** + * Adds a runner to be run asynchronously. + * + * @param runner A {@link Runner} object. + */ + void add(Runner runner); +} diff --git a/src/main/java/com/github/sttk/sabi/Dax.java b/src/main/java/com/github/sttk/sabi/Dax.java new file mode 100644 index 0000000..bd4cddc --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/Dax.java @@ -0,0 +1,30 @@ +/* + * Dax class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +/** + * Dax is the interface for a set of data access methods. + * + * This interface is inherited by Dax implementations for data stores, and + * each Dax implementation defines data access methods to each data store. + * In data access methods, {@link DaxConn} instances conected to data stores + * can be obtained with {@link #getDaxConn} method. + */ +public interface Dax { + + /** + * Gets a DaxConn instance associated with the argument name. + * The name is same as what was registered with DaxSrc using {@link + * Sabi#uses} method. + * + * @param The type of a {@link DaxConn}. + * @param name A name of a {@link DaxConn}. + * @return A {@link DaxConn} instance. + * @throws Err If getting a {@link DaxConn} fails. + */ + C getDaxConn(String name) throws Err; +} diff --git a/src/main/java/com/github/sttk/sabi/DaxBase.java b/src/main/java/com/github/sttk/sabi/DaxBase.java new file mode 100644 index 0000000..d559fd7 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/DaxBase.java @@ -0,0 +1,417 @@ +/* + * DaxBase class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.AsyncGroup; +import com.github.sttk.sabi.async.AsyncGroupAsync; +import com.github.sttk.sabi.async.AsyncGroupSync; + +/** + * DaxBase is the class that defines the methods to manage {@link DaxSrc}(s). + * And this class defines private methods to process a transaction. + */ +public class DaxBase implements Dax, AutoCloseable { + /** + * FailToSetupGlobalDaxSrcs is the error reason which indicates that some + * DaxSrc(s) failed to set up. + * + * @param errors The map holding keys that are the registered names of + * {@link DaxSrc}(s) failed, and values that are {@link Err}(s) having + * their error reasons. + */ + public record FailToSetupGlobalDaxSrcs(Map errors) {}; + + /** + * FailToSetupLocalDaxSrc is the error reason which indicates a local + * {@link DaxSrc} failed to set up. + * + * @param name Tthe registered name of the {@link DaxSrc} failed. + */ + public record FailToSetupLocalDaxSrc(String name) {}; + + /** + * DaxSrcIsNotFound is the error reason which indicates that a specified + * {@link DaxSrc} is not found. + * + * @param name The registered name of the {@link DaxSrc} not found. + */ + public record DaxSrcIsNotFound(String name) {} + + /** + * FailToCreateDaxConn is the error reason which indicates that it is failed + * to create a new connection to a data store. + * + * @param name The registered name of the {@link DaxSrc} failed to create a + * {@link DaxConn}. + */ + public record FailToCreateDaxConn(String name) {} + + /** + * FailToCommitDaxConn is the error reason which indicates that some + * connections failed to commit. + * + * @param errors The map holding keys that are the names of + * {@link DaxConn}(s) failed, and values that are {@link Err}(s) having + * their error reasons. + */ + public record FailToCommitDaxConn(Map errors) {} + + /** + * CreatedDaxConnIsNull is the error reason which indicates that a + * {@link DaxSrc} created a {@link DaxConn} interface but it is null. + * + * @param name The name of the {@link DaxSrc} that try to create a + * {@link DaxConn}. + */ + public record CreatedDaxConnIsNull(String name) {} + + /** + * FailToRunLogic is the error reason which indicates that a logic failed + * to run. + * + * @param logic The logic class name failed. + */ + public record FailToRunLogic(String logic) {} + + /** The flag to prevent further registration of global {@link DaxSrc}(s). */ + private static boolean isGlobalDaxSrcsFixed; + + /** The map for registering global DaxSrc instances. */ + private static final Map globalDaxSrcMap; + + static { + globalDaxSrcMap = new LinkedHashMap<>(); + } + + /** + * Registers to enable data accesses to data store associated with the + * argument DaxSrc in all Dax instances. + */ + static void addGlobalDaxSrc(String name, DaxSrc ds) { + if (isGlobalDaxSrcsFixed) { + return; + } + globalDaxSrcMap.putIfAbsent(name, ds); + } + + /** + * Makes all globally registered {@link DaxSrc}(s) usable. + * This method forbids adding more global {@link DaxSrc}(s), and call each + * {@link DaxSrc#setup} method of all registered {@link DaxSrc}(s). + * + * If one of {@link DaxSrc}(s) fails to execute synchronous {@link + * DaxSrc#setup}, this method stops other setting up and throws an {@link + * Err} containing the error reason of that failure. + * + * If one of {@link DaxSrc}(s) fails to execute asynchronous {@link + * DaxSrc#setup}, this function continue to other setting up and throws + * an {@link Err} containing the error reason of that failure and other + * errors if any. + * + * @throws Err If an exception occuers by either of the following reasons: + *

+ */ + static void setupGlobalDaxSrcs() throws Err { + isGlobalDaxSrcsFixed = true; + Err.fixCfg(); + + var ag = new AsyncGroupAsync(); + + for (var ent : globalDaxSrcMap.entrySet()) { + try { + ag.name = ent.getKey(); + ent.getValue().setup(ag); + } catch (Exception e) { + ag.join(); + ag.addErr(ag.name, e); + throw new Err(new FailToSetupGlobalDaxSrcs(ag.makeErrs())); + } + } + + ag.join(); + + if (ag.hasErr()) { + throw new Err(new FailToSetupGlobalDaxSrcs(ag.makeErrs())); + } + } + + /** + * Closes and frees each resource of registered global {@link DaxSrc}(s). + * This method should always be called before an application ends. + */ + static void closeGlobalDaxSrcs() { + for (var ent : globalDaxSrcMap.entrySet()) { + ent.getValue().close(); + } + } + + /** The flag to prevent further registration of local {@link DaxSrc}(s). */ + private boolean isLocalDaxSrcsFixed; + + /** The map for registering local {@link DaxSrc} instances. */ + private Map localDaxSrcMap = new LinkedHashMap<>(); + + /** The map for registering {@link DaxConn} instances. */ + private Map daxConnMap = new LinkedHashMap<>(); + + /** + * The lock to registering {@link DaxConn} exclusively for this + * {@link DaxBase} instance. + */ + private final Lock daxConnLock = new ReentrantLock(); + + /** + * The default constructor. + */ + public DaxBase() { + isGlobalDaxSrcsFixed = true; + Err.fixCfg(); + } + + /** + * Closes and frees all local {@link DaxSrc}(s). + */ + @Override + public void close() { + if (isLocalDaxSrcsFixed) { + return; + } + + for (var ds : localDaxSrcMap.values()) { + ds.close(); + } + localDaxSrcMap.clear(); + } + + /** + * Registers and sets up a local {@link DaxSrc} with an argument name. + * + * @param name The name for the {@link DaxSrc} to be registered. + * @param ds The {@link DaxSrc} instance to be registered. + * @throws Err If an exception occuers by either of the following reasons: + *
    + *
  • {@link com.github.sttk.sabi.DaxBase.FailToSetupLocalDaxSrc} + * If failing to setup some of local {@link DaxSrc}(s).
  • + *
+ */ + public void uses(String name, DaxSrc ds) throws Err { + if (isLocalDaxSrcsFixed) { + return; + } + + var agSync = new AsyncGroupSync(); + + try { + ds.setup(agSync); + } catch (Exception e) { + throw new Err(new FailToSetupLocalDaxSrc(name), e); + } + + if (agSync.getErr() != null) { + throw new Err(new FailToSetupLocalDaxSrc(name), agSync.getErr()); + } + + localDaxSrcMap.putIfAbsent(name, ds); + } + + /** + * Closes and removes a local {@link DaxSrc} specified by the argument name. + * + * @param name The name of the local {@link DaxSrc} to be removed. + */ + public void disuses(String name) { + if (isLocalDaxSrcsFixed) { + return; + } + + var ds = localDaxSrcMap.remove(name); + if (ds != null) { + ds.close(); + } + } + + /** + * Begins a transaction processing. + * This method forbids registration of more local {@link DaxSrc}(s) while + * a transaction processing. + */ + protected void begin() { + isLocalDaxSrcsFixed = true; + } + + /** + * Commits updates in a transaction processing. + * + * @throws Err If an exception occuers by either of the following reasons: + *
    + *
  • {@link com.github.sttk.sabi.DaxBase.FailToCommitDaxConn} + * If failing to commit updates via some {@link DaxConn}(s).
  • + *
+ */ + protected void commit() throws Err { + var ag = new AsyncGroupAsync(); + + for (var ent : daxConnMap.entrySet()) { + ag.name = ent.getKey(); + try { + ent.getValue().commit(ag); + } catch (Exception e) { + ag.join(); + ag.addErr(ent.getKey(), e); + throw new Err(new FailToCommitDaxConn(ag.makeErrs())); + } + } + + ag.join(); + + if (ag.hasErr()) { + throw new Err(new FailToCommitDaxConn(ag.makeErrs())); + } + } + + /** + * Rollbacks all updates in a transaction. + */ + protected void rollback() { + var ag = new AsyncGroupAsync(); + + for (var ent : daxConnMap.entrySet()) { + var conn = ent.getValue(); + if (conn.isCommitted()) { + conn.forceBack(ag); + } else { + conn.rollback(ag); + } + } + + ag.join(); + } + + /** + * Ends a transaction. + * This method closes all {@link DaxConn} and removes them from this + * {@link DaxBase}.. + */ + protected void end() { + for (var conn : daxConnMap.values()) { + conn.close(); + } + daxConnMap.clear(); + + isLocalDaxSrcsFixed = false; + } + + private class ErrWrapper extends RuntimeException { + Err err; + ErrWrapper(Err err) { + this.err = err; + } + } + + /** + * {@inheritDoc} + */ + public C getDaxConn(String name) throws Err { + var conn = daxConnMap.get(name); + if (conn != null) { + @SuppressWarnings("unchecked") + final C c = (C) conn; + return c; + } + + daxConnLock.lock(); + + try { + @SuppressWarnings("unchecked") + final C c = (C) daxConnMap.computeIfAbsent(name, key -> { + var ds = localDaxSrcMap.get(key); + if (ds == null) { + ds = globalDaxSrcMap.get(key); + } + if (ds == null) { + var err = new Err(new DaxSrcIsNotFound(key)); + throw new ErrWrapper(err); + } + + DaxConn dc = null; + try { + dc = ds.createDaxConn(); + } catch (Exception e) { + var err = new Err(new FailToCreateDaxConn(key), e); + throw new ErrWrapper(err); + } + if (dc == null) { + var err = new Err(new CreatedDaxConnIsNull(key)); + throw new ErrWrapper(err); + } + return dc; + }); + + return c; + + } catch (ErrWrapper e) { + throw e.err; + + } finally { + daxConnLock.unlock(); + } + } + + /** + * Executes logics in a transaction. + * + * First, this method casts the argument {@link DaxBase} to the type + * specified as a logic's argument. + * Next, this method begins the transaction, and executes the argument + * logics. + * Then, if no error occurs, this method commits all updates in the + * transaction, otherwise rollbaks them. + * If there are commit errors after some {@link DaxConn}(s) are committed, + * or there are {@link DaxConn}(s) which don't have rollback mechanism, + * this method executes {@link DaxConn#forceBack} methods of these + * {@link DaxConn}(s). + * And after that, this method ends the transaction. + * + * During a transaction, it is denied to add or remove any local + * {@link DaxSrc}(s). + */ + @SafeVarargs + public final void txn(Logic ...logics) throws Err { + @SuppressWarnings("unchecked") + final D dax = (D) this; + + begin(); + + try { + for (var logic : logics) { + try { + logic.run(dax); + } catch (Err e) { + throw e; + } catch (Exception e) { + throw new Err(new FailToRunLogic(logic.getClass().getName()), e); + } + } + + commit(); + + } catch (Err | RuntimeException | Error e) { + rollback(); + throw e; + + } finally { + end(); + } + } +} diff --git a/src/main/java/com/github/sttk/sabi/DaxConn.java b/src/main/java/com/github/sttk/sabi/DaxConn.java new file mode 100644 index 0000000..1de8f51 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/DaxConn.java @@ -0,0 +1,67 @@ +/* + * DaxConn class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +/** + * DaxConn is the interface that represents a connection to a data store. + * This interface declares methods: {@link #commit}, {@link rollback} and + * {@link #close} to + * work in a transaction + * process. + * + * {@link #commit} is the method for comming updates in a transaction. + * {@link #rollback} is the method for rollback updates in a transaction. + * If commiting and rollbacking procedures are asynchronous, the argument + * {@link AsyncGroup}(s) are used to process them. + * {@link #close} is the method to close this connection. + */ +public interface DaxConn { + + /** + * Commits updates to a target data store in a transaction. + * If the commit procedure is asynchronous, it is processed with an argument + * {@link AsyncGroup} object. + * + * @param ag An {@link AsyncGroup} object which is used if this commit + * procedure is asynchronous. + * @throws Err If commiting updates fails. + */ + void commit(AsyncGroup ag) throws Err; + + /** + * Checks whether updates are already committed. + * + * @return true if committed, or no rollback mechanism. + */ + default boolean isCommitted() { + return true; + } + + /** + * Rollbacks updates to a target data store in a transaction. + * If the rollback procedure is asynchronous, it is processed with an + * argument {@link AsyncGroup} object. + * + * @param ag An {@link AsyncGroup} object which is used if this rollback + * procedure is asynchronous. + */ + default void rollback(AsyncGroup ag) {} + + /** + * Reverts updates forcely even if updates are already commited or this + * connection does not have rollback mechanism. + * + * @param ag An {@link AsyncGroup} object which is used if this rollback + * procedure is asynchronous. + */ + void forceBack(AsyncGroup ag); + + /** + * Closes this connection. + */ + void close(); +} diff --git a/src/main/java/com/github/sttk/sabi/DaxSrc.java b/src/main/java/com/github/sttk/sabi/DaxSrc.java new file mode 100644 index 0000000..0023a18 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/DaxSrc.java @@ -0,0 +1,46 @@ +/* + * DaxSrc class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +/** + * DaxSrc is the interface that represents a data source like database, etc., + * and creates a {@link DaxConn} which is a connection to the data source. + * This interface declares three methods: {@link #setup}, {@link #close}, and + * {@link #createDaxConn}. + * + * {@link #setup} is the method to connect to a data store and to prepare to + * create {@link DaxConn} objects which represents a connection to access data + * in the data store. + * If the set up procedure is asynchronous, the {@link #setup} method is + * implemented so as to use {@link AsyncGroup}. + * {@link #close} is the method to disconnect to a data store. + * {@link #createDaxConn} is the method to create a {@link DaxConn} object. + */ +public interface DaxSrc { + /** + * Sets up this data source. + * + * @param ag An {@link AsyncGroup} object which is used if a setting up + * process is asynchronous. + * @throws Err If setting up pprocess is synchronous and fails. + */ + void setup(AsyncGroup ag) throws Err; + + /** + * Closes this data source. + */ + void close(); + + /** + * Creates a {@link DaxConn} object which represents a connection to this + * data source. + * + * @return A {@link DaxConn} object. + * @throws Err If creating a {@link DaxConn} fails. + */ + DaxConn createDaxConn() throws Err; +} diff --git a/src/main/java/com/github/sttk/sabi/Logic.java b/src/main/java/com/github/sttk/sabi/Logic.java new file mode 100644 index 0000000..c4c1dfd --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/Logic.java @@ -0,0 +1,31 @@ +/* + * Logic class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +/** + * Logic is the functional interface that represents a logical procedure. + * The {@link #run} method of the class inheriting this interface implements + * only logic processing. + * Data access processing is described only by calling methods of the argument + * dax, and the details are implemented elsewhere. + * + * @param dax + */ +@FunctionalInterface +public interface Logic { + + /** + * Runs the logical procedure represented by this class. + * + * This method is the entry point of the whole of this logical prcedure. + * + * @param dax A Dax instance providing data access methods for this logical + * procedure. + * @throws Err If an error occurs within this logic processing. + */ + void run(D dax) throws Err; +} diff --git a/src/main/java/sabi/Para.java b/src/main/java/com/github/sttk/sabi/Para.java similarity index 93% rename from src/main/java/sabi/Para.java rename to src/main/java/com/github/sttk/sabi/Para.java index 243efe6..0483eb8 100644 --- a/src/main/java/sabi/Para.java +++ b/src/main/java/com/github/sttk/sabi/Para.java @@ -2,7 +2,9 @@ * Para class. * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. */ -package sabi; +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; import java.util.Map; import java.util.ArrayList; @@ -15,7 +17,7 @@ /** * Para is a class to runs {@link Runner}(s) in parallel. */ -public class Para { +public class Para implements Runner { /** The array of {@link Runner}(s). */ private final Runner[] runners; @@ -49,6 +51,7 @@ public Para(Runner ...runners) { * * @throws Err If it is failed to run some of the runners. */ + @Override public void run() throws Err { Para.run(runners); } @@ -60,7 +63,8 @@ public void run() throws Err { * @throws Err If it is failed to run some of the runners. */ public static void run(final Runner... runners) throws Err { - final var executors = Executors.newFixedThreadPool(runners.length); + final var factory = Thread.ofVirtual().factory(); + final var executors = Executors.newFixedThreadPool(runners.length, factory); final var futures = new ArrayList(runners.length); final var errors = new HashMap(); try { diff --git a/src/main/java/com/github/sttk/sabi/Runner.java b/src/main/java/com/github/sttk/sabi/Runner.java new file mode 100644 index 0000000..74b377f --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/Runner.java @@ -0,0 +1,23 @@ +/* + * Runner class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +/** + * Runner is the interface that runs any procedure.. + */ +@FunctionalInterface +public interface Runner { + + /** + * Runs the procedure that this instance represents. + * This method takes no argument and returns nothing. + * And this method throws an {@link Err} exception if this method failed. + * + * @throws Err If this method failed. + */ + void run() throws Err; +} diff --git a/src/main/java/com/github/sttk/sabi/Sabi.java b/src/main/java/com/github/sttk/sabi/Sabi.java new file mode 100644 index 0000000..52a25bb --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/Sabi.java @@ -0,0 +1,146 @@ +/* + * Sabi class. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi; + +import com.github.sttk.sabi.errs.Err; + +/** + * {@code Sabi} is the class that provies the static methods related to the + * global fanctionalities of sabi framework. + *

+ * This class declares {@link #uses uses} method to register a {@link DaxSrc} + * object used globally with its name. + * And this class also declares {@link #setup setup}, {@link #close close}, + * and {@link #startApp startApp} methods. + * {@link #setup setup} is the static method to setup all global registered + * {@link DaxSrc} objects, and {@link #close close} is the static method to + * free each resource of global {@link DaxSrc} objects. + * {@link #startApp startApp} is the method that executes {@link #setup setup} + * and return {@link AutoCloseable} object which executes the {@link #close + * close} method in its {@link AutoCloseable#close} method. + *

+ * The usages of these static methods is as follows: + * + *

   public class Application {
+ *       static {
+ *           Sabi.uses("foo", new FooDaxSrc());
+ *           Sabi.uses("bar", new BarDaxSrc());
+ *       }
+ *       public static void main(String ...args) {
+ *           int exitCode = 0;
+ *           try {
+ *               Sabi.setup();
+ *               ...
+ *           } catch (Exception e) {
+ *               exitCode = 1;
+ *           } finally {
+ *               Sabi.close();
+ *           }
+ *           System.exit(exitCode);
+ *       }
+ *   }
+ * + * Or, + * + *
   public class Application {
+ *       static {
+ *           Sabi.uses("foo", new FooDaxSrc());
+ *           Sabi.uses("bar", new BarDaxSrc());
+ *       }
+ *       public static void main(String ...args) {
+ *           try (var ac = Sabi.startApp()) {
+ *               ...
+ *           } catch (Exception e) {
+ *               System.exit(1);
+ *           }
+ *       }
+ *   }
+ */ +public final class Sabi { + + /** + * The default constructor. + */ + private Sabi() {} + + /** + * Registers a global {@link DaxSrc} object with its name to enable to use + * {@link DaxConn} created by the argument {@link DaxSrc} in all + * transactions. + *

+ * If a {@link DaxSrc} is tried to register with a name already registered, + * it is ignored and a {@link DaxSrc} registered with the same name first is + * used. + * And this method ignore adding {@link DaxSrc}(s) after {@link #setup setup} + * or beginning of {@link DaxBase#txn}. + * + * @param name The name for the argument {@link DaxSrc} object. + * @param ds A {@link DaxSrc} object. + */ + public static void uses(String name, DaxSrc ds) { + DaxBase.addGlobalDaxSrc(name, ds); + } + + /** + * Makes the globally registered {@link DaxSrc} object usable. + *

+ * This method forbids adding more global {@link DaxSrc}, and called each + * {@link DaxSrc#setup setup} method of all registered {@link DaxSrc} + * objects. + * If one of global {@link DaxSrc} objects fails to execute synchronous + * {@link DaxSrc#setup setup}, this function stops other setting up and + * returns an {@link Err} containing the error reason of that failure. + *

+ * If one of global {@link DaxSrc} objects fails to execute asynchronous + * {@link DaxSrc#setup setup}, this function continue to other setting up + * and returns an {@link Err} containing the error reason of that failure + * and other errors if any. + * + * @throws Err If one of global {@link DaxSrc} objects. + */ + public static void setup() throws Err { + DaxBase.setupGlobalDaxSrcs(); + } + + /** + * Closes and frees each resource of registered global {@link DaxSrc} + * objects. + */ + public static void close() { + DaxBase.closeGlobalDaxSrcs(); + } + + /** + * Executes {@link #setup setup} method, and returns an {@link AutoCloseable} + * object of which {@link AutoCloseable#close close} method executes {@link + * #close close} method of this class in it. + * + * @return An {@link AutoCloseable} object. + * @throws Err If one of global {@link DaxSrc} objects. + */ + public static AutoCloseable startApp() throws Err { + try { + DaxBase.setupGlobalDaxSrcs(); + } catch (Err | RuntimeException | Error e) { + DaxBase.closeGlobalDaxSrcs(); + throw e; + } + + return new Closer(); + } +} + +/** + * Closer is the private class. + */ +class Closer implements AutoCloseable { + /** + * {@inheritDoc} + */ + @Override + public void close() { + DaxBase.closeGlobalDaxSrcs(); + } +} diff --git a/src/main/java/sabi/Seq.java b/src/main/java/com/github/sttk/sabi/Seq.java similarity index 67% rename from src/main/java/sabi/Seq.java rename to src/main/java/com/github/sttk/sabi/Seq.java index 2cde7f5..232f3ae 100644 --- a/src/main/java/sabi/Seq.java +++ b/src/main/java/com/github/sttk/sabi/Seq.java @@ -1,23 +1,17 @@ /* * Seq class. - * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. + * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. */ -package sabi; +package com.github.sttk.sabi; -import java.util.Map; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ExecutionException; +import com.github.sttk.sabi.errs.Err; /** - * Seq is a class to runs {@link Runner}(s) sequencially. + * Seq is a class to run {@link Runner}(s) sequencially. */ public class Seq implements Runner { - /** The array of {@link Runner}(s). */ + /* The array of {@link Runner}(s). */ private final Runner[] runners; /** diff --git a/src/main/java/com/github/sttk/sabi/async/AsyncGroupAsync.java b/src/main/java/com/github/sttk/sabi/async/AsyncGroupAsync.java new file mode 100644 index 0000000..21bcce7 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/async/AsyncGroupAsync.java @@ -0,0 +1,96 @@ +/* + * AsyncGroupAsync class. + * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi.async; + +import com.github.sttk.sabi.AsyncGroup; +import com.github.sttk.sabi.Runner; +import com.github.sttk.sabi.errs.Err; + +import java.util.Map; +import java.util.HashMap; + +/** + * AsyncGroupAsync is the class to run added {@link Runner}(s) on {@link + * AsyncGroup} API asynchronously. + * + * @param The type of a name of a runner or its error. + */ +public class AsyncGroupAsync implements AsyncGroup { + + /** A map that holds {@link Err}(s) by {@link Runner}. */ + private Map errMap = new HashMap<>(); + + /** A map that holds mappings of a name and a thread. */ + private Map vthMap = new HashMap<>(); + + /** A name which will be run. */ + public N name; + + /** + * The default constructor. + */ + public AsyncGroupAsync() {} + + /** + * {@inheritDoc} + */ + @Override + public void add(final Runner runner) { + final var name = this.name; + var vth = Thread.ofVirtual().start(() -> { + try { + runner.run(); + } catch (Err | RuntimeException e) { + addErr(name, e); + } + }); + vthMap.put(name, vth); + } + + /** + * Adds an {@link Exception} by a {@link Runner}. + * + * @param name A name of a {@link Runner}. + * @param exc An {@link Err} by a {@link Runner}. + */ + public synchronized void addErr(N name, Exception exc) { + if (exc instanceof Err) { + errMap.put(name, Err.class.cast(exc)); + } else { + addErr(name, new Err(new RunnerFailed(), exc)); + } + } + + /** + * Checks whether there are errors by {@link Runner}(s). + * + * @return {@code true} if there are errors. + */ + public boolean hasErr() { + return !errMap.isEmpty(); + } + + /** + * Creates a map which holds mappings of a name and an {@link Err}. + * + * @return A map of names and {@link Err}(s). + */ + public Map makeErrs() { + return errMap; + } + + /** + * Waits for completions of {@link Runner}'s threads. + */ + public void join() { + for (var ent : vthMap.entrySet()) { + try { + ent.getValue().join(); + } catch (InterruptedException e) { + addErr(ent.getKey(), new Err(new RunnerInterrupted(), e)); + } + } + } +} diff --git a/src/main/java/com/github/sttk/sabi/async/AsyncGroupSync.java b/src/main/java/com/github/sttk/sabi/async/AsyncGroupSync.java new file mode 100644 index 0000000..bd43876 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/async/AsyncGroupSync.java @@ -0,0 +1,47 @@ +/* + * AsyncGroupSync class. + * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi.async; + +import com.github.sttk.sabi.AsyncGroup; +import com.github.sttk.sabi.Runner; +import com.github.sttk.sabi.errs.Err; + +/** + * AsyncGroupSync is the class to run added {@link Runner}(s) on {@link + * AsyncGroup} API but synchronously. + */ +public class AsyncGroupSync implements AsyncGroup { + + /** An error result when a {@link Runner} object runs. */ + private Err err = null; + + /** + * The default constructor. + */ + public AsyncGroupSync() {} + + /** + * {@inheritDoc} + */ + @Override + public void add(final Runner runner) { + try { + runner.run(); + } catch (Err e) { + this.err = e; + } catch (Exception e) { + this.err = new Err(new RunnerFailed(), e); + } + } + + /** + * Gets a {@link Err} object that this instance holds. + * + * @return A {@link Err} object. + */ + public Err getErr() { + return this.err; + } +} diff --git a/src/main/java/sabi/Err.java b/src/main/java/com/github/sttk/sabi/errs/Err.java similarity index 57% rename from src/main/java/sabi/Err.java rename to src/main/java/com/github/sttk/sabi/errs/Err.java index 7ccda71..c85f259 100644 --- a/src/main/java/sabi/Err.java +++ b/src/main/java/com/github/sttk/sabi/errs/Err.java @@ -2,25 +2,24 @@ * Err class. * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. */ -package sabi; +package com.github.sttk.sabi.errs; -import sabi.notify.ErrNotifier; - -import java.lang.reflect.Field; import java.util.Map; import java.util.HashMap; +import java.lang.reflect.Field; import java.util.StringJoiner; +import com.github.sttk.sabi.errs.notify.ErrNotifier; /** - * Err is an exception class with a reason. + * {@code Err} is the exception class with a reason. *
* This class has a record value which indicates a reason by which this * exception is caused. - * A record as a reason has some fields that helps to know error situation - * where an exception of this class is caused. + * A record as a reason can have some fields that helps to know error situation + * where this exception is caused. *
- * The example code of creating and throwing an exception is as follows: + * The example code of creating and throwing an excepton is as follows: *

{@code
  *   public record FailToDoSomething(String name, int value) {}
  *
@@ -28,7 +27,7 @@
  * }
*/ public final class Err extends Exception { - + /** The serial version UID. */ private static final long serialVersionUID = -4983633951387450536L; @@ -38,12 +37,12 @@ public final class Err extends Exception { /** The stack trace for the location of occurrence. */ private final StackTraceElement trace; - /** The notifier of this instance creation. */ + /** The notifier of {@link Err} instance creations. */ private static final ErrNotifier notifier = new ErrNotifier(); - /** - * A constructor which takes a reason of this exception as an argument. + * A constructor which constructs a new Err instance with a specified reason. + * A reason is a structore type of which name expresses what is a reason. * * @param reason A reason of this exception. */ @@ -53,16 +52,15 @@ public Err(final Record reason) { } this.reason = reason; - var traces = getStackTrace(); - this.trace = traces[0]; + this.trace = getStackTrace()[0]; notifier.notify(this); } - /** - * A constructor which takes a reason and a cause exception of this exception - * as arguments. + * A constructor which constructs a new Err instance with a specified reason + * and an cause. + * A reason is a structore type of which name expresses what is a reason. * * @param reason A reason of this exception. * @param cause A cause exception. @@ -75,15 +73,11 @@ public Err(final Record reason, final Throwable cause) { } this.reason = reason; - var traces = getStackTrace(); - this.trace = traces[0]; - - notifier.notify(this); + this.trace = getStackTrace()[0]; } - /** - * Gets the reason of this exception. + * Gets the reason's {@link Record} instance of this exception. * * @return The reason of this exception. */ @@ -91,68 +85,30 @@ public Record getReason() { return this.reason; } - - /** - * Gets an error situation parameter value by the specified name. - * - * @param name An error situation parameter name. - * @return An error situation parameter value. - */ - public Object get(final String name) { - try { - final Field f = this.reason.getClass().getDeclaredField(name); - f.setAccessible(true); - return f.get(this.reason); - } catch (NoSuchFieldException e) { - final Throwable t = getCause(); - if (t != null && t instanceof Err) { - return Err.class.cast(t).get(name); - } - } catch (Exception e) {} - - return null; - } - - /** - * Gets an error situation parameter value by the specified name. + * Gets the simple name of this reason's class. * - * @param name An enum value for an error situation parameter. - * @return An error situation parameter value. + * @return The simple name of this reason's class. */ - public Object get(final Enum name) { - return get(name.name()); + public String getReasonName() { + return this.reason.getClass().getSimpleName(); } - /** - * gets a map which contains error situation parameters. + * Gets the package full name of this reason's class. * - * @return A map of error situation parameters. + * @return The package full name of this reason's class. */ - public Map getSituation() { - final var map = new HashMap(); - for (Field f : this.reason.getClass().getDeclaredFields()) { - try { - f.setAccessible(true); - map.put(f.getName(), f.get(this.reason)); - } catch (Exception e) {} - } - - final Throwable t = getCause(); - if (t != null && t instanceof Err) { - map.putAll(Err.class.cast(t).getSituation()); - } - - return map; + public String getReasonPackage() { + return this.reason.getClass().getPackageName(); } - /** - * Gets a message represents this exception. + * Gets a message expresses the content of this exception. * - * @return A message. + * @return A error message. */ + @Override public String getMessage() { final var sj = new StringJoiner(", ", "{", "}"); @@ -177,51 +133,91 @@ public String getMessage() { return sj.toString(); } - /** - * Returns the fully qualified name of the class of this error occurrence. + * Gets a field value of the reason object by the specified name. + * If the specified named field is not found in the reason of this Err, + * this method finds a same named field in reasons of cause exception + * hierarchically. * - * @return The fully qualified name of the class of this error occurrence. + * @param name A field name of the reason object in this exception. + * @return A field value of the reason object by the specified name. */ - public String getClassName() { - return trace.getClassName(); - } + public Object get(final String name) { + try { + final Field f = this.reason.getClass().getDeclaredField(name); + f.setAccessible(true); + return f.get(this.reason); + } catch (NoSuchFieldException e) { + final Throwable t = getCause(); + if (t != null && t instanceof Err) { + return Err.class.cast(t).get(name); + } + } catch (Exception e) {} + return null; + } /** - * Returns the name of the method of this error occurrence. + * Gets a field value of the reason object by the specified {@link Enum} + * name. + * If the specified named field is not found in the reason of this Err, + * this method finds a same named field in reasons of cause exception + * hierarchically. * - * @return The name of the method of this error occurrence. + * @param name An {@link Enum} that same with a field name of the reason + * object in this exception. + * @return A field value of the reason object by the specified {@link Enum} + * name. */ - public String getMethodName() { - return trace.getMethodName(); + public Object get(final Enum name) { + return get(name.name()); } + /** + * Gets a map which contains variables that represent error situation. + * + * @return A map of error situation variables. + */ + public Map getSituation() { + final var map = new HashMap(); + for (Field f : this.reason.getClass().getDeclaredFields()) { + try { + f.setAccessible(true); + map.put(f.getName(), f.get(this.reason)); + } catch (Exception e) {} + } + + final Throwable t = getCause(); + if (t != null && t instanceof Err) { + map.putAll(Err.class.cast(t).getSituation()); + } + + return map; + } /** - * Returns the name of the source file of this error occurrence. + * Gets the name of the source file of this error occurance. * - * This method returns null if this information is unavailable. + * This method can return null if this information is unavailable. * - * @return The name of the source file of this error occurrence. + * @return The name of the source file of this error occurence. */ protected String getFileName() { return trace.getFileName(); } - /** - * Returns the name of the source file of this error occurrence. + * Gets the line number in the source file of this error occurence. * - * This method returns a negative number if this information is unavailable. + * This method can return a negative number if this information is + * unavailable. * - * @return The name of the source file of this error occurrence. + * @return The line number in the source file of this error occurence. */ protected int getLineNumber() { return trace.getLineNumber(); } - /** * Adds an {@link ErrHandler} object which is executed synchronously just * after an {@link Err} is created. @@ -232,11 +228,10 @@ protected int getLineNumber() { * * @param handler An {@link ErrHandler} object. */ - public static void addSyncErrHandler(final ErrHandler handler) { - notifier.addSyncErrHandler(handler); + public static void addSyncHandler(final ErrHandler handler) { + notifier.addSyncHandler(handler); } - /** * Adds an {@link ErrHandler} object which is executed asynchronously just * after an {@link Err} is created. @@ -246,18 +241,17 @@ public static void addSyncErrHandler(final ErrHandler handler) { * * @param handler An {@link ErrHandler} object. */ - public static void addAsyncErrHandler(final ErrHandler handler) { - notifier.addAsyncErrHandler(handler); + public static void addAsyncHandler(final ErrHandler handler) { + notifier.addAsyncHandler(handler); } - /** - * Fixes configuration for {@link Err}s. + * Fixes configuration of {@link Err}. * * After calling this method, any more {@link ErrHandler}s cannot be - * registered and notification of {@link Err} creations becomes effective. + * registered, and notification of {@link Err} creations becomes effective. */ - public static void fixErrCfgs() { + public static void fixCfg() { notifier.fix(); } } diff --git a/src/main/java/com/github/sttk/sabi/errs/ErrHandler.java b/src/main/java/com/github/sttk/sabi/errs/ErrHandler.java new file mode 100644 index 0000000..954692c --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/errs/ErrHandler.java @@ -0,0 +1,23 @@ +/* + * ErrHandler class. + * Copyright (C) 2022 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi.errs; + +import java.time.OffsetDateTime; + +/** + * {@code ErrHandler} is a handler of an {@link Err} object creation. + */ +@FunctionalInterface +public interface ErrHandler { + + /** + * Handles an {@link Err} object creation. + * + * @param err An {@link Err} object. + * @param occ An {@link ErrOcc} object which holds the situation parameters + * that indicates when and where an {@link Err} occured. + */ + void handle(Err err, ErrOcc occ); +} diff --git a/src/main/java/com/github/sttk/sabi/errs/ErrOcc.java b/src/main/java/com/github/sttk/sabi/errs/ErrOcc.java new file mode 100644 index 0000000..a122f11 --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/errs/ErrOcc.java @@ -0,0 +1,68 @@ +/* + * ErrOcc class. + * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. + */ +package com.github.sttk.sabi.errs; + +import java.time.OffsetDateTime; + +/** + * {@code ErrOcc} is the class which contains time and position in a source + * file when and where an {@link Err} occured. + */ +public final class ErrOcc { + + /** Time when an {@link Err} occured. */ + private final OffsetDateTime time; + + /** The source file name where an {@link Err} occured. */ + private final String file; + + /** The line number where an {@link Err} occured. */ + private final int line; + + /** + * The constructor which takes an {@link Err} object as an argument. + * + * @param e An {@link Err} object. + */ + public ErrOcc(final Err e) { + this.time = OffsetDateTime.now(); + this.file = e.getFileName(); + this.line = e.getLineNumber(); + } + + /** + * Gets time when an {@link Err} occured. + * + * @return A {@link OffsetDateTime} object. + */ + public OffsetDateTime getTime() { + return time; + } + + /** + * Gets the source file name where an {@link Err} occured. + * + * This source file name can be null if this is unavailable in the stack + * trace element. + * + * @return A source file name. + */ + public String getFile() { + return file; + } + + /** + * Gets the line number where an {@link Err} occured. + * + * This line number can be a negative number if this is unavailable in the + * stack trace element. + * + * @return A line number. + */ + public int getLine() { + return line; + } +} + diff --git a/src/main/java/sabi/notify/ErrNotifier.java b/src/main/java/com/github/sttk/sabi/errs/notify/ErrNotifier.java similarity index 52% rename from src/main/java/sabi/notify/ErrNotifier.java rename to src/main/java/com/github/sttk/sabi/errs/notify/ErrNotifier.java index 1db35f8..ce00c69 100644 --- a/src/main/java/sabi/notify/ErrNotifier.java +++ b/src/main/java/com/github/sttk/sabi/errs/notify/ErrNotifier.java @@ -2,118 +2,113 @@ * ErrNotifier class. * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. */ -package sabi.notify; +package com.github.sttk.sabi.errs.notify; -import sabi.Err; -import sabi.ErrHandler; -import sabi.ErrOccasion; +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.errs.ErrOcc; +import com.github.sttk.sabi.errs.ErrHandler; import java.util.List; import java.util.LinkedList; /** - * This class notifies {@link Err} object creations to {@link ErrHandler}. - * This class manages a list for handlers which is processed synchronously and - * another list for handlers which is processed asynchronously. + * ErrNotifier is the class that notifies {@link Err} creations to {@link + * ErrHandler}(s). + * This class manages a list for handlers that process a {@link Err} + * synchronously and another list for handlers that process it asynchronously. * * The only one instance of this class is created as a static field of {@link * Err} class. */ public final class ErrNotifier { - /** The flag meaning whether this instance is fixed or not. */ + /** The flag that indicates whether this instance is fixed or not. */ private boolean isFixed = false; /** - * The list which holds {@link ErrHandler} objects which is executed + * The list that holds {@link ErrHandler} objects which is executed * synchronously. */ protected final List syncErrHandlers = new LinkedList<>(); - /** - * The list which holds {@link ErrHandler} objects which is executed + * The list that holds {@link ErrHandler} objects which is executed * asynchronously. */ protected final List asyncErrHandlers = new LinkedList<>(); - /** * Constructs an instance of this class with no argument. */ public ErrNotifier() {} - /** * Checks whether this object is fixed or not. + * + * @return [@code true} if this object is fixed. */ public boolean isFixed() { return this.isFixed; } - /** - * Registers an {@link ErrHandler} object which is processed synchronously. - * After calling {@link #fix}, this method registers no more. + * Adds an {@link ErrHandler} object that is processed synchronously. + * After calling {@link #fix}, this method adds no more. * * @param handler An {@link ErrHandler} object. */ - public synchronized void addSyncErrHandler(final ErrHandler handler) { + public void addSyncHandler(final ErrHandler handler) { if (this.isFixed) { return; } this.syncErrHandlers.add(handler); } - /** - * Registers an {@link ErrHandler} object which is processed asynchronously. - * After calling {@link #fix}, this method registers no more. + * Adds an {@link ErrHandler} object that is processed asynchronously. + * After calling {@link #fix}, this method adds no more. * * @param handler An {@link ErrHandler} object. */ - public synchronized void addAsyncErrHandler(final ErrHandler handler) { + public void addAsyncHandler(final ErrHandler handler) { if (this.isFixed) { return; } this.asyncErrHandlers.add(handler); } - /** * Fixes this object. * This method makes it impossible to add more {@link ErrHandler}s to this * object, and possible to notify that an {@link Err} object is created. */ - public synchronized void fix() { + public void fix() { this.isFixed = true; } - /** * Notifies that an {@link Err} object is created. - * However, this method does nothing until this object is fixed. + * However, this method does nothging until this object is fixed. + * + * @param err An {@link Err} object. */ public void notify(final Err err) { if (!this.isFixed) { return; } - final var errOcc = new ErrOccasion(err); + final var occ = new ErrOcc(err); for (var handler : this.syncErrHandlers) { - handler.handle(err, errOcc); + handler.handle(err, occ); } if (!this.asyncErrHandlers.isEmpty()) { - final var handlers = this.asyncErrHandlers; - new Thread(() -> { - for (var handler : handlers) { - try { - handler.handle(err, errOcc); - } catch (Throwable t) {} - } - }).start(); + for (var handler : this.asyncErrHandlers) { + Thread.ofVirtual().start(() -> { + handler.handle(err, occ); + }); + } } } } diff --git a/src/main/java/com/github/sttk/sabi/errs/package-info.java b/src/main/java/com/github/sttk/sabi/errs/package-info.java new file mode 100644 index 0000000..a2e209f --- /dev/null +++ b/src/main/java/com/github/sttk/sabi/errs/package-info.java @@ -0,0 +1,6 @@ +/** + * This package contains modules related to errors for sabi framework. + * + * @version 0.3 + */ +package com.github.sttk.sabi.errs; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 03ece8f..376fc07 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -4,8 +4,9 @@ */ /** - * Defines the APIs of the sabi framework. + * Defines the module of sabi framework. */ -module sabi { - exports sabi; +module com.github.sttk.sabi { + exports com.github.sttk.sabi; + exports com.github.sttk.sabi.errs; } diff --git a/src/main/java/sabi/Dax.java b/src/main/java/sabi/Dax.java deleted file mode 100644 index 2077403..0000000 --- a/src/main/java/sabi/Dax.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Dax class. - * Copyright (C) 2022 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -/** - * Dax is an interface for a set of data accesses, and requires a method: - * {@link #getDaxConn} which gets a connection to an external data access. - */ -public interface Dax { - - /** - * Gets a {@link DaxConn} which is a connection to a data source by specified - * name. - * - * @param name The name of {@link DaxConn} or {@link DaxSrc}. - * @param cls The class object of cast destination. - * @return A {@link DaxConn} object. - * @throws Err If failing to get {@link DaxConn}. - */ - C getDaxConn(String name, Class cls) throws Err; -} diff --git a/src/main/java/sabi/DaxBase.java b/src/main/java/sabi/DaxBase.java deleted file mode 100644 index 0b5a600..0000000 --- a/src/main/java/sabi/DaxBase.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * DaxBase class. - * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -import java.util.Map; -import java.util.HashMap; -import java.util.LinkedHashMap; - -/** - * DaxBase manages multiple {@link DaxSrc} and those {@link DaxConn}, and also - * works as an implementation of 'Dax' interface. - */ -public abstract class DaxBase { - - /** - * An error reason which indicates that some dax sources failed to start up. - * - * @param errors A map of which keys are the registered names of {@link - * DaxSrc}(s) which failed to start up, and of which values are {@link Err} - * having their error reasons. - * - * @param errors A map of {@link DaxSrc} names and {@link Err}(s). - */ - public record FailToStartUpGlobalDaxSrcs(Map errors) {} - - /** - * An error reason which indicates that a specified {@link DaxSrc} instance - * is not found. - * - * @param name A registered name of a {@link DaxSrc} is not found. - */ - public record DaxSrcIsNotFound(String name) {}; - - /** - * An error reason which indicates that it failed to create a new connection - * to a data source. - * - * @param name A registered name of a {@link DaxSrc} which failed to create * a {@link DaxConn}. - */ - public record FailToCreateDaxConn(String name) {}; - - /** - * An error reason which indicates that it is failed to cast type of a - * DaxConn. - * - * @param name A registered name of a {@link DaxSrc} which created the - * target {@link DaxConn}. - * @param fromType The type of the source {@link DaxConn} - * @param toType The type of the destination {@link DaxConn} - */ - public record FailToCastDaxConn(String name, String fromType, String toType) {}; - - /** - * An error reason which indicates that some connections failed to commit. - * - * @param errors A map of which keys are registered names of {@link DaxConn} - * which failed to commit, and of which values are {@link Err} thrown by - * {@link DaxConn#commit} methods. - */ - public record FailToCommitDaxConn(Map errors) {} - - /** - * An error reason which indicates that a {@link DaxConn} of the specified - * name caused an exception. - * - * @param name A name of {@link DaxConn} which caused an exception. - */ - public record CommitExceptionOccurs(String name) {} - - /** The global flag which fixes global {@link DaxSrc} compositions. */ - private static boolean isGlobalDaxSrcsFixed = false; - - /** The global map which composes global {@link DaxSrc} objects. */ - private static final Map globalDaxSrcMap = new LinkedHashMap<>(); - - /** The local flag which fixes local {@link DaxSrc} compositions. */ - private boolean isLocalDaxSrcsFixed = false; - - /** The local map which composes local {@link DaxSrc} objects. */ - private final Map localDaxSrcMap = new LinkedHashMap<>(); - - /** The local map which composes {@link DaxConn} objects. */ - private final Map daxConnMap = new LinkedHashMap<>(); - - /** - * Registers a global {@link DaxSrc} with its name to make enable to use - * {@link DaxSrc} in all transactions. - * - * @param name The name for the argument {@link DaxSrc} and also for a - * {@link DaxConn} created by the argument {@link DaxSrc}. - * This name is used to get a {@link DaxConn} with {@link #getDaxConn} - * method. - * @param ds A {@link DaxSrc} object to be registered globally to enable to - * be used in all transactions. - */ - public static synchronized void addGlobalDaxSrc(final String name, final DaxSrc ds) { - if (!isGlobalDaxSrcsFixed) { - globalDaxSrcMap.put(name, ds); - } - } - - /** - * Forbids adding global dax sources and makes available the registered - * global dax sources by calling {@link DaxSrc#setUp} method. - * If even one {@link DaxSrc} fail to execute its {@link DaxSrc#setUp} - * method, this function executes Free methods of all global {@link - * DaxSrc}(s) and throws an {@link Err} object. - * - * @throws Err If even one {@link DaxSrc} failed to execute {@link - * DaxSrc#setUp} method. - */ - public static synchronized void startUpGlobalDaxSrcs() throws Err { - isGlobalDaxSrcsFixed = true; - - var errors = new HashMap(); - - for (var entry : globalDaxSrcMap.entrySet()) { - var name = entry.getKey(); - var ds = entry.getValue(); - try { - ds.setUp(); - } catch (Err err) { - errors.put(name, err); - } - } - - if (!errors.isEmpty()) { - shutdownGlobalDaxSrcs(); - throw new Err(new FailToStartUpGlobalDaxSrcs(errors)); - } - } - - /** - * Terminates all global dax sources and frees resources of all global dax - * sources. - */ - public static synchronized void shutdownGlobalDaxSrcs() { - for (var ds : globalDaxSrcMap.values()) { - ds.end(); - } - } - - /** - * The default constructor of this class. - */ - public DaxBase() {} - - /** - * Registers a local {@link DaxSrc} with a specified name and sets up it. - * - * @param name The name for the argument {@link DaxSrc} and also for a - * {@link DaxConn} created by the argument {@link DaxSrc}. - * This name is used to get a {@link DaxConn} with {@link #getDaxConn} - * method. - * @param ds A {@link DaxSrc} object to be registered locally to enable to - * be used in only specific transactions. - * @throws Err If the specified {@link DaxSrc} failed to execute {@link - * DaxSrc#setUp} method. - */ - public synchronized void setUpLocalDaxSrc(final String name, final DaxSrc ds) throws Err { - if (!this.isLocalDaxSrcsFixed) { - try { - ds.setUp(); - } catch (Err err) { - throw err; - } - this.localDaxSrcMap.put(name, ds); - } - } - - /** - * Removes a local {@link DaxSrc} of the specified name from this {@link - * DaxBase} and frees the resource of it. - * - * @param name A {@link DaxSrc} name. - */ - public synchronized void freeLocalDaxSrc(final String name) { - if (!this.isLocalDaxSrcsFixed) { - var ds = this.localDaxSrcMap.remove(name); - if (ds != null) { - ds.end(); - } - } - } - - /** - * Removes all local {@link DaxSrc}(s) from this {@link DaxBase} and frees - * the resources of them. - */ - public synchronized void freeAllLocalDaxSrcs() { - if (!this.isLocalDaxSrcsFixed) { - for (var ds : this.localDaxSrcMap.values()) { - ds.end(); - } - this.localDaxSrcMap.clear(); - } - } - - /** - * Gets a {@link DaxConn} which is a connection to a data source by specified - * name. - * If a {@link DaxConn} is found, this method returns it, but not found, - * this method creates a new one with a local or global {@link DaxSrc} with - * same name. - * If there are both local and global {@link DaxSrc} with same name, the - * local {@link DaxSrc} is used. - * - * @param name The name of {@link DaxConn} or {@link DaxSrc}. - * @return A {@link DaxConn} object. - * @throws Err If the following error occured: - *
    - *
  • {@link sabi.DaxBase.DaxSrcIsNotFound} - - * If {@link DaxSrc} with the specified name is not found.
  • - *
- */ - public C getDaxConn(final String name, final Class cls) throws Err { - var conn = _getDaxConn(name); - try { - return cls.cast(conn); - } catch (Exception e) { - var from = conn != null ? conn.getClass().getName() : null; - var to = cls.getName(); - throw new Err(new FailToCastDaxConn(name, from, to), e); - } - } - - private DaxConn _getDaxConn(final String name) throws Err { - var conn = this.daxConnMap.get(name); - if (conn != null) { - return conn; - } - - var ds = this.localDaxSrcMap.get(name); - if (ds == null) { - ds = globalDaxSrcMap.get(name); - } - if (ds == null) { - throw new Err(new DaxSrcIsNotFound(name)); - } - - synchronized (this.daxConnMap) { - conn = this.daxConnMap.get(name); - if (conn != null) { - return conn; - } - - try { - conn = ds.createDaxConn(); - } catch (Err e) { - throw new Err(new FailToCreateDaxConn(name), e); - } - - this.daxConnMap.put(name, conn); - } - - return conn; - } - - void begin() { - this.isLocalDaxSrcsFixed = true; - isGlobalDaxSrcsFixed = true; - } - - void commit() throws Err { - var errors = new HashMap(); - - for (var entry : this.daxConnMap.entrySet()) { - try { - var conn = entry.getValue(); - conn.commit(); - } catch (Err err) { - errors.put(entry.getKey(), err); - break; - } catch (Exception exc) { - var err = new Err(new CommitExceptionOccurs(entry.getKey()), exc); - errors.put(entry.getKey(), err); - break; - } - } - - if (!errors.isEmpty()) { - throw new Err(new FailToCommitDaxConn(errors)); - } - } - - void rollback() { - for (var conn : this.daxConnMap.values()) { - try { - conn.rollback(); - } catch (Throwable t) {} - } - } - - void end() { - for (var conn : this.daxConnMap.values()) { - try { - conn.close(); - } catch (Throwable t) {} - } - - this.daxConnMap.clear(); - - this.isLocalDaxSrcsFixed = false; - } -} diff --git a/src/main/java/sabi/DaxConn.java b/src/main/java/sabi/DaxConn.java deleted file mode 100644 index ff24a3f..0000000 --- a/src/main/java/sabi/DaxConn.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * DaxConn class. - * Copyright (C) 2022 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -/** - * DaxConn is an interface which represents a connection to a data source. - - * The class inheriting this class requires methods: {@link #commit}, - * {@link #rollback} and {@link #close} to work in a transaction process. - */ -public interface DaxConn { - - /** - * Makes all changes since the previous commit/rollback permanent. - * - * @throws Err If this connection failed to commit changes. - */ - void commit() throws Err; - - /** - * Undoes all changes since the previous commit rollback. - */ - void rollback(); - - /** - * Closes and releases this connection. - */ - void close(); -} diff --git a/src/main/java/sabi/DaxSrc.java b/src/main/java/sabi/DaxSrc.java deleted file mode 100644 index 09478f3..0000000 --- a/src/main/java/sabi/DaxSrc.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * DaxSrc claass. - * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -/** - * DaxSrc is an interface which represents a data source like database, etc., - * and creates a {@link DaxConn} to the data source. - * The class inheriting this requires a method: {@link #createDaxConn} to do - * so. - */ -public interface DaxSrc { - - /** - * Creates a {@link DaxConn} object which is a connection to a data source - * which the instance of this class indicates. - * - * @return a {@link DaxConn} object. - * @throws Err If this instance failed to create a {@link DaxConn} object. - */ - DaxConn createDaxConn() throws Err; - - /** - * Makes available this data source. - * - * For example, connecting to the data store, setting up connection pooling, - * and so on. - * - * @throws Err If this instance failed to set up. - */ - void setUp() throws Err; - - /** - * Frees the resources of this data source. - */ - void end(); -} diff --git a/src/main/java/sabi/ErrHandler.java b/src/main/java/sabi/ErrHandler.java deleted file mode 100644 index 642460e..0000000 --- a/src/main/java/sabi/ErrHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * ErrHandler class. - * Copyright (C) 2022 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -import java.time.OffsetDateTime; - -/** - * This class is a handler of an {@link Err} object creation. - */ -@FunctionalInterface -public interface ErrHandler { - - /** - * Handles an {@link Err} object which will be created rigth after. - * - * @param err An {@link Err} object. - * @param occ An {@link ErrOccasion} object which holds when and where - * an Err occured. - */ - void handle(Err err, ErrOccasion occ); -} diff --git a/src/main/java/sabi/ErrOccasion.java b/src/main/java/sabi/ErrOccasion.java deleted file mode 100644 index a1398d0..0000000 --- a/src/main/java/sabi/ErrOccasion.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ErrOccasion class. - * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -import java.time.OffsetDateTime; - -/** - * ErrOccasion is a class which contains time and position in a source file - * when and where an Err occured. - */ -public final class ErrOccasion { - - /** Time when an Err occured. */ - private final OffsetDateTime time; - - /** The source file name where an Err occured. */ - private final String file; - - /** The line number where an Err occured. */ - private final int line; - - /** - * The constructor which takes an Err object. - * - * @param err An Err object. - */ - public ErrOccasion(final Err err) { - this.time = OffsetDateTime.now(); - this.file = err.getFileName(); - this.line = err.getLineNumber(); - } - - /** - * Gets time when an Err occured. - * - * @return A {@link OffsetDateTime} object. - */ - public OffsetDateTime getTime() { - return time; - } - - /** - * Gets a source file name where an Err occured. - * - * This source file name is null if this is unavailable in the stack trace - * element. - * - * @return A source file name. - */ - public String getFile() { - return file; - } - - /** - * Gets a line number where an Err occured. - * - * This line number is a nevative number if this is unavailable in the stack - * trace element. - * - * @return A line number. - */ - public int getLine() { - return line; - } - - /** - * Returns a string representing the content of this object. - * - * @return A string representation of this object. - */ - public String toString() { - return "(" + file + ":" + line + ") " + time; - } -} diff --git a/src/main/java/sabi/Logic.java b/src/main/java/sabi/Logic.java deleted file mode 100644 index 863f41f..0000000 --- a/src/main/java/sabi/Logic.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Logic class. - * Copyright (C) 2022 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -/** - * Logic is a functional interface which runs a logical process. - * - * In this class, only logical codes should be written and data access codes - * for external data sources should not. - * Data access codes should be written in methods associated with a dax - * interface which is an argument of {@link #run} method. - */ -@FunctionalInterface -public interface Logic { - - /** - * Runs the logical process represented by this class. - * - * This method is the entry point of the whole logical process represented by - * this class. - * - * @param dax A data access interface. - * @throws Err If an error occured in an implementation for an argument dax. - */ - void run(final D dax) throws Err; -} diff --git a/src/main/java/sabi/Runner.java b/src/main/java/sabi/Runner.java deleted file mode 100644 index 51733c3..0000000 --- a/src/main/java/sabi/Runner.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Runner class. - * Copyright (C) 2022-2023 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -import java.util.Map; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ExecutionException; - -/** - * Runner is an interface which has {@link #run} method and is runned by - * {@link Seq#run} or {@link Para#run}. - */ -public interface Runner { - - /** - * Runs a process represented by this class. - * - * @throws Err If an exception occurs in this process. - */ - void run() throws Err; -} diff --git a/src/main/java/sabi/Txn.java b/src/main/java/sabi/Txn.java deleted file mode 100644 index 133087c..0000000 --- a/src/main/java/sabi/Txn.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Txn class. - * Copyright (C) 2023 Takayuki Sato. All Rights Reserved. - */ -package sabi; - -/** - * Txn is a class to run a transaction process. - */ -public class Txn implements Runner { - - /** - * The {@link DaxBase} object which holds {@link DaxSrc}(s) and - * {@link DaxConn}(s), and has data access methods used in the logics. - */ - private final DaxBase base; - - /** The logics to be run in this transaction. */ - private final Logic[] logics; - - /** - * The constructor which takes logics to be run in this transaction. - * - * @param base A {@link DaxBase} for a transaction process and data access - * methods. - * @param logics {@link Logic}'s variadic arguments. - */ - @SafeVarargs - public Txn(DaxBase base, Logic ...logics) { - this.base = base; - this.logics = logics; - } - - /** - * Runs the {@link Logic}(s) holding in this object in a transaction. - * - * @throws Err If it is failed to run one of the logics. - */ - @Override - public void run() throws Err { - run(this.base, this.logics); - } - - /** - * Runs the specified {@link Logic}(s) with the {@link DaxBase} in a - * transaction. - * - * @param base A {@link DaxBase} for a transaction process and data access - * methods. - * @param logics {@link Logic}'s variadic arguments. - * @throws Err If it is failed to run one of the logics. - */ - @SafeVarargs - public static void run(DaxBase base, Logic ...logics) throws Err { - try { - base.begin(); - - @SuppressWarnings("unchecked") - final D dax = (D) base; - - for (var logic : logics) { - logic.run(dax); - } - - base.commit(); - - } catch (Err e) { - base.rollback(); - throw e; - - } finally { - base.end(); - } - } -} diff --git a/src/main/java/sabi/package-info.java b/src/main/java/sabi/package-info.java deleted file mode 100644 index 0d568be..0000000 --- a/src/main/java/sabi/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * This package contains modules of Sabi framework. - *
- * Sabi is a small framework to separate logics and data accesses for Java - * applications. - * - * @version 0.3 - */ -package sabi; diff --git a/src/test/java/com/github/sttk/sabi/DaxBaseTest.java b/src/test/java/com/github/sttk/sabi/DaxBaseTest.java new file mode 100644 index 0000000..f9c0a1f --- /dev/null +++ b/src/test/java/com/github/sttk/sabi/DaxBaseTest.java @@ -0,0 +1,1016 @@ +package com.github.sttk.sabi; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.lang.reflect.Field; + +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.DaxBase.*; +import com.github.sttk.sabi.DaxBaseTest.*; +import static com.github.sttk.sabi.DaxBaseTest.*; + +public class DaxBaseTest { + + record FailToSetupFooDaxSrc() {} + record FailToSetupBarDaxSrc() {} + record FailToCreateFooDaxConn() {} + record FailToCreateBarDaxConn() {} + record CreatedFooDaxConnIsNull() {} + record CreatedBarDaxConnIsNull() {} + record FailToCommitFooDaxConn() {} + record FailToCommitBarDaxConn() {} + record FailToCommitQuxDaxConn() {} + + static List logs = new ArrayList<>(); + + static boolean willFailToSetupFooDaxSrc = false; + static boolean willFailToSetupBarDaxSrc = false; + static boolean willFailToCreateFooDaxConn = false; + static boolean willFailToCreateBarDaxConn = false; + static boolean willCreatedFooDaxConnBeNull = false; + static boolean willCreatedBarDaxConnBeNull = false; + static boolean willFailToCommitFooDaxConn = false; + static boolean willFailToCommitBarDaxConn = false; + + static boolean getIsGlobalDaxSrcsFixed() { + final var c = DaxBase.class; + try { + final var f = c.getDeclaredField("isGlobalDaxSrcsFixed"); + f.setAccessible(true); + return f.getBoolean(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static void setIsGlobalDaxSrcsFixed(boolean b) { + final var c = DaxBase.class; + try { + final var f = c.getDeclaredField("isGlobalDaxSrcsFixed"); + f.setAccessible(true); + f.setBoolean(null, b); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static Map getGlobalDaxSrcMap() { + final var c = DaxBase.class; + try { + final Field f = c.getDeclaredField("globalDaxSrcMap"); + f.setAccessible(true); + @SuppressWarnings("unchecked") + final var m = (Map) f.get(null); + return m; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static boolean getIsLocalDaxSrcsFixed(DaxBase base) { + final var c = DaxBase.class; + try { + final var f = c.getDeclaredField("isLocalDaxSrcsFixed"); + f.setAccessible(true); + return f.getBoolean(base); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static void setIsLocalDaxSrcsFixed(DaxBase base, boolean b) { + final var c = DaxBase.class; + try { + final var f = c.getDeclaredField("isLocalDaxSrcsFixed"); + f.setAccessible(true); + f.setBoolean(base, b); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static Map getLocalDaxSrcMap(DaxBase base) { + final var c = DaxBase.class; + try { + final Field f = c.getDeclaredField("localDaxSrcMap"); + f.setAccessible(true); + @SuppressWarnings("unchecked") + final var m = (Map) f.get(base); + return m; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static class FooDaxSrc implements DaxSrc { + @Override + public void setup(AsyncGroup ag) throws Err { + if (willFailToSetupFooDaxSrc) { + throw new Err(new FailToSetupFooDaxSrc()); + } + logs.add("FooDaxSrc#setup"); + } + @Override + public void close() { + logs.add("FooDaxSrc#close"); + } + @Override + public DaxConn createDaxConn() throws Err { + if (willFailToCreateFooDaxConn) { + throw new Err(new FailToCreateFooDaxConn()); + } + if (willCreatedFooDaxConnBeNull) { + return null; + } + logs.add("FooDaxSrc#createDaxConn"); + return new FooDaxConn(); + } + } + + static class FooDaxConn implements DaxConn { + boolean committed = false; + @Override + public void commit(AsyncGroup ag) throws Err { + if (willFailToCommitFooDaxConn) { + throw new Err(new FailToCommitFooDaxConn()); + } + logs.add("FooDaxConn#commit"); + committed = true; + } + @Override + public boolean isCommitted() { + return committed; + } + @Override + public void rollback(AsyncGroup ag) { + logs.add("FooDaxConn#rollback"); + } + @Override + public void forceBack(AsyncGroup ag) { + logs.add("FooDaxConn#forceBack"); + } + @Override + public void close() { + logs.add("FooDaxConn#close"); + } + } + + static class BarDaxSrc implements DaxSrc { + @Override + public void setup(AsyncGroup ag) throws Err { + ag.add(() -> { + if (willFailToSetupBarDaxSrc) { + throw new Err(new FailToSetupBarDaxSrc()); + } + logs.add("BarDaxSrc#setup"); + }); + } + @Override + public void close() { + logs.add("BarDaxSrc#close"); + } + @Override + public DaxConn createDaxConn() throws Err { + if (willFailToCreateBarDaxConn) { + throw new Err(new FailToCreateBarDaxConn()); + } + if (willCreatedBarDaxConnBeNull) { + return null; + } + logs.add("BarDaxSrc#createDaxConn"); + return new BarDaxConn(); + } + } + + static class BarDaxConn implements DaxConn { + boolean committed = false; + @Override + public void commit(AsyncGroup ag) throws Err { + ag.add(() -> { + if (willFailToCommitBarDaxConn) { + throw new Err(new FailToCommitBarDaxConn()); + } + logs.add("BarDaxConn#commit"); + committed = true; + }); + } + @Override + public boolean isCommitted() { + return committed; + } + @Override + public void rollback(AsyncGroup ag) { + ag.add(() -> { + logs.add("BarDaxConn#rollback"); + }); + } + @Override + public void forceBack(AsyncGroup ag) { + ag.add(() -> { + logs.add("BarDaxConn#forceBack"); + }); + } + @Override + public void close() { + logs.add("BarDaxConn#close"); + } + } + + static class QuxDaxSrc implements DaxSrc { + @Override + public void setup(AsyncGroup ag) throws Err { + logs.add("QuxDaxSrc#setup"); + } + @Override + public void close() { + logs.add("QuxDaxSrc#close"); + } + @Override + public DaxConn createDaxConn() throws Err { + logs.add("QuxDaxSrc#createDaxConn"); + return new QuxDaxConn(); + } + } + + static class QuxDaxConn implements DaxConn { + @Override + public void commit(AsyncGroup ag) throws Err { + throw new Err(new FailToCommitQuxDaxConn()); + } + @Override + public void forceBack(AsyncGroup ag) { + logs.add("QuxDaxConn#forceBack"); + } + @Override + public void close() { + logs.add("QuxDaxConn#close"); + } + } + + /// + + @BeforeEach + void reset() { + setIsGlobalDaxSrcsFixed(false); + getGlobalDaxSrcMap().clear(); + + willFailToSetupFooDaxSrc = false; + willFailToSetupBarDaxSrc = false; + willFailToCreateFooDaxConn = false; + willFailToCreateBarDaxConn = false; + willCreatedFooDaxConnBeNull = false; + willCreatedBarDaxConnBeNull = false; + willFailToCommitFooDaxConn = false; + willFailToCommitBarDaxConn = false; + + logs.clear(); + } + + /// + + @Test + public void new_and_close() { + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + + var base = new DaxBase(); + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(getIsLocalDaxSrcsFixed(base)).isFalse(); + assertThat(getLocalDaxSrcMap(base)).hasSize(0); + + base.close(); + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(getIsLocalDaxSrcsFixed(base)).isFalse(); + assertThat(getLocalDaxSrcMap(base)).hasSize(0); + + assertThat(logs).hasSize(0); + } + + @Test + public void uses_ok() { + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(getGlobalDaxSrcMap()).hasSize(0); + + try (var base = new DaxBase()) { + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(getIsLocalDaxSrcsFixed(base)).isFalse(); + assertThat(getLocalDaxSrcMap(base)).hasSize(0); + + base.uses("httpRequest", new FooDaxSrc()); + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(getIsLocalDaxSrcsFixed(base)).isFalse(); + var m = getLocalDaxSrcMap(base); + assertThat(m).hasSize(1); + assertThat(m.get("httpRequest")).isInstanceOf(FooDaxSrc.class); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void uses_failToSetupDaxSrc_sync() { + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(getGlobalDaxSrcMap()).hasSize(0); + + willFailToSetupFooDaxSrc = true; + + try (var base = new DaxBase()) { + base.uses("httpRequest", new FooDaxSrc()); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToSetupLocalDaxSrc r: { + assertThat(r.name()).isEqualTo("httpRequest"); + var e1 = Err.class.cast(e.getCause()); + assertThat(e1.getReason()).isInstanceOf(FailToSetupFooDaxSrc.class); + break; + } + default: { + fail(); + break; + } + } + } + + assertThat(logs).hasSize(0); + } + + @Test + public void uses_failToSetupDaxSrc_async() { + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(getGlobalDaxSrcMap()).hasSize(0); + + willFailToSetupBarDaxSrc = true; + + try (var base = new DaxBase()) { + base.uses("httpRequest", new BarDaxSrc()); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToSetupLocalDaxSrc r: { + assertThat(r.name()).isEqualTo("httpRequest"); + var e1 = Err.class.cast(e.getCause()); + assertThat(e1.getReason()).isInstanceOf(FailToSetupBarDaxSrc.class); + break; + } + default: { + fail(); + break; + } + } + } + + assertThat(logs).hasSize(0); + } + + @Test + public void uses_txnAlreadyBegan() { + try (var base = new DaxBase()) { + base.begin(); + base.uses("httpRequest", new BarDaxSrc()); + base.end(); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(0); + } + + @Test + public void close_txnAlreadyBegan() { + try { + var base = new DaxBase(); + base.uses("httpRequest", new FooDaxSrc()); + base.begin(); + base.close(); + base.end(); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + } + + @Test + public void disuses_ok() { + try { + var base = new DaxBase(); + base.uses("httpRequest", new FooDaxSrc()); + base.disuses("httpRequest"); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void disuses_ignoreIfNoDaxSrc() { + try { + var base = new DaxBase(); + base.uses("httpRequest", new FooDaxSrc()); + base.disuses("xxx"); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + } + + @Test + public void disuses_txnAlreadyBegan() { + try { + var base = new DaxBase(); + base.uses("httpRequest", new FooDaxSrc()); + base.begin(); + base.disuses("httpRequest"); + base.end(); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + } + + @Test + public void txn_ok() { + try (var base = new DaxBase()) { + base.txn((Dax dax) -> { + logs.add("exec logic"); + }); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("exec logic"); + } + + @Test + public void txn_withCustomDax() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn((MyLogicDax dax) -> { + var s = dax.getData(); + dax.setData(s); + }); + } catch (Err e) { + fail(e); + } + + assertThat(logs).hasSize(5); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#createDaxConn"); + assertThat(logs.get(2)).isEqualTo("FooDaxConn#commit"); + assertThat(logs.get(3)).isEqualTo("FooDaxConn#close"); + assertThat(logs.get(4)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_failToCastDaxBase() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + class MyLogic implements Logic { + @Override public void run(MyLogicDax dax) throws Err { + var s = dax.getData(); + dax.setData(s); + } + } + + interface AllLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn(new MyLogic()); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToRunLogic r: { + assertThat(r.logic()).isEqualTo( + "com.github.sttk.sabi.DaxBaseTest$1MyLogic"); + assertThat(e.getCause()).isInstanceOf(ClassCastException.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_failToCastDaxConn() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + class MyLogic implements Logic { + @Override public void run(MyLogicDax dax) throws Err { + var s = dax.getData(); + dax.setData(s); + } + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + BarDaxConn conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn(new MyLogic()); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToRunLogic r: { + assertThat(r.logic()).isEqualTo( + "com.github.sttk.sabi.DaxBaseTest$2MyLogic"); + assertThat(e.getCause()).isInstanceOf(ClassCastException.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(5); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#createDaxConn"); + assertThat(logs.get(2)).isEqualTo("FooDaxConn#rollback"); + assertThat(logs.get(3)).isEqualTo("FooDaxConn#close"); + assertThat(logs.get(4)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_errorFromLogic() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + BarDaxConn conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + record FailToDoSomething() {} + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn((MyLogicDax dax) -> { + throw new Err(new FailToDoSomething()); + }); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToDoSomething r: { + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_failToCreateDaxConn() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + class MyLogic implements Logic { + @Override public void run(MyLogicDax dax) throws Err { + var s = dax.getData(); + dax.setData(s); + } + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + willFailToCreateFooDaxConn = true; + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn(new MyLogic()); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToCreateDaxConn r: { + assertThat(r.name()).isEqualTo("mydata"); + var r2 = Err.class.cast(e.getCause()).getReason(); + assertThat(r2).isInstanceOf(FailToCreateFooDaxConn.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_errorBecauseCreatedDaxConnIsNull() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + class MyLogic implements Logic { + @Override public void run(MyLogicDax dax) throws Err { + var s = dax.getData(); + dax.setData(s); + } + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + willCreatedFooDaxConnBeNull = true; + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn(new MyLogic()); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case CreatedDaxConnIsNull r: { + assertThat(r.name()).isEqualTo("mydata"); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_failBecauseDaxSrcIsNotFound() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + try (var base = new MyDaxBase()) { + base.txn((MyLogicDax dax) -> { + var s = dax.getData(); + dax.setData(s); + }); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case DaxSrcIsNotFound r: { + assertThat(r.name()).isEqualTo("mydata"); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(0); + } + + @Test + public void txn_failToCommitDaxConn_sync() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + willFailToCommitFooDaxConn = true; + + try (var base = new MyDaxBase()) { + base.uses("mydata", new FooDaxSrc()); + base.txn((MyLogicDax dax) -> { + var s = dax.getData(); + dax.setData(s); + }); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToCommitDaxConn r: { + var m = r.errors(); + assertThat(m).hasSize(1); + var r1 = m.get("mydata").getReason(); + assertThat(r1).isInstanceOf(FailToCommitFooDaxConn.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(5); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#createDaxConn"); + assertThat(logs.get(2)).isEqualTo("FooDaxConn#rollback"); + assertThat(logs.get(3)).isEqualTo("FooDaxConn#close"); + assertThat(logs.get(4)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void txn_failToCommitDaxConn_async() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + willFailToCommitBarDaxConn = true; + + try (var base = new MyDaxBase()) { + base.uses("mydata", new BarDaxSrc()); + base.txn((MyLogicDax dax) -> { + var s = dax.getData(); + dax.setData(s); + }); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToCommitDaxConn r: { + var m = r.errors(); + assertThat(m).hasSize(1); + var r1 = m.get("mydata").getReason(); + assertThat(r1).isInstanceOf(FailToCommitBarDaxConn.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(5); + assertThat(logs.get(0)).isEqualTo("BarDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("BarDaxSrc#createDaxConn"); + assertThat(logs.get(2)).isEqualTo("BarDaxConn#rollback"); + assertThat(logs.get(3)).isEqualTo("BarDaxConn#close"); + assertThat(logs.get(4)).isEqualTo("BarDaxSrc#close"); + } + + @Test + public void txn_forceBack() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("foo"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("bar"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + willFailToCommitBarDaxConn = true; + + try (var base = new MyDaxBase()) { + base.uses("foo", new FooDaxSrc()); + base.uses("bar", new BarDaxSrc()); + base.txn((MyLogicDax dax) -> { + var s = dax.getData(); + dax.setData(s); + }); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToCommitDaxConn r: { + var m = r.errors(); + assertThat(m).hasSize(1); + var r1 = m.get("bar").getReason(); + assertThat(r1).isInstanceOf(FailToCommitBarDaxConn.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(11); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setup"); + assertThat(logs.get(2)).isEqualTo("FooDaxSrc#createDaxConn"); + assertThat(logs.get(3)).isEqualTo("BarDaxSrc#createDaxConn"); + assertThat(logs.get(4)).isEqualTo("FooDaxConn#commit"); + assertThat(logs.get(5)).isEqualTo("FooDaxConn#forceBack"); + assertThat(logs.get(6)).isEqualTo("BarDaxConn#rollback"); + assertThat(logs.get(7)).isEqualTo("FooDaxConn#close"); + assertThat(logs.get(8)).isEqualTo("BarDaxConn#close"); + assertThat(logs.get(9)).isEqualTo("FooDaxSrc#close"); + assertThat(logs.get(10)).isEqualTo("BarDaxSrc#close"); + } + + @Test + public void txn_whenDaxConnHasNoRollbackMechanism() { + interface MyLogicDax { + String getData() throws Err; + void setData(String v) throws Err; + } + + interface AllLogicDax extends MyLogicDax {} + + interface MyDax extends Dax, AllLogicDax { + default String getData() throws Err { + var conn = getDaxConn("mydata"); + return "hello"; + } + default void setData(String v) throws Err { + var conn = getDaxConn("mydata"); + } + } + + class MyDaxBase extends DaxBase implements MyDax {} + + try (var base = new MyDaxBase()) { + base.uses("mydata", new QuxDaxSrc()); + base.txn((MyLogicDax dax) -> { + var s = dax.getData(); + dax.setData(s); + }); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToCommitDaxConn r: { + var m = r.errors(); + assertThat(m).hasSize(1); + var r1 = m.get("mydata").getReason(); + assertThat(r1).isInstanceOf(FailToCommitQuxDaxConn.class); + break; + } + default: { + fail(e); + } + } + } + + assertThat(logs).hasSize(5); + assertThat(logs.get(0)).isEqualTo("QuxDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("QuxDaxSrc#createDaxConn"); + assertThat(logs.get(2)).isEqualTo("QuxDaxConn#forceBack"); + assertThat(logs.get(3)).isEqualTo("QuxDaxConn#close"); + assertThat(logs.get(4)).isEqualTo("QuxDaxSrc#close"); + } +} diff --git a/src/test/java/sabi/ParaTest.java b/src/test/java/com/github/sttk/sabi/ParaTest.java similarity index 96% rename from src/test/java/sabi/ParaTest.java rename to src/test/java/com/github/sttk/sabi/ParaTest.java index 3780b46..8d02122 100644 --- a/src/test/java/sabi/ParaTest.java +++ b/src/test/java/com/github/sttk/sabi/ParaTest.java @@ -1,9 +1,11 @@ -package sabi; +package com.github.sttk.sabi; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; +import com.github.sttk.sabi.errs.Err; + import java.util.ArrayList; public class ParaTest { @@ -75,4 +77,3 @@ void should_run_holding_runners_in_parallel() throws Exception { assertThat(logs).containsExactly("2", "1"); } } - diff --git a/src/test/java/com/github/sttk/sabi/SabiTest.java b/src/test/java/com/github/sttk/sabi/SabiTest.java new file mode 100644 index 0000000..8119f7a --- /dev/null +++ b/src/test/java/com/github/sttk/sabi/SabiTest.java @@ -0,0 +1,390 @@ +package com.github.sttk.sabi; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import java.util.Map; +import java.util.List; +import java.util.ArrayList; + +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.DaxBase.*; +import com.github.sttk.sabi.DaxBaseTest.*; +import static com.github.sttk.sabi.DaxBaseTest.*; + +public class SabiTest { + + @BeforeEach + void reset() { + new DaxBaseTest().reset(); + } + + /// + + @Test + public void should_register_global_DaxSrc() { + Sabi.uses("cliargs", new FooDaxSrc()); + + final Map m = getGlobalDaxSrcMap(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + + Sabi.uses("database", new BarDaxSrc()); + + assertThat(m).hasSize(2); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(m.get("database")).isInstanceOf(BarDaxSrc.class); + } + + @Test + public void should_register_global_DaxSrc_but_name_already_exists() { + Sabi.uses("cliargs", new FooDaxSrc()); + + final Map m = getGlobalDaxSrcMap(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + + Sabi.uses("cliargs", new BarDaxSrc()); + + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + } + + @Test + public void should_setup_zero_global_DaxSrc() { + final Map m = getGlobalDaxSrcMap(); + + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(m).hasSize(0); + assertThat(logs).hasSize(0); + + try { + Sabi.setup(); + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(0); + assertThat(logs).hasSize(0); + } + + @Test + public void should_setup_one_global_DaxSrc() { + final Map m = getGlobalDaxSrcMap(); + + Sabi.uses("cliargs", new FooDaxSrc()); + + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(0); + + try { + Sabi.setup(); + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void should_setup_multiple_global_DaxSrc() { + final Map m = getGlobalDaxSrcMap(); + + Sabi.uses("cliargs", new FooDaxSrc()); + Sabi.uses("database", new BarDaxSrc()); + + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(m).hasSize(2); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(m.get("database")).isInstanceOf(BarDaxSrc.class); + assertThat(logs).hasSize(0); + + try { + Sabi.setup(); + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(2); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(m.get("database")).isInstanceOf(BarDaxSrc.class); + assertThat(logs).hasSize(4); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setup"); + assertThat(logs.get(2)).isEqualTo("FooDaxSrc#close"); + assertThat(logs.get(3)).isEqualTo("BarDaxSrc#close"); + } + + @Test + public void should_setup_and_cannot_add_after_setup() { + final Map m = getGlobalDaxSrcMap(); + + Sabi.uses("cliargs", new FooDaxSrc()); + + assertThat(getIsGlobalDaxSrcsFixed()).isFalse(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(0); + + try { + Sabi.setup(); + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + + Sabi.uses("database", new FooDaxSrc()); + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void should_setup_sync_but_error() { + final Map m = getGlobalDaxSrcMap(); + + willFailToSetupFooDaxSrc = true; + + Sabi.uses("cliargs", new FooDaxSrc()); + + try { + Sabi.setup(); + fail(); + } catch (Err e) { + assertThat(e.getReason()).isInstanceOf(FailToSetupGlobalDaxSrcs.class); + switch (e.getReason()) { + case FailToSetupGlobalDaxSrcs r: { + assertThat(r.errors()).hasSize(1); + var e1 = r.errors().get("cliargs"); + assertThat(e1.getReason()).isInstanceOf(FailToSetupFooDaxSrc.class); + break; + } + default: { + fail(); + break; + } + } + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void should_setup_async_but_error() { + final Map m = getGlobalDaxSrcMap(); + + willFailToSetupBarDaxSrc = true; + + Sabi.uses("cliargs", new BarDaxSrc()); + + try { + Sabi.setup(); + fail(); + } catch (Err e) { + assertThat(e.getReason()).isInstanceOf(FailToSetupGlobalDaxSrcs.class); + switch (e.getReason()) { + case FailToSetupGlobalDaxSrcs r: { + assertThat(r.errors()).hasSize(1); + var e1 = r.errors().get("cliargs"); + assertThat(e1.getReason()).isInstanceOf(FailToSetupBarDaxSrc.class); + break; + } + default: { + fail(); + break; + } + } + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("cliargs")).isInstanceOf(BarDaxSrc.class); + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("BarDaxSrc#close"); + } + + @Test + public void should_setup_sync_and_async_but_error() { + final Map m = getGlobalDaxSrcMap(); + + willFailToSetupFooDaxSrc = true; + willFailToSetupBarDaxSrc = true; + + Sabi.uses("cliargs", new BarDaxSrc()); + Sabi.uses("database", new FooDaxSrc()); + + try { + Sabi.setup(); + fail(); + } catch (Err e) { + assertThat(e.getReason()).isInstanceOf(FailToSetupGlobalDaxSrcs.class); + switch (e.getReason()) { + case FailToSetupGlobalDaxSrcs r: { + assertThat(r.errors()).hasSize(2); + var e1 = r.errors().get("cliargs"); + assertThat(e1.getReason()).isInstanceOf(FailToSetupBarDaxSrc.class); + var e2 = r.errors().get("database"); + assertThat(e2.getReason()).isInstanceOf(FailToSetupFooDaxSrc.class); + break; + } + default: { + fail(); + break; + } + } + } catch (Exception e) { + fail(e); + } finally { + Sabi.close(); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(2); + assertThat(m.get("cliargs")).isInstanceOf(BarDaxSrc.class); + assertThat(m.get("database")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("BarDaxSrc#close"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void should_do_startApp_and_ok() { + final Map m = getGlobalDaxSrcMap(); + + Sabi.uses("database", new FooDaxSrc()); + + final Runner app = () -> { + logs.add("run app"); + }; + + try (var ac = Sabi.startApp()) { + app.run(); + } catch (Exception e) { + fail(e); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("database")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(3); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("run app"); + assertThat(logs.get(2)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void should_do_startApp_but_fail_to_setup() { + final Map m = getGlobalDaxSrcMap(); + + Sabi.uses("database", new FooDaxSrc()); + + willFailToSetupFooDaxSrc = true; + + final Runner app = () -> { + logs.add("run app"); + }; + + try (var c = Sabi.startApp()) { + app.run(); + fail(); + } catch (Err e) { + switch (e.getReason()) { + case FailToSetupGlobalDaxSrcs r: { + assertThat(r.errors()).hasSize(1); + var r2 = r.errors().get("database").getReason(); + assertThat(r2).isInstanceOf(FailToSetupFooDaxSrc.class); + break; + } + default: { + fail(e); + break; + } + } + } catch (Exception e) { + fail(e); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("database")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#close"); + } + + @Test + public void should_do_startApp_but_app_failed() { + final Map m = getGlobalDaxSrcMap(); + + Sabi.uses("database", new FooDaxSrc()); + + record FailToDoSomething() {} + + final Runner app = () -> { + throw new Err(new FailToDoSomething()); + }; + + try (var c = Sabi.startApp()) { + app.run(); + } catch (Err e) { + switch (e.getReason()) { + case FailToDoSomething r: { + break; + } + default: { + fail(e); + break; + } + } + } catch (Exception e) { + fail(e); + } + + assertThat(getIsGlobalDaxSrcsFixed()).isTrue(); + assertThat(m).hasSize(1); + assertThat(m.get("database")).isInstanceOf(FooDaxSrc.class); + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setup"); + assertThat(logs.get(1)).isEqualTo("FooDaxSrc#close"); + } +} diff --git a/src/test/java/sabi/SeqTest.java b/src/test/java/com/github/sttk/sabi/SeqTest.java similarity index 94% rename from src/test/java/sabi/SeqTest.java rename to src/test/java/com/github/sttk/sabi/SeqTest.java index 5931527..dc5e7f6 100644 --- a/src/test/java/sabi/SeqTest.java +++ b/src/test/java/com/github/sttk/sabi/SeqTest.java @@ -1,9 +1,11 @@ -package sabi; +package com.github.sttk.sabi; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; +import com.github.sttk.sabi.errs.Err; + import java.util.ArrayList; public class SeqTest { @@ -41,4 +43,3 @@ void should_run_holding_runners_sequentially() throws Exception { assertThat(logs).containsExactly("1", "2"); } } - diff --git a/src/test/java/com/github/sttk/sabi/async/AsyncGroupAsyncTest.java b/src/test/java/com/github/sttk/sabi/async/AsyncGroupAsyncTest.java new file mode 100644 index 0000000..15cbb61 --- /dev/null +++ b/src/test/java/com/github/sttk/sabi/async/AsyncGroupAsyncTest.java @@ -0,0 +1,134 @@ +package com.github.sttk.sabi.async; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Nested; + +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.AsyncGroup; +import com.github.sttk.sabi.Runner; + +public class AsyncGroupAsyncTest { + + @Test + void should_be_ok() { + var ag = new AsyncGroupAsync(); + assertThat(ag.hasErr()).isFalse(); + + final boolean[] exec = {false}; + final Runner fn = () -> { + try { + Thread.sleep(50); + } catch (Exception e) {} + exec[0] = true; + }; + + ag.name = "foo"; + ag.add(fn); + assertThat(ag.hasErr()).isFalse(); + assertThat(exec[0]).isFalse(); + + ag.join(); + assertThat(ag.hasErr()).isFalse(); + assertThat(exec[0]).isTrue(); + + assertThat(ag.makeErrs()).hasSize(0); + assertThat(exec[0]).isTrue(); + } + + record FailToDoSomething() {} + + @Test + void should_be_error() { + var ag = new AsyncGroupAsync(); + assertThat(ag.hasErr()).isFalse(); + + final boolean[] exec = {false}; + final Runner fn = () -> { + try { + Thread.sleep(50); + } catch (Exception e) {} + exec[0] = true; + throw new Err(new FailToDoSomething()); + }; + + ag.name = "foo"; + ag.add(fn); + assertThat(ag.hasErr()).isFalse(); + assertThat(exec[0]).isFalse(); + + ag.join(); + assertThat(ag.hasErr()).isTrue(); + assertThat(exec[0]).isTrue(); + + var m = ag.makeErrs(); + assertThat(m).hasSize(1); + switch (m.get("foo").getReason()) { + case FailToDoSomething reason: + break; + default: + fail(m.get("foo").toString()); + break; + } + assertThat(exec[0]).isTrue(); + } + + record Err0() {} + record Err1() {} + + @Test + void should_get_multipleErrors() { + var ag = new AsyncGroupAsync(); + assertThat(ag.hasErr()).isFalse(); + + final boolean[] exec = {false, false, false}; + final Runner fn0 = () -> { + try { + Thread.sleep(200); + } catch (Exception e) {} + exec[0] = true; + throw new Err(new Err0()); + }; + final Runner fn1 = () -> { + try { + Thread.sleep(400); + } catch (Exception e) {} + exec[1] = true; + throw new Err(new Err1()); + }; + final Runner fn2 = () -> { + try { + Thread.sleep(800); + } catch (Exception e) {} + exec[2] = true; + throw new RuntimeException(); + }; + + ag.name = "foo0"; + ag.add(fn0); + ag.name = "foo1"; + ag.add(fn1); + ag.name = "foo2"; + ag.add(fn2); + assertThat(ag.hasErr()).isFalse(); + assertThat(exec[0]).isFalse(); + assertThat(exec[1]).isFalse(); + assertThat(exec[2]).isFalse(); + + ag.join(); + assertThat(ag.hasErr()).isTrue(); + assertThat(exec[0]).isTrue(); + assertThat(exec[1]).isTrue(); + assertThat(exec[2]).isTrue(); + + var m = ag.makeErrs(); + assertThat(m).hasSize(3); + assertThat(m.get("foo0").getReasonName()).isEqualTo("Err0"); + assertThat(m.get("foo1").getReasonName()).isEqualTo("Err1"); + assertThat(m.get("foo2").getReasonName()).isEqualTo("RunnerFailed"); + assertThat(exec[0]).isTrue(); + assertThat(exec[1]).isTrue(); + assertThat(exec[2]).isTrue(); + } +} diff --git a/src/test/java/com/github/sttk/sabi/async/AsyncGroupSyncTest.java b/src/test/java/com/github/sttk/sabi/async/AsyncGroupSyncTest.java new file mode 100644 index 0000000..1452fce --- /dev/null +++ b/src/test/java/com/github/sttk/sabi/async/AsyncGroupSyncTest.java @@ -0,0 +1,76 @@ +package com.github.sttk.sabi.async; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Nested; + +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.AsyncGroup; +import com.github.sttk.sabi.Runner; + +public class AsyncGroupSyncTest { + + @Test + void should_be_ok() { + var ag = new AsyncGroupSync(); + assertThat(ag.getErr()).isNull(); + + final boolean[] exec = { false }; + final Runner fn = () -> { + exec[0] = true; + }; + + ag.add(fn); + assertThat(ag.getErr()).isNull(); + assertThat(exec[0]).isTrue(); + } + + record FailToDoSomething() {} + + @Test + void should_be_error() { + var ag = new AsyncGroupSync(); + assertThat(ag.getErr()).isNull(); + + final boolean[] exec = { false }; + final Runner fn = () -> { + exec[0] = true; + throw new Err(new FailToDoSomething()); + }; + + ag.add(fn); + switch (ag.getErr().getReason()) { + case FailToDoSomething reason: + break; + default: + fail(ag.getErr().toString()); + break; + } + assertThat(exec[0]).isTrue(); + } + + @Test + void should_be_error_by_runtimeexception() { + var ag = new AsyncGroupSync(); + assertThat(ag.getErr()).isNull(); + + final boolean[] exec = { false }; + final Runner fn = () -> { + exec[0] = true; + throw new RuntimeException(); + }; + + ag.add(fn); + switch (ag.getErr().getReason()) { + case AsyncGroup.RunnerFailed reason: + assertThat(ag.getErr().getCause().getClass()) + .isEqualTo(RuntimeException.class); + break; + default: + fail(ag.getErr().toString()); + break; + } + assertThat(exec[0]).isTrue(); + } +} diff --git a/src/test/java/com/github/sttk/sabi/errs/ErrHandlerTest.java b/src/test/java/com/github/sttk/sabi/errs/ErrHandlerTest.java new file mode 100644 index 0000000..486f208 --- /dev/null +++ b/src/test/java/com/github/sttk/sabi/errs/ErrHandlerTest.java @@ -0,0 +1,79 @@ +package errs; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import java.util.List; +import java.util.ArrayList; + +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.errs.notify.ErrNotifier; + +public class ErrHandlerTest { + + static final List syncLogger = new ArrayList<>(); + static final List asyncLogger = new ArrayList<>(); + + public record FailToDoSomething(String name) {} + + @BeforeEach + void reset() { + try { + var f0 = Err.class.getDeclaredField("notifier"); + f0.setAccessible(true); + var n0 = f0.get(null); + var f1 = ErrNotifier.class.getDeclaredField("isFixed"); + f1.setAccessible(true); + f1.setBoolean(n0, false); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Test + public void should_notify_that_errs_are_created() { + Err.addSyncHandler((err, occ) -> { + syncLogger.add(String.format("1. %s (%s:%d)", + err.getReason().toString(), occ.getFile(), occ.getLine())); + }); + Err.addSyncHandler((err, occ) -> { + syncLogger.add(String.format("2. %s (%s:%d)", + err.getReason().toString(), occ.getFile(), occ.getLine())); + }); + Err.addAsyncHandler((err, occ) -> { + try { Thread.sleep(10); } catch (Exception e) {} + asyncLogger.add(String.format("3. %s (%s:%d)", + err.getReason().toString(), occ.getFile(), occ.getLine())); + }); + Err.addAsyncHandler((err, occ) -> { + try { Thread.sleep(20); } catch (Exception e) {} + asyncLogger.add(String.format("4. %s (%s:%d)", + err.getReason().toString(), occ.getFile(), occ.getLine())); + }); + Err.fixCfg(); + + try { + throw new Err(new FailToDoSomething("abc")); + } catch (Err e) { + assertThat(e.getReason()).isInstanceOf(FailToDoSomething.class); + + try { + Thread.sleep(100); + } catch (Exception e2) {} + + assertThat(syncLogger).hasSize(2); + assertThat(syncLogger.get(0)).startsWith( + "1. FailToDoSomething[name=abc] (ErrHandlerTest.java:58)"); + assertThat(syncLogger.get(1)).startsWith( + "2. FailToDoSomething[name=abc] (ErrHandlerTest.java:58)"); + + assertThat(asyncLogger).hasSize(2); + assertThat(asyncLogger.get(0)).startsWith( + "3. FailToDoSomething[name=abc] (ErrHandlerTest.java:58)"); + assertThat(asyncLogger.get(1)).startsWith( + "4. FailToDoSomething[name=abc] (ErrHandlerTest.java:58)"); + } + } +} diff --git a/src/test/java/com/github/sttk/sabi/errs/ErrTest.java b/src/test/java/com/github/sttk/sabi/errs/ErrTest.java new file mode 100644 index 0000000..2a8af5b --- /dev/null +++ b/src/test/java/com/github/sttk/sabi/errs/ErrTest.java @@ -0,0 +1,109 @@ +package com.github.sttk.sabi.errs; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; +import java.io.IOException; + +public class ErrTest { + + // error reasons + record InvalidValue(String value) {} + record FailToGetValue(String name) {} + + enum Reason { + value, + name, + } + + @Test + void should_create_an_Err() { + try { + throw new Err(new InvalidValue("abc")); + } catch (Err e) { + assertThat(e.getMessage()).isEqualTo("{reason=InvalidValue, value=abc}"); + assertThat(e.getReason()).isInstanceOf(InvalidValue.class); + assertThat(e.getReasonName()).isEqualTo("InvalidValue"); + assertThat(e.getReasonPackage()).isEqualTo("com.github.sttk.sabi.errs"); + assertThat(e.get("value")).isEqualTo("abc"); + assertThat(e.get("name")).isNull(); + assertThat(e.get(Reason.value)).isEqualTo("abc"); + assertThat(e.get(Reason.name)).isNull(); + assertThat(e.getCause()).isNull(); + + var m = e.getSituation(); + assertThat(m).hasSize(1); + assertThat(m.get("value")).isEqualTo("abc"); + } + } + + @Test + void should_be_error_if_reason_is_null() { + try { + throw new Err(null); + } catch (Err e) { + fail(e); + } catch (NullPointerException e) { + } catch (Exception e) { + fail(e); + } + + var cause = new IOException(); + try { + throw new Err(null, cause); + } catch (Err e) { + fail(e); + } catch (NullPointerException e) { + } catch (Exception e) { + fail(e); + } + } + + @Test + void should_create_an_Err_with_cause() { + var cause = new IOException(); + try { + throw new Err(new InvalidValue("abc"), cause); + } catch (Err e) { + assertThat(e.getMessage()).isEqualTo("{reason=InvalidValue, value=abc, cause=java.io.IOException}"); + assertThat(e.getReason()).isInstanceOf(InvalidValue.class); + assertThat(e.getReasonName()).isEqualTo("InvalidValue"); + assertThat(e.getReasonPackage()).isEqualTo("com.github.sttk.sabi.errs"); + assertThat(e.get("value")).isEqualTo("abc"); + assertThat(e.get("name")).isNull(); + assertThat(e.get(Reason.value)).isEqualTo("abc"); + assertThat(e.get(Reason.name)).isNull(); + assertThat(e.getCause()).isEqualTo(cause); + + var m = e.getSituation(); + assertThat(m).hasSize(1); + assertThat(e.get("value")).isEqualTo("abc"); + } + } + + @Test + void should_create_an_Err_with_cause_that_is_also_Err() { + var cause = new Err(new FailToGetValue("foo")); + try { + throw new Err(new InvalidValue("abc"), cause); + } catch (Err e) { + assertThat(e.getMessage()).isEqualTo("{reason=InvalidValue, value=abc, cause={reason=FailToGetValue, name=foo}}"); + assertThat(e.getReason()).isInstanceOf(InvalidValue.class); + assertThat(e.getReasonName()).isEqualTo("InvalidValue"); + assertThat(e.getReasonPackage()).isEqualTo("com.github.sttk.sabi.errs"); + assertThat(e.get("value")).isEqualTo("abc"); + assertThat(e.get("name")).isEqualTo("foo"); + assertThat(e.get(Reason.value)).isEqualTo("abc"); + assertThat(e.get(Reason.name)).isEqualTo("foo"); + assertThat(e.getCause()).isEqualTo(cause); + + var m = e.getSituation(); + assertThat(m).hasSize(2); + assertThat(e.get("value")).isEqualTo("abc"); + assertThat(e.get("name")).isEqualTo("foo"); + + assertThat(e.getFileName()).isEqualTo("ErrTest.java"); + assertThat(e.getLineNumber()).isEqualTo(88); + } + } +} diff --git a/src/test/java/sabi/notify/ErrNotifierTest.java b/src/test/java/com/github/sttk/sabi/errs/notify/ErrNotifierTest.java similarity index 57% rename from src/test/java/sabi/notify/ErrNotifierTest.java rename to src/test/java/com/github/sttk/sabi/errs/notify/ErrNotifierTest.java index 6ff6d7e..6afb753 100644 --- a/src/test/java/sabi/notify/ErrNotifierTest.java +++ b/src/test/java/com/github/sttk/sabi/errs/notify/ErrNotifierTest.java @@ -1,44 +1,55 @@ -package sabi.notify; +package com.github.sttk.sabi.errs.notify; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.BeforeEach; -import sabi.Err; -import sabi.ErrHandler; +import com.github.sttk.sabi.errs.Err; +import com.github.sttk.sabi.errs.ErrHandler; import java.util.ArrayList; public class ErrNotifierTest { + @BeforeEach + void reset() { + try { + var f0 = Err.class.getField("notifier"); + var n0 = f0.get(null); + var f1 = ErrNotifier.class.getField("isFixed"); + f1.setBoolean(n0, false); + } catch (Exception e) {} + } + @Test - void should_add_err_handlers_and_fix() { + void should_add_handlers_and_fix() { final var notifier = new ErrNotifier(); assertThat(notifier.isFixed()).isFalse(); assertThat(notifier.syncErrHandlers).isEmpty(); assertThat(notifier.asyncErrHandlers).isEmpty(); final ErrHandler h1 = (err, occ) -> {}; - notifier.addSyncErrHandler(h1); + notifier.addSyncHandler(h1); assertThat(notifier.isFixed()).isFalse(); assertThat(notifier.syncErrHandlers).containsExactly(h1); assertThat(notifier.asyncErrHandlers).isEmpty(); final ErrHandler h2 = (err, occ) -> {}; - notifier.addSyncErrHandler(h2); + notifier.addSyncHandler(h2); assertThat(notifier.isFixed()).isFalse(); assertThat(notifier.syncErrHandlers).containsExactly(h1, h2); assertThat(notifier.asyncErrHandlers).isEmpty(); final ErrHandler h3 = (err, occ) -> {}; - notifier.addAsyncErrHandler(h3); + notifier.addAsyncHandler(h3); assertThat(notifier.isFixed()).isFalse(); assertThat(notifier.syncErrHandlers).containsExactly(h1, h2); assertThat(notifier.asyncErrHandlers).containsExactly(h3); final ErrHandler h4 = (err, occ) -> {}; - notifier.addAsyncErrHandler(h4); + notifier.addAsyncHandler(h4); assertThat(notifier.isFixed()).isFalse(); assertThat(notifier.syncErrHandlers).containsExactly(h1, h2); assertThat(notifier.asyncErrHandlers).containsExactly(h3, h4); @@ -46,18 +57,19 @@ void should_add_err_handlers_and_fix() { notifier.fix(); final ErrHandler h5 = (err, occ) -> {}; - notifier.addSyncErrHandler(h5); + notifier.addSyncHandler(h5); assertThat(notifier.isFixed()).isTrue(); assertThat(notifier.syncErrHandlers).containsExactly(h1, h2); assertThat(notifier.asyncErrHandlers).containsExactly(h3, h4); final ErrHandler h6 = (err, occ) -> {}; - notifier.addAsyncErrHandler(h6); + notifier.addAsyncHandler(h6); assertThat(notifier.isFixed()).isTrue(); assertThat(notifier.syncErrHandlers).containsExactly(h1, h2); assertThat(notifier.asyncErrHandlers).containsExactly(h3, h4); } + // error reasons record FailToDoSomething(String name) {} @@ -83,8 +95,10 @@ void should_execute_sync_handlers() { final var logs = new ArrayList(); final var notifier = new ErrNotifier(); - notifier.addSyncErrHandler((err, occ) -> { - logs.add(err.getReason().toString()); + notifier.addSyncHandler((err, occ) -> { + var log = String.format("%s (%s:%d) %s", err.getReason().toString(), + occ.getFile(), occ.getLine(), occ.getTime().toString()); + logs.add(log); }); try { @@ -111,7 +125,9 @@ void should_execute_sync_handlers() { } } - assertThat(logs).containsExactly("FailToDoSomething[name=abc]"); + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).startsWith( + "FailToDoSomething[name=abc] (ErrNotifierTest.java:119) "); } @Test @@ -119,8 +135,10 @@ void should_execute_async_handlers() { final var logs = new ArrayList(); final var notifier = new ErrNotifier(); - notifier.addAsyncErrHandler((err, occ) -> { - logs.add(err.getReason().toString()); + notifier.addAsyncHandler((err, occ) -> { + var log = String.format("%s (%s:%d) %s", err.getReason().toString(), + occ.getFile(), occ.getLine(), occ.getTime().toString()); + logs.add(log); }); try { @@ -149,18 +167,26 @@ void should_execute_async_handlers() { } } - assertThat(logs).containsExactly("FailToDoSomething[name=abc]"); + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).startsWith( + "FailToDoSomething[name=abc] (ErrNotifierTest.java:160) "); } @Test void should_execute_sync_and_async_handlers() { final var logs = new ArrayList(); final var notifier = new ErrNotifier(); - notifier.addAsyncErrHandler((err, occ) -> { - logs.add("Async: " + err.getReason()); + notifier.addAsyncHandler((err, occ) -> { + var log = String.format("Async: %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); - notifier.addSyncErrHandler((err, occ) -> { - logs.add("Sync: " + err.getReason()); + notifier.addSyncHandler((err, occ) -> { + var log = String.format("Sync: %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); try { @@ -189,27 +215,37 @@ void should_execute_sync_and_async_handlers() { } } - assertThat(logs).containsOnly( - "Async: FailToDoSomething[name=abc]", - "Sync: FailToDoSomething[name=abc]" - ); + assertThat(logs).hasSize(2); + assertThat(logs.get(0)).startsWith( + "Sync: FailToDoSomething[name=abc] (ErrNotifierTest.java:208) "); + assertThat(logs.get(1)).startsWith( + "Async: FailToDoSomething[name=abc] (ErrNotifierTest.java:208) "); } @Test void should_stop_executing_sync_handlers_if_one_of_handlers_failed() { final var logs = new ArrayList(); final var notifier = new ErrNotifier(); - notifier.addAsyncErrHandler((err, occ) -> { - logs.add("Async: " + err.getReason()); + notifier.addAsyncHandler((err, occ) -> { + var log = String.format("Async: %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); - notifier.addSyncErrHandler((err, occ) -> { - logs.add("Sync(1): " + err.getReason()); + notifier.addSyncHandler((err, occ) -> { + var log = String.format("Sync(1): %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); - notifier.addSyncErrHandler((err, occ) -> { + notifier.addSyncHandler((err, occ) -> { throw new RuntimeException(); }); - notifier.addSyncErrHandler((err, odt) -> { - logs.add("Sync(3): " + err.getReason()); + notifier.addSyncHandler((err, occ) -> { + var log = String.format("Sync(3): %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); try { @@ -241,26 +277,38 @@ void should_stop_executing_sync_handlers_if_one_of_handlers_failed() { } } - assertThat(logs).containsOnly( - "Sync(1): FailToDoSomething[name=abc]" - ); + assertThat(logs).hasSize(1); + assertThat(logs.get(0)).startsWith( + "Sync(1): FailToDoSomething[name=abc] (ErrNotifierTest.java:267) "); } @Test void should_execute_all_async_handlers_even_if_one_of_handlers_failed() { final var logs = new ArrayList(); final var notifier = new ErrNotifier(); - notifier.addAsyncErrHandler((err, occ) -> { - logs.add("Async(1): " + err.getReason()); + notifier.addAsyncHandler((err, occ) -> { + try { Thread.sleep(10); } catch (Exception e) {} + var log = String.format("Async(1): %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); - notifier.addAsyncErrHandler((err, occ) -> { - throw new RuntimeException(); + notifier.addAsyncHandler((err, occ) -> { + throw new RuntimeException( + "**This exception is not a error but for test purpose.**"); }); - notifier.addAsyncErrHandler((err, occ) -> { - logs.add("Async(3): " + err.getReason()); + notifier.addAsyncHandler((err, occ) -> { + try { Thread.sleep(100); } catch (Exception e) {} + var log = String.format("Async(3): %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); - notifier.addSyncErrHandler((err, occ) -> { - logs.add("Sync: " + err.getReason()); + notifier.addSyncHandler((err, occ) -> { + var log = String.format("Sync: %s (%s:%d) %s", + err.getReason().toString(), occ.getFile(), occ.getLine(), + occ.getTime().toString()); + logs.add(log); }); try { @@ -289,11 +337,15 @@ void should_execute_all_async_handlers_even_if_one_of_handlers_failed() { } } - assertThat(logs).containsOnly( - "Sync: FailToDoSomething[name=abc]", - "Async(1): FailToDoSomething[name=abc]", - "Async(3): FailToDoSomething[name=abc]" - ); + try { Thread.sleep(200); } catch (Exception e) {} + + assertThat(logs).hasSize(3); + assertThat(logs.get(0)).startsWith( + "Sync: FailToDoSomething[name=abc] (ErrNotifierTest.java:330) "); + assertThat(logs.get(1)).startsWith( + "Async(1): FailToDoSomething[name=abc] (ErrNotifierTest.java:330) "); + assertThat(logs.get(2)).startsWith( + "Async(3): FailToDoSomething[name=abc] (ErrNotifierTest.java:330) "); } } } diff --git a/src/test/java/sabi/DaxAuxForTest.java b/src/test/java/sabi/DaxAuxForTest.java deleted file mode 100644 index afc4f0e..0000000 --- a/src/test/java/sabi/DaxAuxForTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package sabi; - -import java.util.Map; - -public class DaxAuxForTest { - - static void clearGlobalDaxSrcs() throws Exception { - final var f0 = DaxBase.class.getDeclaredField("isGlobalDaxSrcsFixed"); - f0.setAccessible(true); - f0.setBoolean(null, false); - - final var f1 = DaxBase.class.getDeclaredField("globalDaxSrcMap"); - f1.setAccessible(true); - @SuppressWarnings("unchecked") - var map1 = (Map) f1.get(null); - map1.clear(); - } - - static boolean isGlobalDaxSrcsFixed() throws Exception { - final var f0 = DaxBase.class.getDeclaredField("isGlobalDaxSrcsFixed"); - f0.setAccessible(true); - return f0.getBoolean(null); - } - - static Map globalDaxSrcMap() throws Exception { - final var f1 = DaxBase.class.getDeclaredField("globalDaxSrcMap"); - f1.setAccessible(true); - @SuppressWarnings("unchecked") - var map1 = (Map) f1.get(null); - return map1; - } - - static boolean isLocalDaxSrcsFixed(DaxBase base) throws Exception { - final var f0 = DaxBase.class.getDeclaredField("isLocalDaxSrcsFixed"); - f0.setAccessible(true); - return f0.getBoolean(base); - } - - static Map localDaxSrcMap(DaxBase base) throws Exception { - final var f1 = DaxBase.class.getDeclaredField("localDaxSrcMap"); - f1.setAccessible(true); - @SuppressWarnings("unchecked") - var map1 = (Map) f1.get(base); - return map1; - } - - static Map daxConnMap(DaxBase base) throws Exception { - final var f1 = DaxBase.class.getDeclaredField("daxConnMap"); - f1.setAccessible(true); - @SuppressWarnings("unchecked") - var map1 = (Map) f1.get(base); - return map1; - } -} diff --git a/src/test/java/sabi/DaxDummyForTest.java b/src/test/java/sabi/DaxDummyForTest.java deleted file mode 100644 index 2e1c9e3..0000000 --- a/src/test/java/sabi/DaxDummyForTest.java +++ /dev/null @@ -1,366 +0,0 @@ -package sabi; - -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; - -public class DaxDummyForTest { - - static List logs = new ArrayList<>();; - - static boolean willFailToSetUpFooDaxSrc = false; - static boolean willFailToCommitFooDaxConn = false; - static boolean willFailToCreateFooDaxConn = false; - - static boolean willFailToCreateBDaxConn = false; - static boolean willFailToCommitBDaxConn = false; - - static void clearDaxBase() throws Exception { - DaxAuxForTest.clearGlobalDaxSrcs(); - - logs.clear(); - - willFailToSetUpFooDaxSrc = false; - willFailToCommitFooDaxConn = false; - willFailToCreateFooDaxConn = false; - - willFailToCreateBDaxConn = false; - willFailToCommitBDaxConn = false; - } - - static record FailToDoSomething(String text) {} - - static record FailToCreateBDaxConn() {} - static record FailToCommitBDaxConn() {} - static record FailToRunLogic() {} - - static class FooDaxConn implements DaxConn { - final String label; - final Map map = new HashMap<>(); - - FooDaxConn(String label) { - this.label = label; - } - - @Override - public void commit() throws Err { - if (willFailToCommitFooDaxConn) { - throw new Err(new FailToDoSomething("FailToCommitFooDaxConn")); - } - logs.add("FooDaxConn#commit"); - } - - @Override - public void rollback() { - logs.add("FooDaxConn#rollback"); - } - - @Override - public void close() { - logs.add("FooDaxConn#close"); - } - } - - static class FooDaxSrc implements DaxSrc { - String label; - - @Override - public DaxConn createDaxConn() throws Err { - if (willFailToCreateFooDaxConn) { - throw new Err(new FailToDoSomething("FailToCreateFooDaxConn")); - } - logs.add("FooDaxSrc#createDaxConn"); - return new FooDaxConn(this.label); - } - - @Override - public void setUp() throws Err { - if (willFailToSetUpFooDaxSrc) { - throw new Err(new FailToDoSomething("FailToSetUpFooDaxSrc")); - } - logs.add("FooDaxSrc#setUp"); - } - - @Override - public void end() { - logs.add("FooDaxSrc#end"); - } - } - - static class BarDaxConn implements DaxConn { - final String label; - final Map map = new HashMap<>(); - - BarDaxConn(String label) { - this.label = label; - } - - @Override - public void commit() throws Err { - logs.add("BarDaxConn#commit"); - } - - @Override - public void rollback() { - logs.add("BarDaxConn#rollback"); - } - - @Override - public void close() { - logs.add("BarDaxConn#close"); - } - } - - static class BarDaxSrc implements DaxSrc { - String label; - - @Override - public DaxConn createDaxConn() throws Err { - logs.add("BarDaxSrc#createDaxConn"); - return new BarDaxConn(this.label); - } - - @Override - public void setUp() throws Err { - logs.add("BarDaxSrc#setUp"); - } - - @Override - public void end() { - logs.add("BarDaxSrc#end"); - } - } - - static class MapDaxSrc implements DaxSrc { - final Map dataMap = new HashMap<>(); - - @Override - public DaxConn createDaxConn() throws Err { - return new MapDaxConn(this.dataMap); - } - - @Override - public void setUp() throws Err { - } - - @Override - public void end() { - } - } - - static class MapDaxConn implements DaxConn { - final Map dataMap; - - MapDaxConn(Map map) { - this.dataMap = map; - } - - @Override - public void commit() throws Err { - } - - @Override - public void rollback() { - } - - @Override - public void close() { - } - } - - static interface HogeFugaDax extends Dax { - String getHogeData() throws Err; - void setFugaData(String data) throws Err; - } - - static class HogeFugaLogic implements Logic { - @Override - public void run(final HogeFugaDax dax) throws Err { - var data = dax.getHogeData(); - dax.setFugaData(data); - } - } - - static interface FugaPiyoDax extends Dax { - String getFugaData() throws Err; - void setPiyoData(String data) throws Err; - } - - static class FugaPiyoLogic implements Logic { - @Override - public void run(FugaPiyoDax dax) throws Err { - var data = dax.getFugaData(); - dax.setPiyoData(data); - } - } - - static interface HogeDax extends Dax, HogeFugaDax { - default String getHogeData() throws Err { - var conn = getDaxConn("hoge", MapDaxConn.class); - var data = conn.dataMap.get("hogehoge"); - return data; - } - - default void SetHogeData(String data) throws Err { - var conn = getDaxConn("hoge", MapDaxConn.class); - conn.dataMap.put("hogehoge", data); - } - } - - static interface FugaDax extends Dax, HogeFugaDax, FugaPiyoDax { - default String getFugaData() throws Err { - var conn = getDaxConn("fuga", MapDaxConn.class); - var data = conn.dataMap.get("fugafuga"); - return data; - } - - default void setFugaData(String data) throws Err { - var conn = getDaxConn("fuga", MapDaxConn.class); - conn.dataMap.put("fugafuga", data); - } - } - - static interface PiyoDax extends Dax, FugaPiyoDax { - default String getPiyoData() throws Err { - var conn = getDaxConn("piyo", MapDaxConn.class); - var data = conn.dataMap.get("piyopiyo"); - return data; - } - - default void setPiyoData(String data) throws Err { - var conn = getDaxConn("piyo", MapDaxConn.class); - conn.dataMap.put("piyopiyo", data); - } - } - - static class HogeFugaPiyoDaxBase extends DaxBase implements - HogeDax, FugaDax, PiyoDax, - HogeFugaDax, FugaPiyoDax {} - - static class ADaxSrc implements DaxSrc { - final Map aMap = new HashMap<>(); - - @Override - public DaxConn createDaxConn() throws Err { - return new ADaxConn(this.aMap); - } - - @Override - public void setUp() throws Err { - } - - @Override - public void end() { - } - } - - static class ADaxConn implements DaxConn { - final Map aMap; - - ADaxConn(Map map) { - this.aMap = map; - } - - @Override - public void commit() throws Err { - logs.add("ADaxConn#commit"); - } - - @Override - public void rollback() { - logs.add("ADaxConn#rollback"); - } - - @Override - public void close() { - logs.add("ADaxConn#close"); - } - } - - static class BDaxSrc implements DaxSrc { - final Map bMap = new HashMap<>(); - - @Override - public DaxConn createDaxConn() throws Err { - if (willFailToCreateBDaxConn) { - throw new Err(new FailToCreateBDaxConn()); - } - return new BDaxConn(this.bMap); - } - - @Override - public void setUp() throws Err { - } - - @Override - public void end() { - } - } - - static class BDaxConn implements DaxConn { - final Map bMap; - - BDaxConn(Map map) { - this.bMap = map; - } - - @Override - public void commit() throws Err { - if (willFailToCommitBDaxConn) { - throw new Err(new FailToCommitBDaxConn()); - } - logs.add("BDaxConn#commit"); - } - - @Override - public void rollback() { - logs.add("BDaxConn#rollback"); - } - - @Override - public void close() { - logs.add("BDaxConn#close"); - } - } - - static class CDaxSrc implements DaxSrc { - final Map cMap = new HashMap<>(); - - @Override - public DaxConn createDaxConn() throws Err { - return new CDaxConn(this.cMap); - } - - @Override - public void setUp() throws Err { - } - - @Override - public void end() { - } - } - - static class CDaxConn implements DaxConn { - final Map cMap; - - CDaxConn(Map map) { - this.cMap = map; - } - - @Override - public void commit() throws Err { - logs.add("CDaxConn#commit"); - } - - @Override - public void rollback() { - logs.add("CDaxConn#rollback"); - } - - @Override - public void close() { - logs.add("CDaxConn#close"); - } - } -} diff --git a/src/test/java/sabi/DaxTest.java b/src/test/java/sabi/DaxTest.java deleted file mode 100644 index bcd51a5..0000000 --- a/src/test/java/sabi/DaxTest.java +++ /dev/null @@ -1,778 +0,0 @@ -package sabi; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; -import static sabi.DaxAuxForTest.*; -import static sabi.DaxDummyForTest.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.BeforeEach; - -import java.lang.reflect.Field; - -import java.util.List; -import java.util.ArrayList; -import java.util.Map; - -public class DaxTest { - - @BeforeEach - void clear() throws Exception { - clearDaxBase(); - } - - @Nested - class TestAddGlobalDaxSrc { - @Test - void should_add_DaxSrc() throws Exception { - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(0); - - DaxBase.addGlobalDaxSrc("foo", new FooDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(1); - - DaxBase.addGlobalDaxSrc("bar", new BarDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(2); - } - } - - @Nested - class TestStartUpGlobalDaxSrcs { - @Test - void should_fix_composition_of_global_DaxSrc() throws Exception { - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(0); - - DaxBase.addGlobalDaxSrc("foo", new FooDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(1); - - try { - DaxBase.startUpGlobalDaxSrcs(); - } catch (Err e) { - fail(e); - } - - assertThat(isGlobalDaxSrcsFixed()).isTrue(); - assertThat(globalDaxSrcMap()).hasSize(1); - - DaxBase.addGlobalDaxSrc("bar", new BarDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isTrue(); - assertThat(globalDaxSrcMap()).hasSize(1); - - assertThat(logs).hasSize(1); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - } - - @Test - void should_fail_to_set_up_dax_src() throws Exception { - willFailToSetUpFooDaxSrc = true; - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(0); - - DaxBase.addGlobalDaxSrc("bar", new BarDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(1); - - DaxBase.addGlobalDaxSrc("foo", new FooDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(2); - - try { - DaxBase.startUpGlobalDaxSrcs(); - fail(); - } catch (Err e) { - var r = DaxBase.FailToStartUpGlobalDaxSrcs.class.cast(e.getReason()); - var e1 = r.errors().get("foo"); - var r1 = FailToDoSomething.class.cast(e1.getReason()); - assertThat(r1.text()).isEqualTo("FailToSetUpFooDaxSrc"); - } - - assertThat(logs).hasSize(3); - assertThat(logs.get(0)).isEqualTo("BarDaxSrc#setUp"); - if (logs.get(0).equals("FooDaxSrc#End")) { - assertThat(logs.get(1)).isEqualTo("FooDaxSrc#end"); - assertThat(logs.get(2)).isEqualTo("BarDaxSrc#end"); - } else { - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#end"); - assertThat(logs.get(2)).isEqualTo("FooDaxSrc#end"); - } - } - } - - @Nested - class TestShutdownGlobalDaxSrcs { - @Test - void should_free_all_global_dax_srcs() throws Exception { - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(0); - - DaxBase.addGlobalDaxSrc("foo", new FooDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(1); - - DaxBase.addGlobalDaxSrc("bar", new BarDaxSrc()); - - assertThat(isGlobalDaxSrcsFixed()).isFalse(); - assertThat(globalDaxSrcMap()).hasSize(2); - - try { - DaxBase.startUpGlobalDaxSrcs(); - } catch (Err e) { - fail(e); - } - - assertThat(isGlobalDaxSrcsFixed()).isTrue(); - assertThat(globalDaxSrcMap()).hasSize(2); - - DaxBase.shutdownGlobalDaxSrcs(); - - assertThat(isGlobalDaxSrcsFixed()).isTrue(); - assertThat(globalDaxSrcMap()).hasSize(2); - - assertThat(logs).hasSize(4); - if (logs.get(0).equals("FooDaxSrc#setUp")) { - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setUp"); - } else { - assertThat(logs.get(0)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("FooDaxSrc#setUp"); - } - if (logs.get(2).equals("FooDaxSrc#end")) { - assertThat(logs.get(2)).isEqualTo("FooDaxSrc#end"); - assertThat(logs.get(3)).isEqualTo("BarDaxSrc#end"); - } else { - assertThat(logs.get(2)).isEqualTo("BarDaxSrc#end"); - assertThat(logs.get(3)).isEqualTo("FooDaxSrc#end"); - } - } - } - - @Nested - class TestDaxBase { - @Nested - class TestSetUpLocalDaxSrc { - @Test - void should_register_local_dax_src() throws Exception { - var base = new DaxBase() {}; - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(2); - assertThat(daxConnMap(base)).hasSize(0); - - assertThat(logs).hasSize(2); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setUp"); - } - - @Test - void should_unable_to_add_local_dax_src_in_txn() throws Exception { - var base = new DaxBase() {}; - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.begin(); - - assertThat(isLocalDaxSrcsFixed(base)).isTrue(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isTrue(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - assertThat(logs).hasSize(1); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - - base.end(); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(2); - assertThat(daxConnMap(base)).hasSize(0); - } - - @Test - void should_fail_to_set_up_dax_src() throws Exception { - willFailToSetUpFooDaxSrc = true; - - var base = new DaxBase() {}; - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - fail(); - } catch (Err e) { - var r = FailToDoSomething.class.cast(e.getReason()); - assertThat(r.text()).isEqualTo("FailToSetUpFooDaxSrc"); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.freeAllLocalDaxSrcs(); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - assertThat(logs).hasSize(2); - assertThat(logs.get(0)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#end"); - } - } - - @Nested - class TestFreeLocalDaxSrc { - @Test - void should_free_local_dax_src() throws Exception { - var base = new DaxBase() {}; - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.freeLocalDaxSrc("foo"); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(2); - assertThat(daxConnMap(base)).hasSize(0); - - base.freeLocalDaxSrc("bar"); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.freeLocalDaxSrc("foo"); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - assertThat(logs).hasSize(6); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("FooDaxSrc#end"); - assertThat(logs.get(2)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(3)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(4)).isEqualTo("BarDaxSrc#end"); - assertThat(logs.get(5)).isEqualTo("FooDaxSrc#end"); - } - - @Test - void should_unable_to_free_local_dax_src_in_txn() throws Exception { - var base = new DaxBase() {}; - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.begin(); - - assertThat(isLocalDaxSrcsFixed(base)).isTrue(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.freeLocalDaxSrc("foo"); - - assertThat(isLocalDaxSrcsFixed(base)).isTrue(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.end(); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(1); - assertThat(daxConnMap(base)).hasSize(0); - - base.freeLocalDaxSrc("foo"); - - assertThat(isLocalDaxSrcsFixed(base)).isFalse(); - assertThat(localDaxSrcMap(base)).hasSize(0); - assertThat(daxConnMap(base)).hasSize(0); - } - } - - @Nested - class TestGetDaxConn { - @Test - void should_get_dax_conn_of_local_dax_src() throws Exception { - var base = new DaxBase() {}; - - try { - base.getDaxConn("foo", FooDaxConn.class); - fail(); - } catch (Err e) { - var r = DaxBase.DaxSrcIsNotFound.class.cast(e.getReason()); - assertThat(r.name()).isEqualTo("foo"); - } - - try { - DaxBase.startUpGlobalDaxSrcs(); - } catch (Err e) { - fail(e); - } - - try { - base.getDaxConn("foo", FooDaxConn.class); - fail(); - } catch (Err e) { - var r = DaxBase.DaxSrcIsNotFound.class.cast(e.getReason()); - assertThat(r.name()).isEqualTo("foo"); - } - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - var conn = base.getDaxConn("foo", FooDaxConn.class); - - var conn2 = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn2).isEqualTo(conn); - } - - @Test - void should_get_dax_conn_of_global_dax_src() throws Exception { - var base = new DaxBase() {}; - - try { - base.getDaxConn("foo", FooDaxConn.class); - fail(); - } catch (Err e) { - var r = DaxBase.DaxSrcIsNotFound.class.cast(e.getReason()); - assertThat(r.name()).isEqualTo("foo"); - } - - DaxBase.addGlobalDaxSrc("foo", new FooDaxSrc()); - - try { - DaxBase.startUpGlobalDaxSrcs(); - } catch (Err e) { - fail(e); - } - - var conn = base.getDaxConn("foo", FooDaxConn.class); - - var conn2 = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn2).isEqualTo(conn); - } - - @Test - void should_take_priority_of_local_ds_than_global_ds() throws Exception { - var base = new DaxBase() {}; - - try { - base.getDaxConn("foo", FooDaxConn.class); - fail(); - } catch (Err e) { - var r = DaxBase.DaxSrcIsNotFound.class.cast(e.getReason()); - assertThat(r.name()).isEqualTo("foo"); - } - - DaxBase.addGlobalDaxSrc("foo", new FooDaxSrc()); - - try { - DaxBase.startUpGlobalDaxSrcs(); - } catch (Err e) { - fail(e); - } - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - var conn = base.getDaxConn("foo", FooDaxConn.class); - - var conn2 = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn2).isEqualTo(conn); - } - - @Test - void should_fail_to_create_dax_conn() throws Exception { - willFailToCreateFooDaxConn = true; - - var base = new DaxBase() {}; - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - try { - base.getDaxConn("foo", FooDaxConn.class); - fail(); - } catch (Err e) { - var r = DaxBase.FailToCreateDaxConn.class.cast(e.getReason()); - assertThat(r.name()).isEqualTo("foo"); - var e1 = Err.class.cast(e.getCause()); - assertThat(e1.getReason()).isInstanceOf(FailToDoSomething.class); - } - } - - @Test - void should_commit() throws Exception { - var base = new DaxBase() {}; - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - base.begin(); - - try { - var conn = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - var conn = base.getDaxConn("bar", BarDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - base.commit(); - } catch (Err e) { - fail(e); - } - - assertThat(logs).hasSize(6); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(2)).isEqualTo("FooDaxSrc#createDaxConn"); - assertThat(logs.get(3)).isEqualTo("BarDaxSrc#createDaxConn"); - if (logs.get(4).equals("FooDaxConn#commit")) { - assertThat(logs.get(4)).isEqualTo("FooDaxConn#commit"); - assertThat(logs.get(5)).isEqualTo("BarDaxConn#commit"); - } else { - assertThat(logs.get(4)).isEqualTo("BarDaxConn#commit"); - assertThat(logs.get(5)).isEqualTo("FooDaxConn#commit"); - } - } - - @Test - void should_fail_to_commit() throws Exception { - willFailToCommitFooDaxConn = true; - - var base = new DaxBase() {}; - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - base.begin(); - - try { - var conn = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - var conn = base.getDaxConn("bar", BarDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - base.commit(); - fail(); - } catch (Err e) { - var r = DaxBase.FailToCommitDaxConn.class.cast(e.getReason()); - var e1 = r.errors().get("foo"); - var r1 = e1.getReason(); - assertThat(r1.getClass()).isEqualTo(FailToDoSomething.class); - } - - assertThat(logs).hasSize(4); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(2)).isEqualTo("FooDaxSrc#createDaxConn"); - assertThat(logs.get(3)).isEqualTo("BarDaxSrc#createDaxConn"); - } - - @Test - void should_rollback() throws Exception { - var base = new DaxBase() {}; - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - base.begin(); - - try { - var conn = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - var conn = base.getDaxConn("bar", BarDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - base.rollback(); - - assertThat(logs).hasSize(6); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(2)).isEqualTo("FooDaxSrc#createDaxConn"); - assertThat(logs.get(3)).isEqualTo("BarDaxSrc#createDaxConn"); - if (logs.get(4).equals("FooDaxConn#rollback")) { - assertThat(logs.get(4)).isEqualTo("FooDaxConn#rollback"); - assertThat(logs.get(5)).isEqualTo("BarDaxConn#rollback"); - } else { - assertThat(logs.get(4)).isEqualTo("BarDaxConn#rollback"); - assertThat(logs.get(5)).isEqualTo("FooDaxConn#rollback"); - } - } - - @Test - void should_end() throws Exception { - var base = new DaxBase() {}; - - try { - base.setUpLocalDaxSrc("foo", new FooDaxSrc()); - } catch (Err e) { - fail(e); - } - - try { - base.setUpLocalDaxSrc("bar", new BarDaxSrc()); - } catch (Err e) { - fail(e); - } - - base.begin(); - - try { - var conn = base.getDaxConn("foo", FooDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - var conn = base.getDaxConn("bar", BarDaxConn.class); - assertThat(conn).isNotNull(); - } catch (Err e) { - fail(e); - } - - try { - base.commit(); - } catch (Err e) { - fail(e); - } - - base.end(); - - base.freeAllLocalDaxSrcs(); - - assertThat(logs).hasSize(10); - assertThat(logs.get(0)).isEqualTo("FooDaxSrc#setUp"); - assertThat(logs.get(1)).isEqualTo("BarDaxSrc#setUp"); - assertThat(logs.get(2)).isEqualTo("FooDaxSrc#createDaxConn"); - assertThat(logs.get(3)).isEqualTo("BarDaxSrc#createDaxConn"); - if (logs.get(4).equals("FooDaxConn#commit")) { - assertThat(logs.get(4)).isEqualTo("FooDaxConn#commit"); - assertThat(logs.get(5)).isEqualTo("BarDaxConn#commit"); - } else { - assertThat(logs.get(4)).isEqualTo("BarDaxConn#commit"); - assertThat(logs.get(5)).isEqualTo("FooDaxConn#commit"); - } - if (logs.get(6).equals("FooDaxConn#close")) { - assertThat(logs.get(6)).isEqualTo("FooDaxConn#close"); - assertThat(logs.get(7)).isEqualTo("BarDaxConn#close"); - } else { - assertThat(logs.get(6)).isEqualTo("BarDaxConn#close"); - assertThat(logs.get(7)).isEqualTo("FooDaxConn#close"); - } - if (logs.get(8).equals("FooDaxSrc#end")) { - assertThat(logs.get(8)).isEqualTo("FooDaxSrc#end"); - assertThat(logs.get(9)).isEqualTo("BarDaxSrc#end"); - } else { - assertThat(logs.get(8)).isEqualTo("BarDaxSrc#end"); - assertThat(logs.get(9)).isEqualTo("FooDaxSrc#end"); - } - } - } - } - - @Nested - class TestDax { - @Test - void should_run_txn() throws Exception { - var hogeDs = new MapDaxSrc(); - var fugaDs = new MapDaxSrc(); - var piyoDs = new MapDaxSrc(); - - var base = new HogeFugaPiyoDaxBase(); - - try { - base.setUpLocalDaxSrc("hoge", hogeDs); - base.setUpLocalDaxSrc("fuga", fugaDs); - base.setUpLocalDaxSrc("piyo", piyoDs); - - hogeDs.dataMap.put("hogehoge", "Hello, world"); - - try { - Txn.run(base, new HogeFugaLogic()); - } catch (Err e) { - fail(e); - } - - try { - Txn.run(base, new FugaPiyoLogic()); - } catch (Err e) { - fail(e); - } - - assertThat(piyoDs.dataMap.get("piyopiyo")).isEqualTo("Hello, world"); - - } catch (Err e) { - fail(e); - } finally { - base.freeAllLocalDaxSrcs(); - } - } - } -} diff --git a/src/test/java/sabi/ErrHandlerTest.java b/src/test/java/sabi/ErrHandlerTest.java deleted file mode 100644 index 38ca39c..0000000 --- a/src/test/java/sabi/ErrHandlerTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package sabi; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.ArrayList; - -public class ErrHandlerTest { - - static final List syncLogger = new ArrayList<>(); - static final List asyncLogger = new ArrayList<>(); - - public record FailToDoSomething(String name) {} - - @Test - public void should_notify_that_errs_are_created() { - Err.addSyncErrHandler((err, occ) -> { - syncLogger.add(String.format("1. %s %s", - err.getReason().toString(), occ.toString())); - }); - Err.addSyncErrHandler((err, occ) -> { - syncLogger.add(String.format("2. %s %s", - err.getReason().toString(), occ.toString())); - }); - Err.addAsyncErrHandler((err, occ) -> { - asyncLogger.add(String.format("3. %s %s", - err.getReason().toString(), occ.toString())); - }); - Err.addAsyncErrHandler((err, occ) -> { - asyncLogger.add(String.format("4. %s %s", - err.getReason().toString(), occ.toString())); - }); - Err.fixErrCfgs(); - - try { - throw new Err(new FailToDoSomething("abc")); - } catch (Err e) { - assertThat(e.getReason()).isInstanceOf(FailToDoSomething.class); - - try { - Thread.sleep(100); - } catch (Exception e2) {} - - assertThat(syncLogger).hasSize(2); - assertThat(syncLogger.get(0)).startsWith( - "1. FailToDoSomething[name=abc] (ErrHandlerTest.java:38) "); - assertThat(syncLogger.get(1)).startsWith( - "2. FailToDoSomething[name=abc] (ErrHandlerTest.java:38) "); - - assertThat(asyncLogger).hasSize(2); - assertThat(asyncLogger.get(0)).startsWith( - "3. FailToDoSomething[name=abc] (ErrHandlerTest.java:38) "); - assertThat(asyncLogger.get(1)).startsWith( - "4. FailToDoSomething[name=abc] (ErrHandlerTest.java:38) "); - } - } -} diff --git a/src/test/java/sabi/ErrTest.java b/src/test/java/sabi/ErrTest.java deleted file mode 100644 index eaa6b05..0000000 --- a/src/test/java/sabi/ErrTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package sabi; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; -import org.junit.jupiter.api.Test; -import java.io.IOException; - -public class ErrTest { - - // error reasons - record FailToDoSomething() {} - record InvalidState(String name1) {} - record InvalidValue(String name2, int name3) {} - - @Test - void should_create_an_exception_with_reason() { - try { - throw new Err(new FailToDoSomething()); - } catch (Err e) { - assertThat(e.getReason()).isInstanceOf(FailToDoSomething.class); - assertThat(e.getSituation()).isEmpty(); - assertThat(e.get("name1")).isNull(); - assertThat(e.get("name2")).isNull(); - assertThat(e.get("name3")).isNull(); - assertThat(e.getCause()).isNull(); - assertThat(e.getMessage()).isEqualTo("{reason=FailToDoSomething}"); - assertThat(e.getClassName()).isEqualTo("sabi.ErrTest"); - assertThat(e.getMethodName()).isEqualTo( - "should_create_an_exception_with_reason"); - assertThat(e.getFileName()).isEqualTo("ErrTest.java"); - assertThat(e.getLineNumber()).isEqualTo(18); - } - } - - @Test - void should_create_an_exception_with_reason_and_cause() { - final var cause = new IOException("Message"); - try { - throw new Err(new FailToDoSomething(), cause); - } catch (Err e) { - assertThat(e.getReason()).isInstanceOf(FailToDoSomething.class); - assertThat(e.getSituation()).isEmpty(); - assertThat(e.get("name1")).isNull(); - assertThat(e.get("name2")).isNull(); - assertThat(e.get("name3")).isNull(); - assertThat(e.getCause()).isEqualTo(cause); - assertThat(e.getMessage()).isEqualTo( - "{reason=FailToDoSomething, cause=java.io.IOException: Message}"); - assertThat(e.getClassName()).isEqualTo("sabi.ErrTest"); - assertThat(e.getMethodName()).isEqualTo( - "should_create_an_exception_with_reason_and_cause"); - assertThat(e.getFileName()).isEqualTo("ErrTest.java"); - assertThat(e.getLineNumber()).isEqualTo(39); - } - } - - enum Param { - name1, - name2, - name3, - } - - @Test - void should_create_an_exception_with_reason_and_params() { - try { - throw new Err(new InvalidState("abc")); - } catch (Err e) { - assertThat(e.getReason()).isInstanceOf(InvalidState.class); - assertThat(e.getSituation()).hasSize(1) - .containsEntry("name1", "abc"); - assertThat(e.get(Param.name1)).isEqualTo("abc"); - assertThat(e.get(Param.name2)).isNull(); - assertThat(e.get(Param.name3)).isNull(); - assertThat(e.getCause()).isNull(); - assertThat(e.getMessage()).isEqualTo("{reason=InvalidState, name1=abc}"); - assertThat(e.getClassName()).isEqualTo("sabi.ErrTest"); - assertThat(e.getMethodName()).isEqualTo( - "should_create_an_exception_with_reason_and_params"); - assertThat(e.getFileName()).isEqualTo("ErrTest.java"); - assertThat(e.getLineNumber()).isEqualTo(66); - } - } - - @Test - void should_create_an_exception_with_reason_and_cause_and_params() { - var cause = new IOException("Message"); - try { - throw new Err(new InvalidValue("abc", 123), cause); - } catch (Err e) { - assertThat(e.getReason()).isInstanceOf(InvalidValue.class); - assertThat(e.getSituation()).hasSize(2) - .containsEntry("name2", "abc") - .containsEntry("name3", 123); - assertThat(e.get("name1")).isNull(); - assertThat(e.get("name2")).isEqualTo("abc"); - assertThat(e.get("name3")).isEqualTo(123); - assertThat(e.getCause()).isEqualTo(cause); - assertThat(e.getMessage()).isEqualTo( - "{reason=InvalidValue, name2=abc, name3=123, " + - "cause=java.io.IOException: Message}"); - assertThat(e.getClassName()).isEqualTo("sabi.ErrTest"); - assertThat(e.getMethodName()).isEqualTo( - "should_create_an_exception_with_reason_and_cause_and_params"); - assertThat(e.getFileName()).isEqualTo("ErrTest.java"); - assertThat(e.getLineNumber()).isEqualTo(88); - } - } - - @Test - void should_print_message_when_cause_is_Err() { - final var cause0 = new IOException("Message"); - try { - throw new Err(new InvalidValue("abc", 123), cause0); - } catch (Err cause1) { - try { - throw new Err(new InvalidState("def"), cause1); - } catch (Err e) { - assertThat(e.getReason()).isInstanceOf(InvalidState.class); - assertThat(e.getSituation()).hasSize(3) - .containsEntry("name1", "def") - .containsEntry("name2", "abc") - .containsEntry("name3", 123); - assertThat(e.get("name1")).isEqualTo("def"); - assertThat(e.get("name2")).isEqualTo("abc"); - assertThat(e.get("name3")).isEqualTo(123); - assertThat(e.getCause()).isEqualTo(cause1); - assertThat(e.getMessage()).isEqualTo( - "{reason=InvalidState, name1=def, cause=" + - "{reason=InvalidValue, name2=abc, name3=123, cause=" + - "java.io.IOException: Message}}"); - assertThat(e.getClassName()).isEqualTo("sabi.ErrTest"); - assertThat(e.getMethodName()).isEqualTo( - "should_print_message_when_cause_is_Err"); - assertThat(e.getFileName()).isEqualTo("ErrTest.java"); - assertThat(e.getLineNumber()).isEqualTo(116); - } - } - } - - @Test - void should_throw_NPE_if_reason_is_null() { - try { - new Err(null); - fail(); - } catch (NullPointerException e) { - assertThat(e).isNotNull(); - } - } - - @Test - void should_throw_NPE_if_reason_is_null_and_with_cause() { - var cause = new IOException("Message"); - try { - new Err(null, cause); - fail(); - } catch (NullPointerException e) { - assertThat(e).isNotNull(); - } - } -} diff --git a/src/test/java/sabi/TxnTest.java b/src/test/java/sabi/TxnTest.java deleted file mode 100644 index c844669..0000000 --- a/src/test/java/sabi/TxnTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package sabi; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; -import static sabi.DaxAuxForTest.*; -import static sabi.DaxDummyForTest.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.BeforeEach; - -public class TxnTest { - - @BeforeEach - void clear() throws Exception { - clearDaxBase(); - } - - static interface ABDax { - String getAData() throws Err; - void setBData(String data) throws Err; - } - - static interface AGetDax extends ABDax, Dax { - default String getAData() throws Err { - var conn = getDaxConn("aaa", ADaxConn.class); - var data = conn.aMap.get("a"); - return data; - } - } - - static interface BGetSetDax extends ABDax, Dax { - default String getBData() throws Err { - var conn = getDaxConn("bbb", BDaxConn.class); - var data = conn.bMap.get("b"); - return data; - } - - default void setBData(String data) throws Err { - var conn = getDaxConn("bbb", BDaxConn.class); - conn.bMap.put("b", data); - } - } - - static interface CSetDax extends ABDax, Dax { - default void setCData(String data) throws Err { - var conn = getDaxConn("ccc", CDaxConn.class); - conn.cMap.put("c", data); - } - } - - static class ABDaxBase extends DaxBase - implements ABDax, AGetDax, BGetSetDax, CSetDax {} - - @Test - void txnRun() throws Exception { - var base = new ABDaxBase(); - - var aDs = new ADaxSrc(); - var bDs = new BDaxSrc(); - - try { - base.setUpLocalDaxSrc("aaa", aDs); - base.setUpLocalDaxSrc("bbb", bDs); - - aDs.aMap.put("a", "hello"); - - Txn.run(base, (ABDax dax) -> { - var data = dax.getAData(); - data = data.toUpperCase(); - dax.setBData(data); - }); - - } catch (Err e) { - fail(e); - } - } -}