diff --git a/src/main/java/com/github/sttk/sabi/AsyncGroup.java b/src/main/java/com/github/sttk/sabi/AsyncGroup.java
index 46f266e..51ce56f 100644
--- a/src/main/java/com/github/sttk/sabi/AsyncGroup.java
+++ b/src/main/java/com/github/sttk/sabi/AsyncGroup.java
@@ -4,38 +4,43 @@
*/
package com.github.sttk.sabi;
+import com.github.sttk.sabi.internal.AsyncGroupImpl;
+
/**
- * An interface for asynchronously executing multiple {@link Runner} instances and waiting for their
- * completion.
+ * Manages asynchronous background tasks executed during data source and data connection lifecycle
+ * events.
+ *
+ *
An instance of {@code AsyncGroup} is passed to methods of {@link DataSrc} (such as {@link
+ * DataSrc#setup(AsyncGroup)}) and {@link DataConn} (such as {@link DataConn#commit(AsyncGroup)} and
+ * {@link DataConn#rollback(AsyncGroup)}). Implementations of data sources and data connections can
+ * register background asynchronous operations (such as parallel cleanup or pre-commit validations)
+ * using the {@link #add(Runner)} method.
*
- *
Implementations of this interface allow adding multiple {@link Runner} objects, which are then
- * executed concurrently. The group waits until all added runners have finished their execution. Any
- * errors occurring during the execution of a {@link Runner} are stored and can be retrieved by
- * their names in a map.
+ *
All registered {@link Runner} tasks are managed by this group and executed asynchronously. If
+ * any registered task fails, is interrupted, or throws an unhandled runtime exception,
+ * corresponding error records defined in this interface are produced to report the failure.
*/
-public sealed interface AsyncGroup permits com.github.sttk.sabi.internal.AsyncGroupImpl {
+public sealed interface AsyncGroup permits AsyncGroupImpl {
- /**
- * Represents the reason for a new {@link com.github.sttk.errs.Err} exception object when an
- * exception occurred during the execution of a {@link Runner} and the exception class was not the
- * {@link com.github.sttk.errs.Err}.
- */
+ /** Indicates that a registered {@link Runner} task failed during its execution. */
record RunnerFailed() {}
- /**
- * Represents the reason for an {@link com.github.sttk.errs.Err} exception object when the
- * creation of a thread for asynchronous execution of a {@link Runner} fails.
- */
+ /** Indicates that a registered {@link Runner} task execution was interrupted. */
record RunnerInterrupted() {}
- /** Represents an unexpected {@link RuntimeException} that occurred. */
- record RuntimeExceptionOccurred() {}
+ /**
+ * Indicates that an unhandled runtime exception was thrown during the execution of a registered
+ * {@link Runner} task.
+ */
+ record RuntimeExceptionOccured() {}
/**
- * Adds a {@link Runner} to this group for asynchronous execution. The added runner will be
- * executed in a separate thread.
+ * Adds a background task to be executed asynchronously by this group.
+ *
+ *
The task is encapsulated in a {@link Runner} functional interface and scheduled for parallel
+ * asynchronous execution.
*
- * @param runner The {@link Runner} to be added and executed asynchronously.
+ * @param runner the {@link Runner} task to be added and executed asynchronously
*/
- void add(final Runner runner);
+ void add(Runner runner);
}
diff --git a/src/main/java/com/github/sttk/sabi/DataAcc.java b/src/main/java/com/github/sttk/sabi/DataAcc.java
index 2a6855c..1e58c89 100644
--- a/src/main/java/com/github/sttk/sabi/DataAcc.java
+++ b/src/main/java/com/github/sttk/sabi/DataAcc.java
@@ -7,25 +7,29 @@
import com.github.sttk.errs.Err;
/**
- * An interface designed for implementing data access operations through default methods in its
- * sub-interfaces.
+ * Provides access to named {@link DataConn} data connection instances.
*
- *
Sub-interfaces of {@code DataAcc} are expected to define and implement data access methods as
- * default methods. Within these default methods, the connection to the underlying data store should
- * be obtained using the {@link #getDataConn(String, Class)} method provided by this interface. This
- * design promotes a clear separation of concerns, allowing data access logic to be encapsulated
- * within the interface itself.
+ *
This interface defines the contract for retrieving managed data connections registered under
+ * specific names within a data access scope (such as a {@link DataHub}). Application business logic
+ * uses this interface to obtain connection objects required to perform database or external service
+ * operations.
*/
public interface DataAcc {
+
/**
- * Retrieves a connection to a data store. This method is intended to be used by default methods
- * in sub-interfaces to obtain the necessary connection for performing data access operations.
+ * Retrieves a data connection associated with the specified data source name and casts it to the
+ * requested connection class type.
+ *
+ *
If a connection for the given {@code name} has already been created within the current
+ * transaction or execution scope, that connection instance is returned. Otherwise, a new
+ * connection is created via the corresponding registered data source.
*
- * @param The type of the data connection, which must extend {@link DataConn}.
- * @param name The name identifying the specific data connection to retrieve.
- * @param cls The {@link Class} object representing the type of the desired data connection.
- * @return A data connection object of the specified type.
- * @throws Err if an error occurs while obtaining the data connection.
+ * @param the expected type of {@link DataConn}
+ * @param name the registered logical name of the data source
+ * @param cls the {@link Class} representing the target connection type {@code C}
+ * @return the data connection instance associated with the specified name
+ * @throws Err if no data source with the specified name is found, if creating the connection
+ * fails or yields null, or if the connection cannot be cast to the target type
*/
C getDataConn(String name, Class cls) throws Err;
}
diff --git a/src/main/java/com/github/sttk/sabi/DataConn.java b/src/main/java/com/github/sttk/sabi/DataConn.java
index 6240219..e327093 100644
--- a/src/main/java/com/github/sttk/sabi/DataConn.java
+++ b/src/main/java/com/github/sttk/sabi/DataConn.java
@@ -5,78 +5,105 @@
package com.github.sttk.sabi;
import com.github.sttk.errs.Err;
+import java.util.List;
/**
- * The interface that abstracts a connection per session to an external data service, such as a
- * database, file system, or messaging service.
+ * Represents a data connection participating in transaction lifecycles managed by a {@link
+ * DataHub}.
*
- * Its primary purpose is to enable cohesive transaction operations across multiple external data
- * services within a single transaction context. Implementations of this interface provide the
- * concrete input/output operations for their respective data services.
- *
- *
Methods declared within this interface are designed to handle transactional logic. The
- * AsyncGroup parameter in various methods allows for asynchronous processing when commit or
- * rollback operations are time-consuming.
+ *
Implementations encapsulate connection state and operations for external resources such as
+ * relational databases, NoSQL stores, or web services. During a transaction, connections progress
+ * through pre-commit, commit, and post-commit phases, and support rollback and failure reporting
+ * when errors occur.
*/
public interface DataConn {
+
/**
- * Commits the changes made within the current session to the external data service. This method
- * is responsible for finalizing all operations performed since the last commit or rollback.
+ * Represents an error when one or more data connections fail during the pre-commit phase.
*
- * @param ag An {@link AsyncGroup} that can be used to perform asynchronous operations if the
- * commit process is time-consuming.
- * @throws Err if an error occurs during the commit operation.
+ * @param errors the list of error entries detailing connection names and failure causes
*/
- void commit(AsyncGroup ag) throws Err;
+ public record FailToPreCommitDataConn(List errors) {}
/**
- * Performs any necessary pre-commit operations. This method is called before the {@link
- * #commit(AsyncGroup)} method.
+ * Represents an error when one or more data connections fail during the commit phase.
*
- * @param ag An {@link AsyncGroup} that can be used for asynchronous pre-commit tasks.
- * @throws Err if an error occurs during the pre-commit operation.
+ * @param errors the list of error entries detailing connection names and failure causes
*/
- default void preCommit(AsyncGroup ag) throws Err {}
+ public record FailToCommitDataConn(List errors) {}
+
+ /**
+ * Represents an error when one or more data connections fail during the post-commit phase.
+ *
+ * @param errors the list of error entries detailing connection names and failure causes
+ */
+ public record FailToPostCommitDataConn(List errors) {}
+
+ ///
+
+ /**
+ * Commits changes made through this connection.
+ *
+ * Async background tasks may be registered to the provided {@link AsyncGroup} during commit
+ * processing.
+ *
+ * @param ag the asynchronous group for registering background tasks
+ * @throws Err if committing the transaction changes fails
+ */
+ void commit(AsyncGroup ag) throws Err;
/**
- * Performs any necessary post-commit operations. This method is called after the {@link
- * #commit(AsyncGroup)} method has successfully completed.
+ * Prepares this connection for commit prior to the main commit phase.
+ *
+ *
The default implementation does nothing. Subclasses can override this method to perform
+ * pre-commit checks or flush pending writes.
*
- * @param ag An {@link AsyncGroup} that can be used for asynchronous post-commit tasks.
+ * @param ag the asynchronous group for registering background tasks
+ * @throws Err if pre-commit preparation or validation fails
*/
- default void postCommit(AsyncGroup ag) {}
+ default void preCommit(AsyncGroup ag) throws Err {}
/**
- * Indicates whether a force-back operation should be performed. A force-back is a mechanism to
- * revert the committed changes when this connection had been already committed but the other
- * connection had failed.
+ * Performs post-commit operations after all connections in a transaction have successfully
+ * committed.
*
- * @return {@code true} if a force-back is required, {@code false} otherwise.
+ *
The default implementation does nothing. Subclasses can override this method to perform
+ * cleanup or trigger post-commit notifications.
+ *
+ * @param ag the asynchronous group for registering background tasks
+ * @throws Err if post-commit processing fails
*/
- default boolean shouldForceBack() {
- return false;
- }
+ default void postCommit(AsyncGroup ag) throws Err {}
/**
- * Rolls back the changes made within the current session, discarding all operations performed
- * since the last commit or rollback.
+ * Checks whether this connection has been successfully committed.
*
- * @param ag An {@link AsyncGroup} that can be used to perform asynchronous operations if the
- * rollback process is time-consuming.
+ * @return {@code true} if this connection was committed; {@code false} otherwise
*/
- void rollback(AsyncGroup ag);
+ boolean isCommitted();
/**
- * Performs a force-back operation to revert the committed changes when this connection had been
- * already committed but the other connection had failed.
+ * Rolls back changes made through this connection.
+ *
+ *
This method is invoked when a transaction fails and must restore the connection or
+ * underlying storage to its pre-transaction state.
*
- * @param ag An {@link AsyncGroup} that can be used for asynchronous force-back tasks.
+ * @param ag the asynchronous group for registering background tasks
+ * @throws Err if rolling back connection changes fails
*/
- default void forceBack(AsyncGroup ag) {}
+ void rollback(AsyncGroup ag) throws Err;
/**
- * Closes the connection to the external data service, releasing any associated resources. This
- * method should be called to ensure proper resource management.
+ * Notifies this connection of a transaction failure with detailed failure reports.
+ *
+ *
The default implementation does nothing. Implementations can inspect failure reports to log
+ * diagnostics or take recovery actions.
+ *
+ * @param ag the asynchronous group for registering background tasks
+ * @param reports the list of transaction failure reports
*/
+ default void onTxnFailure(AsyncGroup ag, List reports) {}
+
+ /** Closes and disposes of this data connection, releasing any held resources. */
void close();
}
diff --git a/src/main/java/com/github/sttk/sabi/DataHub.java b/src/main/java/com/github/sttk/sabi/DataHub.java
index e3f6908..9b22674 100644
--- a/src/main/java/com/github/sttk/sabi/DataHub.java
+++ b/src/main/java/com/github/sttk/sabi/DataHub.java
@@ -6,239 +6,212 @@
import com.github.sttk.errs.Err;
import com.github.sttk.sabi.internal.DataHubInner;
-import java.util.Map;
+import com.github.sttk.sabi.internal.TxnFailureReportBuilder;
+import java.util.ArrayList;
+import java.util.List;
/**
- * {@code DataHub} is a central component in the Sabi framework that manages {@link DataSrc} and
- * {@link DataConn} instances, facilitating data access and transaction management. It implements
- * both {@link DataAcc} for data access operations and {@link AutoCloseable} for resource
- * management.
+ * Manages local data sources and data connections, and executes business logic in transactional or
+ * non-transactional scopes.
*
- * This class allows for the registration and unregistration of local {@link DataSrc} objects,
- * and provides methods to execute application logic with or without transactional boundaries.
+ *
A {@code DataHub} acts as a container for local {@link DataSrc} instances and provides access
+ * to connections implementing {@link DataConn}. It supports executing logic functions through
+ * {@link #run(Logic)} for non-transactional operations and {@link #txn(Logic)} for transactional
+ * operations. In a transaction, connection commits, rollbacks, and failure reporting are handled
+ * automatically.
*/
public class DataHub implements DataAcc, AutoCloseable {
- /**
- * Represents an error reason that occurred when failing to set up global {@link DataSrc}
- * instances.
- *
- * @param errors A map containing the names of the data sources and the corresponding exceptions
- * that occurred during their setup.
- */
- public record FailToSetupGlobalDataSrcs(Map errors) {}
-
- /**
- * Represents an error reason that occurred when failing to set up local {@link DataSrc}
- * instances.
- *
- * @param errors A map containing the names of the data sources and the corresponding exceptions
- * that occurred during their setup.
- */
- public record FailToSetupLocalDataSrcs(Map errors) {}
/**
- * Represents an error reason that occurred when failing to commit one or more {@link DataConn}
- * instances.
+ * Represents an error when setting up global data sources fails.
*
- * @param errors A map containing the names of the data connections and the corresponding
- * exceptions that occurred during their commit.
+ * @param errors the list of error entries during global data source setup
*/
- public record FailToCommitDataConn(Map errors) {}
+ public record FailToSetupGlobalDataSrcs(List errors) {}
/**
- * Represents an error reason that occurred when failing to pre-commit one or more {@link
- * DataConn} instances.
+ * Represents an error when setting up local data sources fails.
*
- * @param errors A map containing the names of the data connections and the corresponding
- * exceptions that occurred during their pre-commit.
+ * @param errors the list of error entries during local data source setup
*/
- public record FailToPreCommitDataConn(Map errors) {}
+ public record FailToSetupLocalDataSrcs(List errors) {}
/**
- * Represents an error reason where no {@link DataSrc} was found to create a {@link DataConn} with
- * the specified name and type.
+ * Represents an error when no data source with the specified name is found.
*
- * @param name The name of the data source requested.
- * @param dataConnType The type of the data connection requested.
+ * @param name the logical name of the data source
+ * @param dataConnType the expected data connection class name
*/
public record NoDataSrcToCreateDataConn(String name, String dataConnType) {}
/**
- * Represents an error reason that occurred when failing to create a {@link DataConn} instance.
+ * Represents an error when creating a data connection fails.
*
- * @param name The name of the data source from which the connection was attempted.
- * @param dataConnType The type of the data connection that failed to be created.
+ * @param name the logical name of the data source
+ * @param dataConnType the expected data connection class name
*/
public record FailToCreateDataConn(String name, String dataConnType) {}
/**
- * Represents an error reason where the created {@link DataConn} instance was null.
+ * Represents an error when a created data connection instance is null.
*
- * @param name The name of the data source.
- * @param dataConnType The type of the data connection expected.
+ * @param name the logical name of the data source
+ * @param dataConnType the expected data connection class name
*/
public record CreatedDataConnIsNull(String name, String dataConnType) {}
/**
- * Represents an error reason that occurred when failing to cast a {@link DataConn} to the
- * requested type.
+ * Represents an error when a connection cannot be cast to the target connection type.
*
- * @param name The name of the data connection.
- * @param castToType The type to which the data connection was attempted to be cast.
+ * @param name the logical name of the data source
+ * @param fromDataConnType the actual connection class name
+ * @param toDataConnType the target connection class name
*/
- public record FailToCastDataConn(String name, String castToType) {}
+ public record FailToCastDataConn(String name, String fromDataConnType, String toDataConnType) {}
/**
- * Represents an unexpected {@link RuntimeException} that occurred during pre-commit or commit
- * operations.
+ * Represents an error when casting this {@code DataHub} instance to the generic data context
+ * fails.
+ *
+ * @param fromType the class name of this DataHub instance
*/
+ public record FailToCastDataHub(String fromType) {}
+
+ /** Represents an error when an unhandled runtime exception occurs during logic execution. */
public record RuntimeExceptionOccurred() {}
- /**
- * Represents an error reason that occurred when failing to cast the {@code DataHub} instance
- * itself to the expected data access interface type for a {@link Logic}.
- *
- * @param castFromType The actual type of the {@code DataHub} instance that failed to cast.
- */
- public record FailToCastDataHub(String castFromType) {}
+ ///
- private final DataHubInner inner = new DataHubInner();
+ private final DataHubInner inner;
- /** Constructs a new {@code DataHub} instance. */
- public DataHub() {}
+ /** Constructs a new, default {@code DataHub} instance. */
+ public DataHub() {
+ this.inner = new DataHubInner();
+ }
/**
- * Registers a local {@link DataSrc} with the specified name for use within this {@code DataHub}
- * instance. This allows specific data sources to be managed independently from globally
- * registered ones.
+ * Constructs a new {@code DataHub} instance configured to use specified global data sources.
*
- * @param name The unique name for the {@link DataSrc}.
- * @param ds The {@link DataSrc} instance to register.
+ * @param names the list of global data source names to bind to this hub
*/
- public void uses(String name, DataSrc ds) {
- inner.uses(name, ds);
+ public DataHub(List names) {
+ this.inner = new DataHubInner(names);
}
/**
- * Unregisters a local {@link DataSrc} with the given name from this {@code DataHub} instance.
+ * Constructs a new {@code DataHub} instance configured to use specified global data sources.
*
- * @param name The name of the {@link DataSrc} to unregister.
+ * @param names varargs array of global data source names to bind to this hub
*/
- public void disuses(String name) {
- inner.disuses(name);
+ public DataHub(String... names) {
+ this(List.of(names));
}
/**
- * Retrieves a {@link DataConn} instance from the managed data sources. This method is part of the
- * {@link DataAcc} interface implementation.
- *
- * @param The type of the {@link DataConn} to retrieve, which must extend {@link DataConn}.
- * @param name The name of the data source from which to get the connection.
- * @param cls The {@link Class} object representing the desired type of the data connection.
- * @return A {@link DataConn} instance of the specified type.
- * @throws Err if no data source is found, if the connection cannot be created, if the created
- * connection is null, or if the connection cannot be cast to the specified class.
+ * Registers a local {@link DataSrc} with a specific name in this hub.
+ *
+ * @param name the logical name for the data source
+ * @param ds the {@link DataSrc} instance to register
*/
- @Override
- public C getDataConn(String name, Class cls) throws Err {
- return inner.getDataConn(name, cls);
+ public void uses(String name, DataSrc ds) {
+ this.inner.useLocal(name, ds);
}
/**
- * Closes all {@link DataConn} instances managed by this {@code DataHub}, releasing their
- * resources. This method is part of the {@link AutoCloseable} interface and should be called to
- * ensure proper resource cleanup, ideally in a try-with-resources statement.
+ * Unregisters a local {@link DataSrc} associated with the given name from this hub.
+ *
+ * @param name the logical name of the data source to remove
*/
- @Override
- public void close() {
- inner.close();
- }
-
- void begin() throws Err {
- inner.begin();
- }
-
- void commit() throws Err {
- inner.commit();
+ public void disuses(String name) {
+ this.inner.disuseLocal(name);
}
- void rollback() {
- inner.rollback();
+ /** Closes all local data sources registered in this hub and releases their resources. */
+ @Override
+ public void close() {
+ this.inner.closeLocals();
}
- void end() {
- inner.end();
+ /**
+ * {@inheritDoc}
+ *
+ * @throws Err if the data source is not found, connection creation fails or returns null, or
+ * casting to {@code cls} fails
+ */
+ @Override
+ public C getDataConn(String name, Class cls) throws Err {
+ return this.inner.getDataConn(name, cls);
}
/**
- * Executes the provided application {@link Logic} without transactional boundaries. The {@code
- * DataHub} instance is treated as the data access object {@code D} to the {@link Logic}'s {@code
- * run} method.
- *
- * @param The type of the data access object, which typically is {@code DataHub} or an
- * interface implemented by {@code DataHub} that {@link Logic} expects.
- * @param logic The application logic to execute.
- * @throws Err if an {@link Err} or {@link RuntimeException} occurs during logic execution or if
- * the {@code DataHub} cannot be cast to the expected data access type.
+ * Executes business logic in a non-transactional scope.
+ *
+ * This method passes this hub instance (cast to type {@code D}) to {@link Logic#run(Object)}.
+ * Connections acquired during execution are managed within the scope.
+ *
+ * @param the type of data context expected by the logic, typically {@code DataHub} or a
+ * subclass/interface
+ * @param logic the business logic to execute
+ * @throws Err if the logic throws {@code Err}, if casting to {@code D} fails (wrapping {@link
+ * FailToCastDataHub}), or if an unhandled runtime exception occurs (wrapping {@link
+ * RuntimeExceptionOccurred})
*/
public void run(Logic logic) throws Err {
- D data;
try {
@SuppressWarnings("unchecked")
- D d = (D) this;
- data = d;
- } catch (Exception e) {
- throw new Err(new FailToCastDataHub(this.getClass().getName()));
- }
- try {
- this.begin();
+ D data = (D) this;
+
+ this.inner.begin();
logic.run(data);
- } catch (Err e) {
- throw e;
- } catch (RuntimeException e) {
- throw new Err(new RuntimeExceptionOccurred(), e);
+ } catch (Err err) {
+ throw err;
+ } catch (ClassCastException e) {
+ throw new Err(new FailToCastDataHub(this.getClass().getName()), e);
+ } catch (RuntimeException re) {
+ throw new Err(new RuntimeExceptionOccurred(), re);
} finally {
- this.end();
+ this.inner.end();
}
}
/**
- * Executes the provided application {@link Logic} within a transactional context. The {@code
- * DataHub} instance is treated as the data access object {@code D} to the {@link Logic}'s {@code
- * run} method. If the logic completes successfully, a commit operation is attempted. If any
- * {@link Err}, {@link RuntimeException}, or {@link Error} occurs, a rollback operation is
- * performed.
- *
- * @param The type of the data access object, which typically is {@code DataHub} or an
- * interface implemented by {@code DataHub} that {@link Logic} expects.
- * @param logic The application logic to execute transactionally.
- * @throws Err if an {@link Err}, {@link RuntimeException}, or {@link Error} occurs during logic
- * execution, pre-commit, or commit. The original exception is re-thrown after rollback.
+ * Executes business logic within a transactional boundary.
+ *
+ * Upon successful completion of {@link Logic#run(Object)}, all acquired data connections are
+ * committed. If an exception occurs, all connections are automatically rolled back, transaction
+ * failure reports are dispatched to connections, and the exception is rethrown wrapped in {@link
+ * Err}.
+ *
+ * @param the type of data context expected by the logic
+ * @param logic the business logic to execute transactionally
+ * @throws Err if the logic throws {@code Err}, connection commit or rollback fails, casting to
+ * {@code D} fails, or a runtime exception occurs
*/
public void txn(Logic logic) throws Err {
- D data;
+ final var reportBuilders = new ArrayList(0);
try {
- @SuppressWarnings("unchecked")
- D d = (D) this;
- data = d;
- } catch (Exception e) {
- throw new Err(new FailToCastDataHub(this.getClass().getName()));
- }
- try {
- this.begin();
- logic.run(data);
- this.commit();
- } catch (Err e) {
- this.rollback();
- throw e;
- } catch (RuntimeException e) {
- this.rollback();
- throw new Err(new RuntimeExceptionOccurred(), e);
- } catch (Error e) {
- this.rollback();
- throw e;
+ try {
+ @SuppressWarnings("unchecked")
+ D data = (D) this;
+
+ this.inner.begin();
+ logic.run(data);
+ } finally {
+ this.inner.prepareTxnFailureReportBuilders(reportBuilders);
+ }
+
+ this.inner.commit(reportBuilders);
+ } catch (Err err) {
+ this.inner.rollback(reportBuilders);
+ throw err;
+ } catch (ClassCastException e) {
+ this.inner.rollback(reportBuilders);
+ throw new Err(new FailToCastDataHub(this.getClass().getName()), e);
+ } catch (RuntimeException re) {
+ this.inner.rollback(reportBuilders);
+ throw new Err(new RuntimeExceptionOccurred(), re);
} finally {
- this.end();
+ this.inner.end();
}
}
}
diff --git a/src/main/java/com/github/sttk/sabi/DataSrc.java b/src/main/java/com/github/sttk/sabi/DataSrc.java
index 0067ba8..609bde4 100644
--- a/src/main/java/com/github/sttk/sabi/DataSrc.java
+++ b/src/main/java/com/github/sttk/sabi/DataSrc.java
@@ -7,38 +7,36 @@
import com.github.sttk.errs.Err;
/**
- * The interface that abstracts a data source responsible for managing connections to external data
- * services, such as databases, file systems, or messaging services.
+ * Represents a data source corresponding to an external data resource, serving as a factory for
+ * data connections or as a connection pool.
*
- * It receives configuration for connecting to an external data service and then creates and
- * supplies a {@link DataConn} instance, representing a single session connection.
+ *
Implementations encapsulate connection management, pool configuration, and resource allocation
+ * for specific data stores or external services (such as relational databases, HTTP clients, or
+ * caching services). Data sources can be registered globally via {@link Sabi#uses(String, DataSrc)}
+ * or bound locally to a {@link DataHub}.
*/
public interface DataSrc {
+
/**
- * Sets up the data source, performing any necessary initialization or configuration to establish
- * connectivity to the external data service. This method is typically called once at the
- * application startup.
+ * Sets up this data source.
+ *
+ *
This method is invoked during application startup or hub setup to prepare connections or
+ * connection pools. Asynchronous setup background tasks can be registered using the provided
+ * {@link AsyncGroup}.
*
- * @param ag An {@link AsyncGroup} that can be used for asynchronous setup operations, especially
- * if initialization is time-consuming.
- * @throws Err if an error occurs during the setup process.
+ * @param ag the asynchronous group for registering background tasks
+ * @throws Err if setting up the data source fails
*/
void setup(AsyncGroup ag) throws Err;
- /**
- * Closes the data source, releasing all resources and shutting down connections managed by this
- * source. This method should be called when the application is shutting down to ensure proper
- * resource cleanup.
- */
+ /** Closes this data source and disposes of any allocated resources or connection pools. */
void close();
/**
- * Creates and returns a new {@link DataConn} instance, representing a single session connection
- * to the external data service. Each call to this method should yield a new, independent
- * connection.
+ * Creates a new {@link DataConn} instance associated with this data source.
*
- * @return A new {@link DataConn} instance for a session.
- * @throws Err if an error occurs while creating the data connection.
+ * @return a new, initialized {@link DataConn} connection instance
+ * @throws Err if creating or establishing the data connection fails
*/
DataConn createDataConn() throws Err;
}
diff --git a/src/main/java/com/github/sttk/sabi/ErrEntry.java b/src/main/java/com/github/sttk/sabi/ErrEntry.java
new file mode 100644
index 0000000..3dd4d18
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/ErrEntry.java
@@ -0,0 +1,40 @@
+/*
+ * ErrEntry.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+import com.github.sttk.errs.Err;
+
+/**
+ * Represents an error entry containing details of a failure associated with a specific data source
+ * or data connection.
+ *
+ *
Instances of this class record the positional index, logical name, and cause of an error when
+ * batch operations (such as global/local data source setup or connection pre-commit, commit, and
+ * post-commit phases) encounter errors across multiple connections.
+ */
+public class ErrEntry {
+
+ /** The positional index of the data source or connection in the operation sequence. */
+ public final int index;
+
+ /** The logical name of the data source or connection where the error occurred. */
+ public final String name;
+
+ /** The error instance containing details of the failure cause. */
+ public final Err err;
+
+ /**
+ * Constructs a new {@code ErrEntry} with the specified index, name, and error cause.
+ *
+ * @param index the positional index of the data source or connection
+ * @param name the logical name of the data source or connection
+ * @param err the error instance detailing the failure cause
+ */
+ public ErrEntry(int index, String name, Err err) {
+ this.index = index;
+ this.name = name;
+ this.err = err;
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/Logic.java b/src/main/java/com/github/sttk/sabi/Logic.java
index 0ff1fce..0fa564b 100644
--- a/src/main/java/com/github/sttk/sabi/Logic.java
+++ b/src/main/java/com/github/sttk/sabi/Logic.java
@@ -7,27 +7,24 @@
import com.github.sttk.errs.Err;
/**
- * Represents the application's business logic, designed to separate data access concerns from the
- * core logic.
+ * Functional interface representing a unit of business logic executed within a {@link DataHub}
+ * scope.
*
- *
Implementations of this functional interface should focus solely on the business logic,
- * utilizing the provided {@code data} object of type {@code D} for all data access operations. The
- * {@link #run(Object)} method should not contain any direct data access code; instead, it should
- * delegate such operations to methods of the {@code D} object.
+ *
Instances of this interface are passed to {@link DataHub#run(Logic)} for non-transactional
+ * execution or {@link DataHub#txn(Logic)} for transactional execution. The provided data access
+ * interface {@code data} gives access to data connections and resources needed during execution.
*
- *
If an exceptional condition occurs during the execution of the logic, an {@link Err} object
- * should be thrown.
- *
- * @param The type of the data access object through which data operations are performed.
+ * @param the type of data access interface passed to the logic execution method
*/
@FunctionalInterface
public interface Logic {
+
/**
- * Executes the application's business logic. This method should implement the core logic, relying
- * on the {@code data} object for all data access needs.
+ * Executes the business logic using the provided data access interface.
*
- * @param data The data access object, providing methods for interacting with data.
- * @throws Err if an error or exceptional condition occurs during the logic execution.
+ * @param data the data access interface instance (typically a {@link DataHub} or custom data
+ * access interface)
+ * @throws Err if an error occurs during logic execution
*/
void run(D data) throws Err;
}
diff --git a/src/main/java/com/github/sttk/sabi/Runner.java b/src/main/java/com/github/sttk/sabi/Runner.java
index a75277e..eae7833 100644
--- a/src/main/java/com/github/sttk/sabi/Runner.java
+++ b/src/main/java/com/github/sttk/sabi/Runner.java
@@ -6,15 +6,20 @@
import com.github.sttk.errs.Err;
-/** {@code Runner} is the interface that runs any procedure. */
+/**
+ * Functional interface representing an asynchronous task to be executed by an {@link AsyncGroup}.
+ *
+ * Tasks implementing this interface are added to an {@link AsyncGroup} (via {@link
+ * AsyncGroup#add(Runner)}) during data source or data connection lifecycle events (such as setup,
+ * commit, or rollback) to perform parallel background processing.
+ */
@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.
+ * Runs the asynchronous background task.
*
- * @throws Err If this method failed.
+ * @throws Err if an error occurs during task execution
*/
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
index 17bd782..8e821dc 100644
--- a/src/main/java/com/github/sttk/sabi/Sabi.java
+++ b/src/main/java/com/github/sttk/sabi/Sabi.java
@@ -6,61 +6,58 @@
import com.github.sttk.errs.Err;
import com.github.sttk.sabi.internal.DataHubInner;
+import java.util.List;
/**
- * {@code Sabi} is the class that provides the static methods related to the global functionalities
- * of sabi framework.
+ * Utility class for managing and setting up global {@link DataSrc} instances.
*
- *
This class declares {@link #uses uses} method to register a {@link DataSrc} object used
- * globally with its name. And this class also declares {@link #setup setup} methods, which is the
- * static method to setup all global registered {@link DataSrc} objects.
- *
- *
The usages of these static methods is as follows:
- *
- *
public class Application {
- * static {
- * Sabi.uses("foo", new FooDataSrc());
- * Sabi.uses("bar", new BarDataSrc());
- * }
- * public static void main(String ...args) {
- * int exitCode = 0;
- * try (var ac = Sabi.setup()) {
- * ...
- * } catch (Exception e) {
- * exitCode = 1;
- * }
- * System.exit(exitCode);
- * }
- * }
+ * Applications can register global data sources using {@link #uses(String, DataSrc)} during
+ * startup, and then call {@link #setup()} or {@link #setup(String...)} to set them up. The returned
+ * {@link AutoCloseable} handles shutting down and closing global data sources when the application
+ * terminates or finishes using them.
*/
public final class Sabi {
private Sabi() {}
/**
- * Registers a {@link DataSrc} object with a unique name for global use within the Sabi framework.
- * This method should typically be called in a static initializer block of your application's main
- * class.
+ * Registers a global data source with the specified logical name.
*
- * @param name The unique name to associate with the {@link DataSrc}.
- * @param ds The {@link DataSrc} instance to be registered.
+ * @param name the logical name for the global data source
+ * @param ds the {@link DataSrc} instance to register
*/
public static void uses(String name, DataSrc ds) {
- DataHubInner.usesGlobal(name, ds);
+ DataHubInner.useGlobal(name, ds);
}
/**
- * Sets up all globally registered {@link DataSrc} objects. This involves calling the {@link
- * DataSrc#setup(AsyncGroup) setup} method on each registered data source. This method should be
- * called once at the application startup.
+ * Sets up all registered global data sources in their registration order.
*
- *
The returned {@link AutoCloseable} object can be used in a try-with-resources statement to
- * automatically invoke the close operations upon exiting the try block.
- *
- * @return An {@link AutoCloseable} object that, when closed, will trigger the global close
- * operation.
- * @throws Err if an error occurs during the setup of any {@link DataSrc}.
+ * @return an {@link AutoCloseable} that closes all initialized global data sources when closed
+ * @throws Err if setting up any global data source fails
*/
public static AutoCloseable setup() throws Err {
return DataHubInner.setupGlobals();
}
+
+ /**
+ * Sets up registered global data sources matching the specified names in the given order.
+ *
+ * @param names varargs array of global data source names to set up
+ * @return an {@link AutoCloseable} that closes the set-up global data sources when closed
+ * @throws Err if setting up any of the specified global data sources fails
+ */
+ public static AutoCloseable setup(String... names) throws Err {
+ return DataHubInner.setupGlobalsWithOrder(List.of(names));
+ }
+
+ /**
+ * Sets up registered global data sources matching the specified list of names in the given order.
+ *
+ * @param names list of global data source names to set up
+ * @return an {@link AutoCloseable} that closes the set-up global data sources when closed
+ * @throws Err if setting up any of the specified global data sources fails
+ */
+ public static AutoCloseable setup(List names) throws Err {
+ return DataHubInner.setupGlobalsWithOrder(names);
+ }
}
diff --git a/src/main/java/com/github/sttk/sabi/TxnFailureCause.java b/src/main/java/com/github/sttk/sabi/TxnFailureCause.java
new file mode 100644
index 0000000..b17c785
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/TxnFailureCause.java
@@ -0,0 +1,48 @@
+/*
+ * TxnFailureCause.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+import com.github.sttk.errs.Err;
+
+/**
+ * Represents the cause of a transaction failure, containing state information and the associated
+ * error.
+ *
+ * This class encapsulates the phase or status in which a transaction failure occurred (via
+ * {@link TxnFailureCauseState}) and the specific {@link Err} instance detailing the failure.
+ */
+public class TxnFailureCause {
+
+ /** The transaction failure cause state indicating the phase or status of the failure. */
+ public final TxnFailureCauseState state;
+
+ /** The error instance associated with the transaction failure, or {@code null} if none. */
+ public final Err err;
+
+ /**
+ * Constructs a new {@code TxnFailureCause} with the specified failure state and error.
+ *
+ * @param state the transaction failure cause state
+ * @param err the error associated with the failure
+ */
+ public TxnFailureCause(TxnFailureCauseState state, Err err) {
+ this.state = state;
+ this.err = err;
+ }
+
+ /**
+ * Returns a string representation of this transaction failure cause.
+ *
+ * @return a formatted string containing the state and error details
+ */
+ public String toString() {
+ var buf = new StringBuilder("{");
+ buf.append("State:").append(this.state);
+ buf.append(" ");
+ buf.append("Err:").append(this.err);
+ buf.append("}");
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/TxnFailureCauseState.java b/src/main/java/com/github/sttk/sabi/TxnFailureCauseState.java
new file mode 100644
index 0000000..42211cc
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/TxnFailureCauseState.java
@@ -0,0 +1,24 @@
+/*
+ * TxnFailureCause.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+/** Defines the states representing causes of transaction failures or successful states. */
+public enum TxnFailureCauseState {
+
+ /** Indicates no failure occurred and the connection was committed successfully. */
+ NoneByCommitted,
+
+ /** Indicates no failure occurred and the connection remained uncommitted. */
+ NoneByUncommitted,
+
+ /** Indicates that the transaction failed due to an error during logic execution. */
+ LogicFailure,
+
+ /** Indicates that the transaction failed during the commit phase of a connection. */
+ CommitFailure,
+
+ /** Indicates that the transaction failed during the post-commit phase of a connection. */
+ PostCommitFailure,
+}
diff --git a/src/main/java/com/github/sttk/sabi/TxnFailureRecovery.java b/src/main/java/com/github/sttk/sabi/TxnFailureRecovery.java
new file mode 100644
index 0000000..b8fe3db
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/TxnFailureRecovery.java
@@ -0,0 +1,30 @@
+/*
+ * TxnFailureRecovery.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+/** Defines recommended recovery actions to handle transaction failures. */
+public enum TxnFailureRecovery {
+
+ /** Indicates that no recovery action is required. */
+ NoActionRequired,
+
+ /** Indicates that the logic should be rerun and committed again without further changes. */
+ RerunLogicAndCommit,
+
+ /** Indicates that the root cause must be resolved before rerunning logic and committing. */
+ ResolveCauseThenRerunLogicAndCommit,
+
+ /** Indicates that the root cause must be resolved before rerunning the post-commit phase. */
+ ResolveCauseThenRerunPostCommit,
+
+ /** Indicates that both the root cause and any resulting data inconsistency must be resolved. */
+ ResolveCauseAndInconsistency,
+
+ /** Indicates an impossible state occurred that requires investigation. */
+ InvestigateBecauseImpossible,
+
+ /** Indicates that manual intervention is required to roll back changes. */
+ ManualRollbackRequired,
+}
diff --git a/src/main/java/com/github/sttk/sabi/TxnFailureReport.java b/src/main/java/com/github/sttk/sabi/TxnFailureReport.java
new file mode 100644
index 0000000..4c4101a
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/TxnFailureReport.java
@@ -0,0 +1,200 @@
+/*
+ * TxnFailureReport.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+/**
+ * Contains transaction failure diagnostic information and rollback status for a specific data
+ * connection.
+ *
+ *
Instances of this class are created during transaction failure processing in {@link DataHub}
+ * and passed to {@link DataConn#onTxnFailure(AsyncGroup, java.util.List)}. It details the
+ * connection's name, type, failure cause ({@link TxnFailureCause}), and rollback status ({@link
+ * TxnFailureRollback}), and provides methods to determine recommended recovery strategies.
+ */
+public class TxnFailureReport {
+
+ /** The logical name of the data connection. */
+ public final String dataConnName;
+
+ /** The class name or type of the data connection. */
+ public final String dataConnType;
+
+ /** The cause of the transaction failure associated with this connection. */
+ public final TxnFailureCause cause;
+
+ /** The rollback outcome associated with this connection. */
+ public final TxnFailureRollback rollback;
+
+ /**
+ * Constructs a new {@code TxnFailureReport} with the specified connection details, cause, and
+ * rollback outcome.
+ *
+ * @param name the logical name of the data connection
+ * @param type the class name or type of the data connection
+ * @param cause the transaction failure cause
+ * @param rollback the rollback outcome
+ */
+ public TxnFailureReport(
+ String name, String type, TxnFailureCause cause, TxnFailureRollback rollback) {
+ this.dataConnName = name;
+ this.dataConnType = type;
+ this.cause = cause;
+ this.rollback = rollback;
+ }
+
+ /**
+ * Returns a string representation of this transaction failure report.
+ *
+ * @return a formatted string containing connection details, cause, and rollback outcome
+ */
+ public String toString() {
+ var buf = new StringBuilder("{");
+ buf.append("dataConnName:").append(this.dataConnName);
+ buf.append(" ");
+ buf.append("dataConnType:").append(this.dataConnType);
+ buf.append(" ");
+ buf.append("cause:").append(this.cause);
+ buf.append(" ");
+ buf.append("rollback:").append(this.rollback);
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * Determines whether this connection was a direct cause of the transaction failure.
+ *
+ * @return {@code true} if this connection caused the failure; {@code false} if the cause state is
+ * {@code NoneByCommitted} or {@code NoneByUncommitted}
+ */
+ public boolean isCauseOfFailure() {
+ switch (this.cause.state) {
+ case NoneByCommitted:
+ case NoneByUncommitted:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Determines the recommended recovery strategy if the intent is to re-attempt and complete the
+ * transaction commit.
+ *
+ * @return the recommended {@link TxnFailureRecovery} action for committing
+ */
+ public TxnFailureRecovery recoveryForCommit() {
+ switch (this.cause.state) {
+ case NoneByUncommitted:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ case NoneByRolledBack:
+ return TxnFailureRecovery.RerunLogicAndCommit;
+ case RollbackFailure:
+ return TxnFailureRecovery.ResolveCauseAndInconsistency;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case NoneByCommitted:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.NoActionRequired;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case LogicFailure:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ case NoneByRolledBack:
+ return TxnFailureRecovery.ResolveCauseThenRerunLogicAndCommit;
+ case RollbackFailure:
+ return TxnFailureRecovery.ResolveCauseAndInconsistency;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case CommitFailure:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ case NoneByRolledBack:
+ return TxnFailureRecovery.ResolveCauseThenRerunLogicAndCommit;
+ case RollbackFailure:
+ return TxnFailureRecovery.ResolveCauseAndInconsistency;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case PostCommitFailure:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.ResolveCauseThenRerunPostCommit;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ }
+
+ /**
+ * Determines the recommended recovery strategy if the intent is to cancel and roll back the
+ * transaction.
+ *
+ * @return the recommended {@link TxnFailureRecovery} action for rolling back
+ */
+ public TxnFailureRecovery recoveryForRollback() {
+ switch (this.cause.state) {
+ case NoneByUncommitted:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ case NoneByRolledBack:
+ return TxnFailureRecovery.NoActionRequired;
+ case RollbackFailure:
+ return TxnFailureRecovery.ResolveCauseAndInconsistency;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case NoneByCommitted:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.ManualRollbackRequired;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case LogicFailure:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ case NoneByRolledBack:
+ return TxnFailureRecovery.NoActionRequired;
+ case RollbackFailure:
+ return TxnFailureRecovery.ResolveCauseAndInconsistency;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case CommitFailure:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ case NoneByRolledBack:
+ return TxnFailureRecovery.NoActionRequired;
+ case RollbackFailure:
+ return TxnFailureRecovery.ResolveCauseAndInconsistency;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ case PostCommitFailure:
+ switch (this.rollback.state) {
+ case NoneByNotRolledBack:
+ return TxnFailureRecovery.ManualRollbackRequired;
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ default:
+ return TxnFailureRecovery.InvestigateBecauseImpossible;
+ }
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/TxnFailureRollback.java b/src/main/java/com/github/sttk/sabi/TxnFailureRollback.java
new file mode 100644
index 0000000..64b776a
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/TxnFailureRollback.java
@@ -0,0 +1,47 @@
+/*
+ * TxnFailureRollback.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+import com.github.sttk.errs.Err;
+
+/**
+ * Represents the rollback outcome of a data connection during transaction failure processing.
+ *
+ *
This class encapsulates the state of the rollback (via {@link TxnFailureRollbackState}) and
+ * any error ({@link Err}) encountered when attempting to roll back the connection.
+ */
+public class TxnFailureRollback {
+
+ /** The transaction failure rollback state indicating the outcome of the rollback. */
+ public final TxnFailureRollbackState state;
+
+ /** The error instance associated with a rollback failure, or {@code null} if none. */
+ public final Err err;
+
+ /**
+ * Constructs a new {@code TxnFailureRollback} with the specified rollback state and error.
+ *
+ * @param state the transaction failure rollback state
+ * @param err the error associated with the rollback failure, or {@code null}
+ */
+ public TxnFailureRollback(TxnFailureRollbackState state, Err err) {
+ this.state = state;
+ this.err = err;
+ }
+
+ /**
+ * Returns a string representation of this transaction failure rollback outcome.
+ *
+ * @return a formatted string containing the rollback state and error details
+ */
+ public String toString() {
+ var buf = new StringBuilder("{");
+ buf.append("State:").append(this.state);
+ buf.append(" ");
+ buf.append("Err:").append(this.err);
+ buf.append("}");
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/TxnFailureRollbackState.java b/src/main/java/com/github/sttk/sabi/TxnFailureRollbackState.java
new file mode 100644
index 0000000..c37ea80
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/TxnFailureRollbackState.java
@@ -0,0 +1,18 @@
+/*
+ * TxnFailureRollbackState.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi;
+
+/** Defines the states representing outcomes of transaction rollback attempts. */
+public enum TxnFailureRollbackState {
+
+ /** Indicates that the connection was successfully rolled back without error. */
+ NoneByRolledBack,
+
+ /** Indicates that no rollback was performed on the connection. */
+ NoneByNotRolledBack,
+
+ /** Indicates that an error occurred while attempting to roll back the connection. */
+ RollbackFailure,
+}
diff --git a/src/main/java/com/github/sttk/sabi/internal/AsyncGroupImpl.java b/src/main/java/com/github/sttk/sabi/internal/AsyncGroupImpl.java
index 10fa9a3..1f74c9a 100644
--- a/src/main/java/com/github/sttk/sabi/internal/AsyncGroupImpl.java
+++ b/src/main/java/com/github/sttk/sabi/internal/AsyncGroupImpl.java
@@ -4,107 +4,82 @@
*/
package com.github.sttk.sabi.internal;
+import static com.github.sttk.sabi.AsyncGroup.RunnerInterrupted;
+
import com.github.sttk.errs.Err;
import com.github.sttk.sabi.AsyncGroup;
+import com.github.sttk.sabi.ErrEntry;
import com.github.sttk.sabi.Runner;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
public final class AsyncGroupImpl implements AsyncGroup {
- private ErrEntry errHead;
- private ErrEntry errLast;
+ private List eeList = new ArrayList<>();
private VthEntry vthHead;
private VthEntry vthLast;
- String name;
+ int _index;
+ String _name;
- public AsyncGroupImpl() {}
+ AsyncGroupImpl() {}
@Override
- public void add(final Runner runner) {
- final var name = this.name;
+ public void add(Runner runner) {
+ var index = this._index;
+ var name = this._name;
var vth =
Thread.ofVirtual()
.start(
() -> {
try {
runner.run();
- } catch (Err e) {
- addErr(name, e);
+ } catch (Err err) {
+ addErr(index, name, err);
} catch (RuntimeException e) {
- addErr(name, new Err(new AsyncGroup.RuntimeExceptionOccurred(), e));
+ addErr(index, name, e);
}
});
- var ent = new VthEntry(name, vth);
+ var ve = new VthEntry(index, name, vth);
if (this.vthLast == null) {
- this.vthHead = ent;
- this.vthLast = ent;
+ this.vthHead = ve;
+ this.vthLast = ve;
} else {
- this.vthLast.next = ent;
- this.vthLast = ent;
+ this.vthLast.next = ve;
+ this.vthLast = ve;
}
}
- synchronized void addErr(String name, Err err) {
- var ent = new ErrEntry(name, err);
-
- if (this.errLast == null) {
- this.errHead = ent;
- this.errLast = ent;
- } else {
- this.errLast.next = ent;
- this.errLast = ent;
- }
+ synchronized void addErr(int index, String name, Err err) {
+ var ee = new ErrEntry(index, name, err);
+ this.eeList.add(ee);
}
- void joinAndPutErrsInto(Map errMap) {
- for (var ent = this.vthHead; ent != null; ent = ent.next) {
- try {
- ent.thread.join();
- } catch (InterruptedException e) {
- addErr(ent.name, new Err(new RunnerInterrupted(), e));
- }
- }
- for (var ent = this.errHead; ent != null; ent = ent.next) {
- errMap.put(ent.name, ent.err);
- }
- clear();
+ synchronized void addErr(int index, String name, RuntimeException e) {
+ var err = new Err(new AsyncGroup.RuntimeExceptionOccured(), e);
+ var ee = new ErrEntry(index, name, err);
+ this.eeList.add(ee);
}
- void joinAndIgnoreErrs() {
- for (var ent = this.vthHead; ent != null; ent = ent.next) {
+ List join() {
+ for (var ve = this.vthHead; ve != null; ve = ve.next) {
try {
- ent.thread.join();
+ ve.thread.join();
} catch (InterruptedException e) {
+ addErr(ve.index, ve.name, new Err(new RunnerInterrupted(), e));
}
}
- clear();
- }
-
- void clear() {
- this.errHead = null;
- this.errLast = null;
- this.vthHead = null;
- this.vthLast = null;
- }
-}
-
-class ErrEntry {
- final String name;
- final Err err;
- ErrEntry next;
-
- ErrEntry(String name, Err err) {
- this.name = name;
- this.err = err;
+ return this.eeList;
}
}
class VthEntry {
+ final int index;
final String name;
final Thread thread;
VthEntry next;
- VthEntry(String name, Thread thread) {
+ VthEntry(int index, String name, Thread thread) {
+ this.index = index;
this.name = name;
this.thread = thread;
}
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataConnContainer.java b/src/main/java/com/github/sttk/sabi/internal/DataConnContainer.java
index e3bc666..ac2eed9 100644
--- a/src/main/java/com/github/sttk/sabi/internal/DataConnContainer.java
+++ b/src/main/java/com/github/sttk/sabi/internal/DataConnContainer.java
@@ -1,16 +1,14 @@
/*
* DataConnContainer.java
- * Copyright (C) 2025-2026 Takayuki Sato. All Rights Reserved.
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
*/
package com.github.sttk.sabi.internal;
import com.github.sttk.sabi.DataConn;
-public final class DataConnContainer {
- DataConnContainer prev;
- DataConnContainer next;
- String name;
- DataConn conn;
+public class DataConnContainer {
+ final String name;
+ final DataConn conn;
DataConnContainer(String name, DataConn conn) {
this.name = name;
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataConnList.java b/src/main/java/com/github/sttk/sabi/internal/DataConnList.java
deleted file mode 100644
index c897da5..0000000
--- a/src/main/java/com/github/sttk/sabi/internal/DataConnList.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * DataConnList.java
- * Copyright (C) 2025-2026 Takayuki Sato. All Rights Reserved.
- */
-package com.github.sttk.sabi.internal;
-
-public final class DataConnList {
- DataConnContainer head;
- DataConnContainer last;
-
- public DataConnList() {}
-
- void appendContainer(DataConnContainer ptr) {
- ptr.next = null;
-
- if (this.last == null) {
- this.head = ptr;
- this.last = ptr;
- ptr.prev = null;
- } else {
- this.last.next = ptr;
- ptr.prev = this.last;
- this.last = ptr;
- }
- }
-
- void closeDataConns() {
- var ptr = this.last;
- while (ptr != null) {
- ptr.conn.close();
- ptr = ptr.prev;
- }
- this.head = null;
- this.last = null;
- }
-}
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataConnManager.java b/src/main/java/com/github/sttk/sabi/internal/DataConnManager.java
new file mode 100644
index 0000000..fe28af1
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/internal/DataConnManager.java
@@ -0,0 +1,219 @@
+/*
+ * DataConnManager.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi.internal;
+
+import static com.github.sttk.sabi.DataConn.FailToCommitDataConn;
+import static com.github.sttk.sabi.DataConn.FailToPostCommitDataConn;
+import static com.github.sttk.sabi.DataConn.FailToPreCommitDataConn;
+
+import com.github.sttk.errs.Err;
+import com.github.sttk.sabi.TxnFailureCause;
+import com.github.sttk.sabi.TxnFailureCauseState;
+import com.github.sttk.sabi.TxnFailureRollback;
+import com.github.sttk.sabi.TxnFailureRollbackState;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DataConnManager {
+ final ArrayList list;
+ final Map indexMap;
+ boolean committed;
+
+ DataConnManager() {
+ this.list = new ArrayList<>();
+ this.indexMap = new HashMap<>();
+ }
+
+ DataConnManager(List names) {
+ this.list = new ArrayList<>(names.size());
+ this.indexMap = new HashMap<>(names.size());
+
+ // Because earlier ones take precedence when names overlap
+ for (int i = names.size() - 1; i >= 0; i--) {
+ this.indexMap.put(names.get(i), i);
+ this.list.add(new DataConnContainer(names.get(i), null));
+ }
+ }
+
+ void add(DataConnContainer cont) {
+ var idx = this.indexMap.get(cont.name);
+ if (idx != null) {
+ // Because earlier ones take precedence when names overlap
+ if (this.list.get(idx).conn == null) {
+ this.list.set(idx, cont);
+ }
+ } else {
+ this.indexMap.put(cont.name, this.list.size());
+ this.list.add(cont);
+ }
+ }
+
+ void prepareTxnFailureReportBuilders(ArrayList list) {
+ list.ensureCapacity(this.indexMap.size());
+ for (var cont : this.list) {
+ if (cont.conn != null) {
+ list.add(new TxnFailureReportBuilder(cont.name, cont.conn.getClass()));
+ }
+ }
+ }
+
+ void commit(List builders) throws Err {
+ var ag = new AsyncGroupImpl();
+ int ii = 0;
+ for (var cont : this.list) {
+ if (cont.conn == null) {
+ continue;
+ }
+ ag._name = cont.name;
+ ag._index = ii;
+ ii++;
+ try {
+ cont.conn.preCommit(ag);
+ } catch (Err err) {
+ ag.addErr(ag._index, ag._name, err);
+ break;
+ } catch (RuntimeException re) {
+ ag.addErr(ag._index, ag._name, re);
+ break;
+ }
+ }
+ var errors = ag.join();
+
+ if (!errors.isEmpty()) {
+ for (var ee : errors) {
+ int idx = ee.index;
+ builders.get(idx).cause = new TxnFailureCause(TxnFailureCauseState.LogicFailure, ee.err);
+ }
+ throw new Err(new FailToPreCommitDataConn(errors));
+ }
+
+ ag = new AsyncGroupImpl();
+ ii = 0;
+ for (var cont : this.list) {
+ if (cont.conn == null) {
+ continue;
+ }
+ ag._name = cont.name;
+ ag._index = ii;
+ ii++;
+ if (!cont.conn.isCommitted()) {
+ try {
+ cont.conn.commit(ag);
+ } catch (Err err) {
+ ag.addErr(ag._index, ag._name, err);
+ break;
+ } catch (RuntimeException re) {
+ ag.addErr(ag._index, ag._name, re);
+ break;
+ }
+ }
+ }
+ errors = ag.join();
+
+ if (!errors.isEmpty()) {
+ for (var ee : errors) {
+ int idx = ee.index;
+ builders.get(idx).cause = new TxnFailureCause(TxnFailureCauseState.CommitFailure, ee.err);
+ }
+ throw new Err(new FailToCommitDataConn(errors));
+ }
+
+ this.committed = true;
+
+ ag = new AsyncGroupImpl();
+ ii = 0;
+ for (var cont : this.list) {
+ if (cont.conn == null) {
+ continue;
+ }
+ ag._name = cont.name;
+ ag._index = ii;
+ ii++;
+ try {
+ cont.conn.postCommit(ag);
+ } catch (Err err) {
+ ag.addErr(ag._index, ag._name, err);
+ // don't break
+ } catch (RuntimeException re) {
+ ag.addErr(ag._index, ag._name, re);
+ // don't break
+ }
+ }
+ errors = ag.join();
+
+ if (!errors.isEmpty()) {
+ for (var ee : errors) {
+ int idx = ee.index;
+ builders.get(idx).cause =
+ new TxnFailureCause(TxnFailureCauseState.PostCommitFailure, ee.err);
+ }
+ throw new Err(new FailToPostCommitDataConn(errors));
+ }
+ }
+
+ void rollback(List builders) {
+ var ag = new AsyncGroupImpl();
+ int ii = 0;
+ for (var cont : this.list) {
+ if (cont.conn == null) {
+ continue;
+ }
+ ag._name = cont.name;
+ ag._index = ii;
+ ii++;
+ if (cont.conn.isCommitted()) {
+ if (builders.get(ag._index).cause.state == TxnFailureCauseState.NoneByUncommitted) {
+ builders.get(ag._index).cause =
+ new TxnFailureCause(TxnFailureCauseState.NoneByCommitted, null);
+ }
+ continue;
+ }
+ if (this.committed) {
+ continue;
+ }
+ try {
+ cont.conn.rollback(ag);
+ builders.get(ag._index).rollback =
+ new TxnFailureRollback(TxnFailureRollbackState.NoneByRolledBack, null);
+ } catch (Err err) {
+ ag.addErr(ag._index, ag._name, err);
+ } catch (RuntimeException re) {
+ ag.addErr(ag._index, ag._name, re);
+ }
+ }
+ var errors = ag.join();
+
+ if (!errors.isEmpty()) {
+ for (var ee : errors) {
+ int idx = ee.index;
+ builders.get(idx).rollback =
+ new TxnFailureRollback(TxnFailureRollbackState.RollbackFailure, ee.err);
+ }
+ }
+
+ ag = new AsyncGroupImpl();
+ for (var cont : this.list) {
+ if (cont.conn != null) {
+ var reports = builders.stream().map(TxnFailureReportBuilder::build).toList();
+ cont.conn.onTxnFailure(ag, reports);
+ }
+ }
+ ag.join();
+ }
+
+ void close() {
+ this.indexMap.clear();
+
+ for (int i = this.list.size() - 1; i >= 0; i--) {
+ var cont = this.list.get(i);
+ if (cont.conn != null) {
+ cont.conn.close();
+ }
+ }
+ this.list.clear();
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java b/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java
index 6fea032..704c79b 100644
--- a/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java
+++ b/src/main/java/com/github/sttk/sabi/internal/DataHubInner.java
@@ -4,32 +4,50 @@
*/
package com.github.sttk.sabi.internal;
+import static com.github.sttk.sabi.DataHub.CreatedDataConnIsNull;
+import static com.github.sttk.sabi.DataHub.FailToCastDataConn;
+import static com.github.sttk.sabi.DataHub.FailToCreateDataConn;
+import static com.github.sttk.sabi.DataHub.FailToSetupGlobalDataSrcs;
+import static com.github.sttk.sabi.DataHub.FailToSetupLocalDataSrcs;
+import static com.github.sttk.sabi.DataHub.NoDataSrcToCreateDataConn;
+
import com.github.sttk.errs.Err;
import com.github.sttk.sabi.DataConn;
-import com.github.sttk.sabi.DataHub;
import com.github.sttk.sabi.DataSrc;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
public final class DataHubInner {
-
- static final DataSrcList GLOBAL_DATA_SRC_LIST = new DataSrcList(false);
+ static final DataSrcManager GLOBAL_DATA_SRC_MANAGER = new DataSrcManager(false);
static final AtomicBoolean GLOBAL_DATA_SRCS_FIXED = new AtomicBoolean(false);
- public static void usesGlobal(String name, DataSrc ds) {
+ public static void useGlobal(String name, DataSrc ds) {
if (!GLOBAL_DATA_SRCS_FIXED.get()) {
- GLOBAL_DATA_SRC_LIST.addDataSrc(name, ds);
+ GLOBAL_DATA_SRC_MANAGER.add(name, ds);
}
}
public static AutoCloseable setupGlobals() throws Err {
if (GLOBAL_DATA_SRCS_FIXED.compareAndSet(false, true)) {
- var errMap = GLOBAL_DATA_SRC_LIST.setupDataSrcs();
- if (!errMap.isEmpty()) {
- GLOBAL_DATA_SRC_LIST.closeDataSrcs();
- throw new Err(new DataHub.FailToSetupGlobalDataSrcs(errMap));
+ var errors = GLOBAL_DATA_SRC_MANAGER.setup();
+ if (!errors.isEmpty()) {
+ GLOBAL_DATA_SRC_MANAGER.close();
+ throw new Err(new FailToSetupGlobalDataSrcs(errors));
+ }
+ }
+ return new AutoShutdown();
+ }
+
+ public static AutoCloseable setupGlobalsWithOrder(List names) throws Err {
+ if (GLOBAL_DATA_SRCS_FIXED.compareAndSet(false, true)) {
+ var errors = GLOBAL_DATA_SRC_MANAGER.setupWithOrder(names);
+ if (!errors.isEmpty()) {
+ GLOBAL_DATA_SRC_MANAGER.close();
+ throw new Err(new FailToSetupGlobalDataSrcs(errors));
}
}
return new AutoShutdown();
@@ -38,176 +56,139 @@ public static AutoCloseable setupGlobals() throws Err {
static class AutoShutdown implements AutoCloseable {
@Override
public void close() {
- GLOBAL_DATA_SRC_LIST.closeDataSrcs();
+ GLOBAL_DATA_SRC_MANAGER.close();
}
}
- final DataSrcList localDataSrcList = new DataSrcList(true);
- final Map dataSrcMap = new HashMap<>();
- final DataConnList dataConnList = new DataConnList();
- final Map dataConnMap = new HashMap<>();
- boolean fixed = false;
+ final DataSrcManager localDataSrcManager;
+ final Map dataSrcMap;
+ final DataConnManager dataConnManager;
+ final Map dataConnMap;
+ boolean fixed;
public DataHubInner() {
GLOBAL_DATA_SRCS_FIXED.compareAndSet(false, true);
- GLOBAL_DATA_SRC_LIST.copyContainerPtrsDidSetupInto(this.dataSrcMap);
+ this.fixed = false;
+
+ this.localDataSrcManager = new DataSrcManager(true);
+ this.dataSrcMap = new HashMap<>();
+ this.dataConnManager = new DataConnManager();
+ this.dataConnMap = new HashMap<>();
+
+ GLOBAL_DATA_SRC_MANAGER.copyDsReadyToMap(this.dataSrcMap);
}
- public void uses(String name, DataSrc ds) {
+ public DataHubInner(List names) {
+ GLOBAL_DATA_SRCS_FIXED.compareAndSet(false, true);
+ this.fixed = false;
+
+ this.localDataSrcManager = new DataSrcManager(true);
+ this.dataSrcMap = new HashMap<>();
+ this.dataConnManager = new DataConnManager(names);
+ this.dataConnMap = new HashMap<>();
+
+ GLOBAL_DATA_SRC_MANAGER.copyDsReadyToMap(this.dataSrcMap);
+ }
+
+ public void useLocal(String name, DataSrc ds) {
if (this.fixed) {
return;
}
- this.localDataSrcList.addDataSrc(name, ds);
+ this.localDataSrcManager.add(name, ds);
}
- public void disuses(String name) {
+ public void disuseLocal(String name) {
if (this.fixed) {
return;
}
- var ptr = this.dataSrcMap.get(name);
- if (ptr != null && ptr.local && Objects.equals(ptr.name, name)) {
+ var cont = this.dataSrcMap.get(name);
+ if (cont != null && cont.local && Objects.equals(cont.name, name)) {
this.dataSrcMap.remove(name);
}
- this.localDataSrcList.removeAndCloseContainerPtrDidSetupByName(name);
- this.localDataSrcList.removeAndCloseContainerPtrNotSetupByName(name);
+ this.localDataSrcManager.remove(name);
}
- public void close() {
+ public void closeLocals() {
this.dataConnMap.clear();
- this.dataConnList.closeDataConns();
+ this.dataConnManager.close();
this.dataSrcMap.clear();
- this.localDataSrcList.closeDataSrcs();
+ this.localDataSrcManager.close();
}
public void begin() throws Err {
this.fixed = true;
- var errMap = this.localDataSrcList.setupDataSrcs();
- this.localDataSrcList.copyContainerPtrsDidSetupInto(this.dataSrcMap);
+ var errors = this.localDataSrcManager.setup();
+ this.localDataSrcManager.copyDsReadyToMap(this.dataSrcMap);
- if (!errMap.isEmpty()) {
- throw new Err(new DataHub.FailToSetupLocalDataSrcs(errMap));
+ if (!errors.isEmpty()) {
+ throw new Err(new FailToSetupLocalDataSrcs(errors));
}
}
- public void commit() throws Err {
- var errMap = new HashMap();
-
- var ag = new AsyncGroupImpl();
- var ptr = this.dataConnList.head;
- while (ptr != null) {
- ag.name = ptr.name;
- try {
- ptr.conn.preCommit(ag);
- } catch (Err e) {
- errMap.put(ptr.name, e);
- break;
- } catch (RuntimeException e) {
- errMap.put(ptr.name, new Err(new DataHub.RuntimeExceptionOccurred(), e));
- break;
- }
- ptr = ptr.next;
- }
- ag.joinAndPutErrsInto(errMap);
-
- if (!errMap.isEmpty()) {
- throw new Err(new DataHub.FailToPreCommitDataConn(errMap));
- }
-
- ag = new AsyncGroupImpl();
- ptr = this.dataConnList.head;
- while (ptr != null) {
- ag.name = ptr.name;
- try {
- ptr.conn.commit(ag);
- } catch (Err e) {
- errMap.put(ptr.name, e);
- break;
- } catch (RuntimeException e) {
- errMap.put(ptr.name, new Err(new DataHub.RuntimeExceptionOccurred(), e));
- break;
- }
- ptr = ptr.next;
- }
- ag.joinAndPutErrsInto(errMap);
-
- if (!errMap.isEmpty()) {
- throw new Err(new DataHub.FailToCommitDataConn(errMap));
- }
-
- ag = new AsyncGroupImpl();
- ptr = this.dataConnList.head;
- while (ptr != null) {
- ag.name = ptr.name;
- ptr.conn.postCommit(ag);
- ptr = ptr.next;
- }
-
- ag.joinAndIgnoreErrs();
+ public void prepareTxnFailureReportBuilders(ArrayList list) {
+ this.dataConnManager.prepareTxnFailureReportBuilders(list);
}
- public void rollback() {
- var ag = new AsyncGroupImpl();
- var ptr = this.dataConnList.head;
- while (ptr != null) {
- ag.name = ptr.name;
- if (ptr.conn.shouldForceBack()) {
- ptr.conn.forceBack(ag);
- } else {
- ptr.conn.rollback(ag);
- }
- ptr = ptr.next;
- }
+ public void commit(List builders) throws Err {
+ this.dataConnManager.commit(builders);
+ }
- ag.joinAndIgnoreErrs();
+ public void rollback(List builders) throws Err {
+ this.dataConnManager.rollback(builders);
}
public void end() {
this.dataConnMap.clear();
- this.dataConnList.closeDataConns();
+ this.dataConnManager.close();
+
this.fixed = false;
}
public C getDataConn(String name, Class cls) throws Err {
- var connPtr = this.dataConnMap.get(name);
- if (connPtr != null) {
+ var dcCont = this.dataConnMap.get(name);
+ if (dcCont != null && dcCont.conn != null) {
try {
- return cls.cast(connPtr.conn);
+ return cls.cast(dcCont.conn);
} catch (Exception e) {
- throw new Err(new DataHub.FailToCastDataConn(name, cls.getName()), e);
+ String fromType = dcCont.conn.getClass().getName();
+ String toType = cls.getName();
+ throw new Err(new FailToCastDataConn(name, fromType, toType), e);
}
}
- var dsPtr = this.dataSrcMap.get(name);
- if (dsPtr == null) {
- throw new Err(new DataHub.NoDataSrcToCreateDataConn(name, cls.getName()));
+ var dsCont = this.dataSrcMap.get(name);
+ if (dsCont == null || dsCont.ds == null) {
+ throw new Err(new NoDataSrcToCreateDataConn(name, cls.getName()));
}
- DataConn conn;
+ DataConn dc;
try {
- conn = dsPtr.ds.createDataConn();
+ dc = dsCont.ds.createDataConn();
} catch (Exception e) {
- throw new Err(new DataHub.FailToCreateDataConn(name, cls.getName()), e);
+ throw new Err(new FailToCreateDataConn(name, cls.getName()), e);
}
- if (conn == null) {
- throw new Err(new DataHub.CreatedDataConnIsNull(name, cls.getName()));
+ if (dc == null) {
+ throw new Err(new CreatedDataConnIsNull(name, cls.getName()));
}
+ dcCont = new DataConnContainer(name, dc);
+ this.dataConnMap.put(name, dcCont);
+ this.dataConnManager.add(dcCont);
+
C c;
try {
- c = cls.cast(conn);
+ c = cls.cast(dc);
} catch (Exception e) {
- throw new Err(new DataHub.FailToCastDataConn(name, cls.getName()), e);
+ String fromType = dc.getClass().getName();
+ String toType = cls.getName();
+ throw new Err(new FailToCastDataConn(name, fromType, toType), e);
}
- connPtr = new DataConnContainer(name, c);
- this.dataConnMap.put(name, connPtr);
- this.dataConnList.appendContainer(connPtr);
-
return c;
}
}
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataSrcContainer.java b/src/main/java/com/github/sttk/sabi/internal/DataSrcContainer.java
index 23fc32f..90e413b 100644
--- a/src/main/java/com/github/sttk/sabi/internal/DataSrcContainer.java
+++ b/src/main/java/com/github/sttk/sabi/internal/DataSrcContainer.java
@@ -1,16 +1,14 @@
/*
* DataSrcContainer.java
- * Copyright (C) 2025-2026 Takayuki Sato. All Rights Reserved.
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
*/
package com.github.sttk.sabi.internal;
import com.github.sttk.sabi.DataSrc;
-public final class DataSrcContainer {
- DataSrcContainer prev;
- DataSrcContainer next;
- boolean local;
- String name;
+public class DataSrcContainer {
+ final boolean local;
+ final String name;
DataSrc ds;
DataSrcContainer(boolean local, String name, DataSrc ds) {
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataSrcList.java b/src/main/java/com/github/sttk/sabi/internal/DataSrcList.java
deleted file mode 100644
index 99ebc81..0000000
--- a/src/main/java/com/github/sttk/sabi/internal/DataSrcList.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * DataSrcList.java
- * Copyright (C) 2025-2026 Takayuki Sato. All Rights Reserved.
- */
-package com.github.sttk.sabi.internal;
-
-import com.github.sttk.errs.Err;
-import com.github.sttk.sabi.DataHub;
-import com.github.sttk.sabi.DataSrc;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-public final class DataSrcList {
- DataSrcContainer notSetupHead;
- DataSrcContainer notSetupLast;
- DataSrcContainer didSetupHead;
- DataSrcContainer didSetupLast;
- boolean local;
-
- DataSrcList(boolean local) {
- this.local = local;
- }
-
- void appendContainerPtrNotSetup(DataSrcContainer ptr) {
- ptr.next = null;
-
- if (this.notSetupLast == null) {
- this.notSetupHead = ptr;
- this.notSetupLast = ptr;
- ptr.prev = null;
- } else {
- this.notSetupLast.next = ptr;
- ptr.prev = this.notSetupLast;
- this.notSetupLast = ptr;
- }
- }
-
- void removeContainerPtrNotSetup(DataSrcContainer ptr) {
- var prev = ptr.prev;
- var next = ptr.next;
-
- if (prev == null && next == null) {
- this.notSetupHead = null;
- this.notSetupLast = null;
- } else if (prev == null) {
- next.prev = null;
- this.notSetupHead = next;
- } else if (next == null) {
- prev.next = null;
- this.notSetupLast = prev;
- } else {
- next.prev = prev;
- prev.next = next;
- }
- }
-
- void removeAndCloseContainerPtrNotSetupByName(String name) {
- var ptr = this.notSetupHead;
- while (ptr != null) {
- if (Objects.equals(ptr.name, name)) {
- this.removeContainerPtrNotSetup(ptr);
- ptr.ds.close();
- }
- ptr = ptr.next;
- }
- }
-
- void appendContainerPtrDidSetup(DataSrcContainer ptr) {
- ptr.next = null;
-
- if (this.didSetupLast == null) {
- this.didSetupHead = ptr;
- this.didSetupLast = ptr;
- ptr.prev = null;
- } else {
- this.didSetupLast.next = ptr;
- ptr.prev = this.didSetupLast;
- this.didSetupLast = ptr;
- }
- }
-
- void removeContainerPtrDidSetup(DataSrcContainer ptr) {
- var prev = ptr.prev;
- var next = ptr.next;
-
- if (prev == null && next == null) {
- this.didSetupHead = null;
- this.didSetupLast = null;
- } else if (prev == null) {
- next.prev = null;
- this.didSetupHead = next;
- } else if (next == null) {
- prev.next = null;
- this.didSetupLast = prev;
- } else {
- next.prev = prev;
- prev.next = next;
- }
- }
-
- void removeAndCloseContainerPtrDidSetupByName(String name) {
- var ptr = this.didSetupHead;
- while (ptr != null) {
- if (Objects.equals(ptr.name, name)) {
- removeContainerPtrDidSetup(ptr);
- ptr.ds.close();
- }
- ptr = ptr.next;
- }
- }
-
- void copyContainerPtrsDidSetupInto(Map m) {
- var ptr = this.didSetupHead;
- while (ptr != null) {
- m.put(ptr.name, ptr);
- ptr = ptr.next;
- }
- }
-
- void addDataSrc(String name, DataSrc ds) {
- var ptr = new DataSrcContainer(this.local, name, ds);
- this.appendContainerPtrNotSetup(ptr);
- }
-
- Map setupDataSrcs() {
- var errMap = new HashMap();
-
- if (this.notSetupHead == null) {
- return errMap;
- }
-
- var ag = new AsyncGroupImpl();
-
- var ptr = this.notSetupHead;
- while (ptr != null) {
- ag.name = ptr.name;
- try {
- ptr.ds.setup(ag);
- } catch (Err err) {
- errMap.put(ptr.name, err);
- break;
- } catch (RuntimeException e) {
- errMap.put(ptr.name, new Err(new DataHub.RuntimeExceptionOccurred(), e));
- break;
- }
- ptr = ptr.next;
- }
-
- ag.joinAndPutErrsInto(errMap);
-
- var firstPtrNotSetupYet = ptr;
-
- ptr = this.notSetupHead;
- while (ptr != null && ptr != firstPtrNotSetupYet) {
- var next = ptr.next;
- if (!errMap.containsKey(ptr.name)) {
- this.removeContainerPtrNotSetup(ptr);
- this.appendContainerPtrDidSetup(ptr);
- }
- ptr = next;
- }
-
- return errMap;
- }
-
- void closeDataSrcs() {
- var ptr = this.didSetupLast;
- while (ptr != null) {
- ptr.ds.close();
- ptr = ptr.prev;
- }
- this.notSetupHead = null;
- this.notSetupLast = null;
- this.didSetupHead = null;
- this.didSetupLast = null;
- }
-}
diff --git a/src/main/java/com/github/sttk/sabi/internal/DataSrcManager.java b/src/main/java/com/github/sttk/sabi/internal/DataSrcManager.java
new file mode 100644
index 0000000..3321d35
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/internal/DataSrcManager.java
@@ -0,0 +1,202 @@
+/*
+ * DataSrcManager.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi.internal;
+
+import com.github.sttk.errs.Err;
+import com.github.sttk.sabi.DataSrc;
+import com.github.sttk.sabi.ErrEntry;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class DataSrcManager {
+ final boolean local;
+ final List listUnready;
+ final List listReady;
+
+ DataSrcManager(boolean local) {
+ this.local = local;
+ this.listUnready = new ArrayList<>(0);
+ this.listReady = new ArrayList<>(0);
+ }
+
+ void add(String name, DataSrc ds) {
+ this.listUnready.add(new DataSrcContainer(this.local, name, ds));
+ }
+
+ void remove(String name) {
+ for (var cont : this.listReady) {
+ if (Objects.equals(cont.name, name) && cont.ds != null) {
+ cont.ds.close();
+ cont.ds = null;
+ }
+ }
+ for (var cont : this.listUnready) {
+ if (Objects.equals(cont.name, name) && cont.ds != null) {
+ cont.ds = null;
+ }
+ }
+ }
+
+ void close() {
+ for (int i = this.listReady.size() - 1; i >= 0; i--) {
+ var cont = this.listReady.get(i);
+ if (cont.ds != null) {
+ cont.ds.close();
+ cont.ds = null;
+ }
+ }
+ for (int i = this.listUnready.size() - 1; i >= 0; i--) {
+ var cont = this.listUnready.get(i);
+ if (cont.ds != null) {
+ cont.ds = null;
+ }
+ }
+ this.listReady.clear();
+ this.listUnready.clear();
+ }
+
+ List setup() {
+ if (this.listUnready.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ var ag = new AsyncGroupImpl();
+ int ii = 0, nDone = 0;
+ for (int i = 0, n = this.listUnready.size(); i < n; i++) {
+ var cont = this.listUnready.get(i);
+ if (cont == null || cont.ds == null) {
+ continue;
+ }
+ ag._name = cont.name;
+ ag._index = ii;
+ ii++;
+ try {
+ cont.ds.setup(ag);
+ } catch (Err err) {
+ ag.addErr(ag._index, ag._name, err);
+ nDone = i;
+ break;
+ } catch (RuntimeException re) {
+ ag.addErr(ag._index, ag._name, re);
+ nDone = i;
+ break;
+ }
+ }
+ var errors = ag.join();
+
+ if (errors.isEmpty()) {
+ for (int i = 0, n = this.listUnready.size(); i < n; i++) {
+ var cont = this.listUnready.get(i);
+ if (cont.ds != null) {
+ this.listReady.add(cont);
+ }
+ }
+ this.listUnready.clear();
+ return Collections.emptyList();
+ } else {
+ for (int i = nDone - 1; i >= 0; i--) {
+ var cont = this.listUnready.get(i);
+ if (cont.ds != null) {
+ cont.ds.close();
+ }
+ }
+ return errors;
+ }
+ }
+
+ List setupWithOrder(List names) {
+ if (this.listUnready.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ var indexedMap = new HashMap(names.size());
+ // Becuase earlier ones take precedence when names overlap
+ for (int i = names.size() - 1; i >= 0; i--) {
+ indexedMap.put(names.get(i), i);
+ }
+
+ var orderedIndexes = new ArrayList(this.listUnready.size());
+ for (int i = 0, n = names.size(); i < n; i++) {
+ orderedIndexes.add(null); // null indicates unset
+ }
+
+ for (int listIndex = 0, n = this.listUnready.size(); listIndex < n; listIndex++) {
+ var cont = this.listUnready.get(listIndex);
+ if (cont != null && cont.ds != null) {
+ Integer orderIndex = indexedMap.get(cont.name);
+ if (orderIndex != null) {
+ orderedIndexes.set(orderIndex, listIndex);
+ indexedMap.remove(cont.name);
+ } else {
+ orderedIndexes.add(listIndex);
+ }
+ }
+ }
+
+ var ag = new AsyncGroupImpl();
+ int ii = 0, nDone = 0;
+ for (int orderIndex = 0, n = orderedIndexes.size(); orderIndex < n; orderIndex++) {
+ Integer listIndex = orderedIndexes.get(orderIndex);
+ if (listIndex == null) { // ignore unset
+ continue;
+ }
+ var cont = this.listUnready.get(listIndex);
+ if (cont == null || cont.ds == null) {
+ continue;
+ }
+ ag._name = cont.name;
+ ag._index = ii;
+ ii++;
+ try {
+ cont.ds.setup(ag);
+ } catch (Err err) {
+ ag.addErr(ag._index, ag._name, err);
+ nDone = orderIndex;
+ break;
+ } catch (RuntimeException re) {
+ ag.addErr(ag._index, ag._name, re);
+ nDone = orderIndex;
+ break;
+ }
+ }
+ var errors = ag.join();
+
+ if (errors.isEmpty()) {
+ for (Integer listIndex : orderedIndexes) {
+ if (listIndex == null) { // ignore unset
+ continue;
+ }
+ var cont = this.listUnready.get(listIndex);
+ if (cont == null || cont.ds == null) {
+ continue;
+ }
+ this.listReady.add(cont);
+ }
+ this.listUnready.clear();
+ return Collections.emptyList();
+ } else {
+ for (int orderIndex = nDone - 1; orderIndex >= 0; orderIndex--) {
+ Integer listIndex = orderedIndexes.get(orderIndex);
+ if (listIndex != null) { // Ignore unset
+ var cont = this.listUnready.get(listIndex);
+ if (cont.ds != null) {
+ cont.ds.close();
+ }
+ }
+ }
+ return errors;
+ }
+ }
+
+ void copyDsReadyToMap(Map contMap) {
+ for (var cont : this.listReady) {
+ contMap.put(cont.name, cont);
+ }
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/internal/TxnFailureReportBuilder.java b/src/main/java/com/github/sttk/sabi/internal/TxnFailureReportBuilder.java
new file mode 100644
index 0000000..a5c8db6
--- /dev/null
+++ b/src/main/java/com/github/sttk/sabi/internal/TxnFailureReportBuilder.java
@@ -0,0 +1,31 @@
+/*
+ * TxnFailureReport.java
+ * Copyright (C) 2026 Takayuki Sato. All Rights Reserved.
+ */
+package com.github.sttk.sabi.internal;
+
+import com.github.sttk.sabi.DataConn;
+import com.github.sttk.sabi.TxnFailureCause;
+import com.github.sttk.sabi.TxnFailureCauseState;
+import com.github.sttk.sabi.TxnFailureReport;
+import com.github.sttk.sabi.TxnFailureRollback;
+import com.github.sttk.sabi.TxnFailureRollbackState;
+
+public class TxnFailureReportBuilder {
+ String dataConnName;
+ Class extends DataConn> dataConnClass;
+ TxnFailureCause cause;
+ TxnFailureRollback rollback;
+
+ TxnFailureReportBuilder(String name, Class extends DataConn> cls) {
+ this.dataConnName = name;
+ this.dataConnClass = cls;
+ this.cause = new TxnFailureCause(TxnFailureCauseState.NoneByUncommitted, null);
+ this.rollback = new TxnFailureRollback(TxnFailureRollbackState.NoneByNotRolledBack, null);
+ }
+
+ TxnFailureReport build() {
+ return new TxnFailureReport(
+ this.dataConnName, this.dataConnClass.getName(), this.cause, this.rollback);
+ }
+}
diff --git a/src/main/java/com/github/sttk/sabi/package-info.java b/src/main/java/com/github/sttk/sabi/package-info.java
index 02a0eae..50616de 100644
--- a/src/main/java/com/github/sttk/sabi/package-info.java
+++ b/src/main/java/com/github/sttk/sabi/package-info.java
@@ -4,7 +4,7 @@
*/
/**
- * Provides classes and interfaces of sabi framework.
+ * Provides core interfaces and classes for the sabi framework.
*
* @version 0.8
*/
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index ecced99..3eaf209 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -4,10 +4,11 @@
*/
/**
- * Defines the APIs of Sabi framework.
+ * Defines the module APIs of the Sabi framework.
*
- * This module includes the interfaces that abstracts data accesses to the external data stores
- * and the classes to execute a logic function with or without transaction operations.
+ *
This module provides interfaces and classes to abstract data access to external data stores,
+ * manage data source and connection lifecycles, and execute logic functions in transactional or
+ * non-transactional scopes.
*
* @version 0.8
*/
diff --git a/src/test/java/com/github/sttk/sabi/internal/AsyncGroupImplTest.java b/src/test/java/com/github/sttk/sabi/internal/AsyncGroupImplTest.java
index 09e4d11..2a7098e 100644
--- a/src/test/java/com/github/sttk/sabi/internal/AsyncGroupImplTest.java
+++ b/src/test/java/com/github/sttk/sabi/internal/AsyncGroupImplTest.java
@@ -1,11 +1,10 @@
package com.github.sttk.sabi.internal;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.assertj.core.api.Assertions.fail;
import com.github.sttk.errs.Err;
import com.github.sttk.sabi.Runner;
-import java.util.HashMap;
import org.junit.jupiter.api.Test;
public class AsyncGroupImplTest {
@@ -15,9 +14,8 @@ private AsyncGroupImplTest() {}
void zero() {
var ag = new AsyncGroupImpl();
- var m = new HashMap();
- ag.joinAndPutErrsInto(m);
- assertThat(m).hasSize(0);
+ var errors = ag.join();
+ assertThat(errors).isEmpty();
}
@Test
@@ -34,14 +32,14 @@ void ok() {
executed[0] = true;
};
- ag.name = "foo";
+ ag._index = 123;
+ ag._name = "foo";
ag.add(fn);
assertThat(executed[0]).isFalse();
- var m = new HashMap();
- ag.joinAndPutErrsInto(m);
- assertThat(m).hasSize(0);
+ var errors = ag.join();
assertThat(executed[0]).isTrue();
+ assertThat(errors).isEmpty();
}
@Test
@@ -61,23 +59,25 @@ record FailToDoSomething() {}
throw new Err(new FailToDoSomething());
};
- ag.name = "foo";
+ ag._index = 123;
+ ag._name = "foo";
ag.add(fn);
assertThat(executed[0]).isFalse();
- var m = new HashMap();
- ag.joinAndPutErrsInto(m);
- assertThat(m).hasSize(1);
+ var errors = ag.join();
assertThat(executed[0]).isTrue();
+ assertThat(errors).hasSize(1);
- switch (m.get("foo").getReason()) {
+ assertThat(errors.get(0).index).isEqualTo(123);
+ assertThat(errors.get(0).name).isEqualTo("foo");
+ switch (errors.get(0).err.getReason()) {
case FailToDoSomething r -> {}
default -> fail();
}
}
@Test
- void multiple_errors_with_an_error_map() {
+ void multiple_errors() {
var ag = new AsyncGroupImpl();
record Reason0() {}
@@ -89,7 +89,7 @@ record Reason2() {}
Runner fn0 =
() -> {
try {
- Thread.sleep(50);
+ Thread.sleep(200);
} catch (Exception e) {
}
executed[0] = true;
@@ -98,7 +98,7 @@ record Reason2() {}
Runner fn1 =
() -> {
try {
- Thread.sleep(50);
+ Thread.sleep(400);
} catch (Exception e) {
}
executed[1] = true;
@@ -114,79 +114,35 @@ record Reason2() {}
throw new Err(new Reason2());
};
- ag.name = "foo0";
+ ag._index = 123;
+ ag._name = "foo0";
ag.add(fn0);
- ag.name = "foo1";
+ ag._index = 456;
+ ag._name = "foo1";
ag.add(fn1);
- ag.name = "foo2";
+ ag._index = 789;
+ ag._name = "foo2";
ag.add(fn2);
- var m = new HashMap();
- ag.joinAndPutErrsInto(m);
- assertThat(m).hasSize(3);
+ var errors = ag.join();
assertThat(executed[0]).isTrue();
assertThat(executed[1]).isTrue();
assertThat(executed[2]).isTrue();
- assertThat(m.get("foo0").toString())
+ assertThat(errors.get(0).index).isEqualTo(789);
+ assertThat(errors.get(0).name).isEqualTo("foo2");
+ assertThat(errors.get(0).err.toString())
+ .isEqualTo(
+ "com.github.sttk.errs.Err { reason = com.github.sttk.sabi.internal.AsyncGroupImplTest$1Reason2 Reason2[], file = AsyncGroupImplTest.java, line = 114 }");
+ assertThat(errors.get(1).index).isEqualTo(123);
+ assertThat(errors.get(1).name).isEqualTo("foo0");
+ assertThat(errors.get(1).err.toString())
.isEqualTo(
"com.github.sttk.errs.Err { reason = com.github.sttk.sabi.internal.AsyncGroupImplTest$1Reason0 Reason0[], file = AsyncGroupImplTest.java, line = 96 }");
- assertThat(m.get("foo1").toString())
+ assertThat(errors.get(2).index).isEqualTo(456);
+ assertThat(errors.get(2).name).isEqualTo("foo1");
+ assertThat(errors.get(2).err.toString())
.isEqualTo(
"com.github.sttk.errs.Err { reason = com.github.sttk.sabi.internal.AsyncGroupImplTest$1Reason1 Reason1[], file = AsyncGroupImplTest.java, line = 105 }");
- assertThat(m.get("foo2").toString())
- .isEqualTo(
- "com.github.sttk.errs.Err { reason = com.github.sttk.sabi.internal.AsyncGroupImplTest$1Reason2 Reason2[], file = AsyncGroupImplTest.java, line = 114 }");
- }
-
- @Test
- void multiple_errors_without_an_error_map() {
- var ag = new AsyncGroupImpl();
-
- record Reason0() {}
- record Reason1() {}
- record Reason2() {}
-
- boolean[] executed = {false, false, false};
-
- Runner fn0 =
- () -> {
- try {
- Thread.sleep(50);
- } catch (Exception e) {
- }
- executed[0] = true;
- throw new Err(new Reason0());
- };
- Runner fn1 =
- () -> {
- try {
- Thread.sleep(50);
- } catch (Exception e) {
- }
- executed[1] = true;
- throw new Err(new Reason1());
- };
- Runner fn2 =
- () -> {
- try {
- Thread.sleep(50);
- } catch (Exception e) {
- }
- executed[2] = true;
- throw new Err(new Reason2());
- };
-
- ag.name = "foo0";
- ag.add(fn0);
- ag.name = "foo1";
- ag.add(fn1);
- ag.name = "foo2";
- ag.add(fn2);
-
- ag.joinAndIgnoreErrs();
- assertThat(executed[0]).isTrue();
- assertThat(executed[1]).isTrue();
- assertThat(executed[2]).isTrue();
}
}
diff --git a/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java b/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java
index a269a94..4b210d1 100644
--- a/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java
+++ b/src/test/java/com/github/sttk/sabi/internal/DataAccTest.java
@@ -1,5 +1,7 @@
package com.github.sttk.sabi.internal;
+import static com.github.sttk.sabi.Sabi.setup;
+import static com.github.sttk.sabi.Sabi.uses;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
@@ -10,7 +12,7 @@
import com.github.sttk.sabi.DataHub;
import com.github.sttk.sabi.DataSrc;
import com.github.sttk.sabi.Logic;
-import com.github.sttk.sabi.Sabi;
+import com.github.sttk.sabi.TxnFailureReport;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
@@ -19,213 +21,192 @@
import org.junit.jupiter.api.Test;
public class DataAccTest {
- DataAccTest() {}
-
- final void suppressWarnings_unused(Object a) {}
-
- static class FooDataSrc implements DataSrc {
- private int id;
- private String text;
- private List logger;
- private boolean willFail;
-
- FooDataSrc(int id, String text, List logger, boolean willFail) {
- this.id = id;
- this.text = text;
- this.logger = logger;
- this.willFail = willFail;
- }
-
- @Override
- public void setup(AsyncGroup ag) throws Err {
- if (this.willFail) {
- this.logger.add(String.format("FooDataSrc %d failed to setup", this.id));
- throw new Err("XXX");
- }
- this.logger.add(String.format("FooDataSrc %d setupped", this.id));
- }
-
- @Override
- public void close() {
- this.logger.add(String.format("FooDataSrc %d closed", this.id));
- }
-
- @Override
- public DataConn createDataConn() throws Err {
- this.logger.add(String.format("FooDataSrc %d created FooDataConn", this.id));
- return new FooDataConn(this.id, this.text, this.logger);
- }
- }
+ private DataAccTest() {}
static class FooDataConn implements DataConn {
- private int id;
- private String text;
- private boolean committed;
- private List logger;
+ final int id;
+ final String text;
+ boolean committed;
+ List logger;
- FooDataConn(int id, String text, List logger) {
+ FooDataConn(int id, String s, List logger) {
this.id = id;
- this.text = text;
+ this.text = s;
this.logger = logger;
+ logger.add(String.format("FooDataConn#new %d", id));
}
- String getText() {
+ public String getText() {
+ this.logger.add(String.format("FooDataConn#getText %d", this.id));
return this.text;
}
@Override
public void commit(AsyncGroup ag) throws Err {
this.committed = true;
- this.logger.add(String.format("FooDataConn %d committed", this.id));
+ this.logger.add(String.format("FooDataConn#commit %d", this.id));
}
@Override
public void preCommit(AsyncGroup ag) throws Err {
- this.logger.add(String.format("FooDataConn %d pre committed", this.id));
+ this.logger.add(String.format("FooDataConn#preCommit %d", this.id));
}
@Override
- public void postCommit(AsyncGroup ag) {
- this.logger.add(String.format("FooDataConn %d post committed", this.id));
+ public void postCommit(AsyncGroup ag) throws Err {
+ this.logger.add(String.format("FooDataConn#postCommit %d", this.id));
}
@Override
- public boolean shouldForceBack() {
+ public boolean isCommitted() {
return this.committed;
}
@Override
- public void rollback(AsyncGroup ag) {
- this.logger.add(String.format("FooDataConn %d rollbacked", this.id));
+ public void rollback(AsyncGroup ag) throws Err {
+ this.logger.add(String.format("FooDataConn#rollback %d", this.id));
}
@Override
- public void forceBack(AsyncGroup ag) {
- this.logger.add(String.format("FooDataConn %d forced back", this.id));
+ public void onTxnFailure(AsyncGroup ag, List reports) {
+ this.logger.add(String.format("FooDataConn#onTxnFailure %d", this.id));
}
@Override
public void close() {
- this.logger.add(String.format("FooDataConn %d closed", this.id));
+ this.logger.add(String.format("FooDataConn#close %d", this.id));
}
}
- static class BarDataSrc implements DataSrc {
- private int id;
- private String text;
- private List logger;
- private boolean willFail;
+ static class FooDataSrc implements DataSrc {
+ final int id;
+ final String text;
+ boolean failToSetup;
+ List logger;
- BarDataSrc(int id, List logger, boolean willFail) {
+ FooDataSrc(int id, List logger, boolean fail) {
this.id = id;
- this.text = null;
+ this.text = "hello";
this.logger = logger;
- this.willFail = willFail;
+ this.failToSetup = fail;
+ logger.add(String.format("FooDataSrc#new %d", id));
}
@Override
public void setup(AsyncGroup ag) throws Err {
- if (this.willFail) {
- this.logger.add(String.format("BarDataSrc %d failed to setup", this.id));
- throw new Err("XXX");
- }
- this.logger.add(String.format("BarDataSrc %d setupped", this.id));
+ this.logger.add(String.format("FooDataSrc#setup %d", this.id));
}
@Override
public void close() {
- this.logger.add(String.format("BarDataSrc.text = %s", this.text));
- this.logger.add(String.format("BarDataSrc %d closed", this.id));
+ this.logger.add(String.format("FooDataSrc#close %d", this.id));
}
@Override
public DataConn createDataConn() throws Err {
- this.logger.add(String.format("BarDataSrc %d created BarDataConn", this.id));
- return new BarDataConn(this.id, this.text, this.logger, this);
+ this.logger.add(String.format("FooDataSrc#createDataConn %d", this.id));
+ return new FooDataConn(this.id, this.text, this.logger);
}
}
static class BarDataConn implements DataConn {
- private int id;
- private String text;
- private boolean committed;
- private List logger;
- private BarDataSrc ds;
+ final int id;
+ String text;
+ boolean committed;
+ List logger;
- BarDataConn(int id, String text, List logger, BarDataSrc ds) {
+ BarDataConn(int id, List logger) {
this.id = id;
- this.text = text;
- this.committed = false;
+ this.text = "";
this.logger = logger;
- this.ds = ds;
+ logger.add(String.format("BarDataConn#new %d", id));
}
- void setText(String s) {
- this.text = s;
+ public void setText(String v) {
+ logger.add(String.format("BarDataConn#setText %d", this.id));
+ this.text = v;
}
@Override
public void commit(AsyncGroup ag) throws Err {
this.committed = true;
- this.ds.text = this.text;
- this.logger.add(String.format("BarDataConn %d committed", this.id));
+ this.logger.add(String.format("BarDataConn#commit %d", this.id));
}
@Override
public void preCommit(AsyncGroup ag) throws Err {
- this.logger.add(String.format("BarDataConn %d pre committed", this.id));
+ this.logger.add(String.format("BarDataConn#preCommit %d", this.id));
}
@Override
- public void postCommit(AsyncGroup ag) {
- this.logger.add(String.format("BarDataConn %d post committed", this.id));
+ public void postCommit(AsyncGroup ag) throws Err {
+ this.logger.add(String.format("BarDataConn#postCommit %d", this.id));
}
@Override
- public boolean shouldForceBack() {
+ public boolean isCommitted() {
return this.committed;
}
@Override
- public void rollback(AsyncGroup ag) {
- this.logger.add(String.format("BarDataConn %d rollbacked", this.id));
+ public void rollback(AsyncGroup ag) throws Err {
+ this.logger.add(String.format("BarDataConn#rollback %d", this.id));
}
@Override
- public void forceBack(AsyncGroup ag) {
- this.logger.add(String.format("BarDataConn %d forced back", this.id));
+ public void onTxnFailure(AsyncGroup ag, List reports) {
+ this.logger.add(String.format("BarDataConn#onTxnFailure %d", this.id));
}
@Override
public void close() {
- this.logger.add(String.format("BarDataConn.text = %s", this.text));
- this.logger.add(String.format("BarDataConn %d closed", this.id));
+ this.logger.add(String.format("BarDataConn#close %d", this.id));
}
}
- ///
+ static class BarDataSrc implements DataSrc {
+ final int id;
+ List logger;
+ boolean failToSetup;
- static interface SampleData {
- String getValue() throws Err;
+ BarDataSrc(int id, List logger, boolean fail) {
+ this.id = id;
+ this.logger = logger;
+ this.failToSetup = fail;
+ logger.add(String.format("BarDataSrc#new %d", id));
+ }
- void setValue(String text) throws Err;
- }
+ @Override
+ public void setup(AsyncGroup ag) throws Err {
+ this.logger.add(String.format("BarDataSrc#setup %d", this.id));
+ }
- static class SampleLogic implements Logic {
@Override
- public void run(SampleData data) throws Err {
- var v = data.getValue();
- data.setValue(v);
+ public void close() {
+ this.logger.add(String.format("BarDataSrc#close %d", this.id));
}
- }
- static class FailingLogic implements Logic {
@Override
- public void run(SampleData data) throws Err {
- throw new Err("ZZZ");
+ public DataConn createDataConn() throws Err {
+ this.logger.add(String.format("BarDataSrc#createDataConn %d", this.id));
+ return new BarDataConn(this.id, this.logger);
}
}
+ static interface SampleData {
+ String getValue() throws Err;
+
+ void setValue(String v) throws Err;
+ }
+
+ static Logic sampleLogic =
+ data -> {
+ String v = data.getValue();
+ data.setValue(v);
+ v = data.getValue();
+ data.setValue(v);
+ };
+
static interface AllLogicData extends SampleData {}
static interface FooDataAcc extends DataAcc, AllLogicData {
@@ -238,437 +219,130 @@ default String getValue() throws Err {
static interface BarDataAcc extends DataAcc, AllLogicData {
@Override
- default void setValue(String text) throws Err {
+ default void setValue(String v) throws Err {
var conn = getDataConn("bar", BarDataConn.class);
- conn.setText(text);
+ conn.setText(v);
+ assertThat(conn.text).isEqualTo("hello");
}
}
- ///
-
static class SampleDataHub extends DataHub implements FooDataAcc, BarDataAcc {}
///
@Nested
- class TestLogicArgument {
+ class RunTest {
@BeforeEach
void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
+ DataHubInnerTest.resetGlobals();
}
@AfterEach
void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
+ DataHubInnerTest.resetGlobals();
}
@Test
- void test() {
+ void testMain() {
var logger = new ArrayList();
- Sabi.uses("foo", new FooDataSrc(1, "hello", logger, false));
- Sabi.uses("bar", new BarDataSrc(2, logger, false));
+ uses("foo", new FooDataSrc(1, logger, false));
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var hub = new SampleDataHub()) {
- new SampleLogic().run(hub);
- } catch (Exception e) {
- fail(e);
- }
+ try (var ac = setup()) {
+ testApp(logger);
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "FooDataSrc 1 setupped",
- "BarDataSrc 2 setupped",
- "FooDataSrc 1 created FooDataConn",
- "BarDataSrc 2 created BarDataConn",
- "BarDataConn.text = hello",
- "BarDataConn 2 closed",
- "FooDataConn 1 closed",
- "BarDataSrc.text = null",
- "BarDataSrc 2 closed",
- "FooDataSrc 1 closed");
- }
- }
-
- @Nested
- class TestDataHubRunUsingGlobal {
- @BeforeEach
- void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @AfterEach
- void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @Test
- void test() {
- var logger = new ArrayList();
-
- Sabi.uses("foo", new FooDataSrc(1, "hello", logger, false));
- Sabi.uses("bar", new BarDataSrc(2, logger, false));
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.run(new SampleLogic());
- } catch (Exception e) {
- fail(e);
- }
+ assertThat(logger).hasSize(16);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("FooDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("FooDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#new 2");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("FooDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("FooDataConn#new 1");
+ assertThat(iter.next()).isEqualTo("FooDataConn#getText 1");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("BarDataConn#new 2");
+ assertThat(iter.next()).isEqualTo("BarDataConn#setText 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#getText 1");
+ assertThat(iter.next()).isEqualTo("BarDataConn#setText 2");
+ assertThat(iter.next()).isEqualTo("BarDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("FooDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ void testApp(List logger) throws Err {
+ try (var hub = new SampleDataHub()) {
+ hub.uses("bar", new BarDataSrc(2, logger, false));
+ hub.run(sampleLogic);
} catch (Exception e) {
fail(e);
}
-
- assertThat(logger)
- .containsExactly(
- "FooDataSrc 1 setupped",
- "BarDataSrc 2 setupped",
- "FooDataSrc 1 created FooDataConn",
- "BarDataSrc 2 created BarDataConn",
- "BarDataConn.text = hello",
- "BarDataConn 2 closed",
- "FooDataConn 1 closed",
- "BarDataSrc.text = null",
- "BarDataSrc 2 closed",
- "FooDataSrc 1 closed");
}
}
@Nested
- class TestDataHubRunUsingLocal {
+ class TxnTest {
@BeforeEach
void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
+ DataHubInnerTest.resetGlobals();
}
@AfterEach
void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
+ DataHubInnerTest.resetGlobals();
}
@Test
- void test() {
+ void testMain() {
var logger = new ArrayList();
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(1, "hello", logger, false));
- data.uses("bar", new BarDataSrc(2, logger, false));
+ uses("foo", new FooDataSrc(1, logger, false));
- data.run(new SampleLogic());
- } catch (Exception e) {
- fail(e);
- }
+ try (var ac = setup()) {
+ testApp(logger);
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "FooDataSrc 1 setupped",
- "BarDataSrc 2 setupped",
- "FooDataSrc 1 created FooDataConn",
- "BarDataSrc 2 created BarDataConn",
- "BarDataConn.text = hello",
- "BarDataConn 2 closed",
- "FooDataConn 1 closed",
- "BarDataSrc.text = null",
- "BarDataSrc 2 closed",
- "FooDataSrc 1 closed");
- }
-
- @Test
- void test_not_run_logic_if_fail_to_setup_local_data_src() {
- var logger = new ArrayList();
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(1, "hello", logger, true));
- data.uses("bar", new BarDataSrc(2, logger, false));
-
- data.run(new SampleLogic());
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToSetupLocalDataSrcs r -> {
- var e2 = r.errors().get("foo");
- assertThat(e2.getReason()).isEqualTo("XXX");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
+ assertThat(logger).hasSize(22);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("FooDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("FooDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#new 2");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("FooDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("FooDataConn#new 1");
+ assertThat(iter.next()).isEqualTo("FooDataConn#getText 1");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("BarDataConn#new 2");
+ assertThat(iter.next()).isEqualTo("BarDataConn#setText 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#getText 1");
+ assertThat(iter.next()).isEqualTo("BarDataConn#setText 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("BarDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("BarDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("BarDataConn#postCommit 2");
+ assertThat(iter.next()).isEqualTo("BarDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("FooDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("BarDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("FooDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ void testApp(List logger) throws Err {
+ try (var hub = new SampleDataHub()) {
+ hub.uses("bar", new BarDataSrc(2, logger, false));
+ hub.txn(sampleLogic);
} catch (Exception e) {
fail(e);
}
-
- assertThat(logger).containsExactly("FooDataSrc 1 failed to setup");
- }
- }
-
- @Nested
- class TestDataHubRunUsingGlobalAndLocal {
- @BeforeEach
- void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @AfterEach
- void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @Test
- void test() {
- var logger = new ArrayList();
-
- Sabi.uses("bar", new BarDataSrc(1, logger, false));
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(2, "Hello", logger, false));
-
- data.run(new SampleLogic());
- } catch (Exception e) {
- fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(logger)
- .containsExactly(
- "BarDataSrc 1 setupped",
- "FooDataSrc 2 setupped",
- "FooDataSrc 2 created FooDataConn",
- "BarDataSrc 1 created BarDataConn",
- "BarDataConn.text = Hello",
- "BarDataConn 1 closed",
- "FooDataConn 2 closed",
- "FooDataSrc 2 closed",
- "BarDataSrc.text = null",
- "BarDataSrc 1 closed");
- }
- }
-
- @Nested
- class TestDataHubTxnUsingGlobal {
- @BeforeEach
- void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @AfterEach
- void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @Test
- void test() {
- var logger = new ArrayList();
-
- Sabi.uses("foo", new FooDataSrc(1, "Hello", logger, false));
- Sabi.uses("bar", new BarDataSrc(2, logger, false));
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.txn(new SampleLogic());
- } catch (Exception e) {
- fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(logger)
- .containsExactly(
- "FooDataSrc 1 setupped",
- "BarDataSrc 2 setupped",
- "FooDataSrc 1 created FooDataConn",
- "BarDataSrc 2 created BarDataConn",
- "FooDataConn 1 pre committed",
- "BarDataConn 2 pre committed",
- "FooDataConn 1 committed",
- "BarDataConn 2 committed",
- "FooDataConn 1 post committed",
- "BarDataConn 2 post committed",
- "BarDataConn.text = Hello",
- "BarDataConn 2 closed",
- "FooDataConn 1 closed",
- "BarDataSrc.text = Hello",
- "BarDataSrc 2 closed",
- "FooDataSrc 1 closed");
- }
- }
-
- @Nested
- class TestDataHubTxnUsingLocal {
- @BeforeEach
- void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @AfterEach
- void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @Test
- void test() {
- var logger = new ArrayList();
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(1, "Hello", logger, false));
- data.uses("bar", new BarDataSrc(2, logger, false));
-
- data.txn(new SampleLogic());
- } catch (Exception e) {
- fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(logger)
- .containsExactly(
- "FooDataSrc 1 setupped",
- "BarDataSrc 2 setupped",
- "FooDataSrc 1 created FooDataConn",
- "BarDataSrc 2 created BarDataConn",
- "FooDataConn 1 pre committed",
- "BarDataConn 2 pre committed",
- "FooDataConn 1 committed",
- "BarDataConn 2 committed",
- "FooDataConn 1 post committed",
- "BarDataConn 2 post committed",
- "BarDataConn.text = Hello",
- "BarDataConn 2 closed",
- "FooDataConn 1 closed",
- "BarDataSrc.text = Hello",
- "BarDataSrc 2 closed",
- "FooDataSrc 1 closed");
- }
-
- @Test
- void test_not_run_logic_if_fail_to_setup_local_data_src() {
- var logger = new ArrayList();
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(1, "Hello", logger, true));
- data.uses("bar", new BarDataSrc(2, logger, false));
-
- data.txn(new SampleLogic());
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToSetupLocalDataSrcs r -> {
- var e2 = r.errors().get("foo");
- assertThat(e2.getReason()).isEqualTo("XXX");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(logger).containsExactly("FooDataSrc 1 failed to setup");
- }
-
- @Test
- void test_not_run_logic_in_txn_and_rollback() {
- var logger = new ArrayList();
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(1, "Hello", logger, false));
- data.uses("bar", new BarDataSrc(2, logger, false));
-
- data.txn(new FailingLogic());
- } catch (Err e) {
- assertThat(e.getReason()).isEqualTo("ZZZ");
- } catch (Exception e) {
- fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(logger)
- .containsExactly(
- "FooDataSrc 1 setupped",
- "BarDataSrc 2 setupped",
- "BarDataSrc.text = null",
- "BarDataSrc 2 closed",
- "FooDataSrc 1 closed");
- }
- }
-
- @Nested
- class TestDataHubTxnUsingGlobalAndLocal {
- @BeforeEach
- void beforeEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @AfterEach
- void afterEach() {
- DataHubInnerTest.resetGlobalVariables();
- }
-
- @Test
- void test() {
- var logger = new ArrayList();
-
- Sabi.uses("bar", new BarDataSrc(1, logger, false));
-
- try (var ac = Sabi.setup()) {
- suppressWarnings_unused(ac);
- try (var data = new SampleDataHub()) {
- data.uses("foo", new FooDataSrc(2, "Hello", logger, false));
-
- data.txn(new SampleLogic());
- } catch (Exception e) {
- fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(logger)
- .containsExactly(
- "BarDataSrc 1 setupped",
- "FooDataSrc 2 setupped",
- "FooDataSrc 2 created FooDataConn",
- "BarDataSrc 1 created BarDataConn",
- "FooDataConn 2 pre committed",
- "BarDataConn 1 pre committed",
- "FooDataConn 2 committed",
- "BarDataConn 1 committed",
- "FooDataConn 2 post committed",
- "BarDataConn 1 post committed",
- "BarDataConn.text = Hello",
- "BarDataConn 1 closed",
- "FooDataConn 2 closed",
- "FooDataSrc 2 closed",
- "BarDataSrc.text = Hello",
- "BarDataSrc 1 closed");
}
}
}
diff --git a/src/test/java/com/github/sttk/sabi/internal/DataConnListTest.java b/src/test/java/com/github/sttk/sabi/internal/DataConnListTest.java
deleted file mode 100644
index 61d4a11..0000000
--- a/src/test/java/com/github/sttk/sabi/internal/DataConnListTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.github.sttk.sabi.internal;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import com.github.sttk.errs.Err;
-import com.github.sttk.sabi.AsyncGroup;
-import com.github.sttk.sabi.DataConn;
-import java.util.ArrayList;
-import java.util.List;
-import org.junit.jupiter.api.Test;
-
-public class DataConnListTest {
- private DataConnListTest() {}
-
- static class SampleDataConn implements DataConn {
- private int id;
- private List logger;
-
- public SampleDataConn(int id, List logger) {
- this.id = id;
- this.logger = logger;
- }
-
- public void commit(AsyncGroup ag) throws Err {}
-
- public void preCommit(AsyncGroup ag) throws Err {}
-
- public void postCommit(AsyncGroup ag) {}
-
- public boolean shouldForceBack() {
- return false;
- }
-
- public void rollback(AsyncGroup ag) {}
-
- public void forceBack(AsyncGroup ag) {}
-
- public void close() {
- this.logger.add(String.format("SampleDataConn %d closed", this.id));
- }
- }
-
- @Test
- void test_new() {
- var dcList = new DataConnList();
-
- assertThat(dcList.head).isNull();
- assertThat(dcList.last).isNull();
- }
-
- @Test
- void test_appendContainer() {
- var dcList = new DataConnList();
- var logger = new ArrayList();
-
- var dc1 = new SampleDataConn(1, logger);
- var cont1 = new DataConnContainer("foo", dc1);
- dcList.appendContainer(cont1);
-
- assertThat(dcList.head).isEqualTo(cont1);
- assertThat(dcList.last).isEqualTo(cont1);
-
- var dc2 = new SampleDataConn(2, logger);
- var cont2 = new DataConnContainer("bar", dc2);
- dcList.appendContainer(cont2);
-
- assertThat(dcList.head).isEqualTo(cont1);
- assertThat(dcList.last).isEqualTo(cont2);
- assertThat(cont1.prev).isNull();
- assertThat(cont1.next).isEqualTo(cont2);
- assertThat(cont2.prev).isEqualTo(cont1);
- assertThat(cont2.next).isNull();
-
- var dc3 = new SampleDataConn(3, logger);
- var cont3 = new DataConnContainer("baz", dc3);
- dcList.appendContainer(cont3);
-
- assertThat(dcList.head).isEqualTo(cont1);
- assertThat(dcList.last).isEqualTo(cont3);
- assertThat(cont1.prev).isNull();
- assertThat(cont1.next).isEqualTo(cont2);
- assertThat(cont2.prev).isEqualTo(cont1);
- assertThat(cont2.next).isEqualTo(cont3);
- assertThat(cont3.prev).isEqualTo(cont2);
- assertThat(cont3.next).isNull();
-
- dcList.closeDataConns();
-
- assertThat(dcList.head).isNull();
- assertThat(dcList.last).isNull();
-
- assertThat(logger)
- .containsExactly(
- "SampleDataConn 3 closed", "SampleDataConn 2 closed", "SampleDataConn 1 closed");
- }
-}
diff --git a/src/test/java/com/github/sttk/sabi/internal/DataConnManagerTest.java b/src/test/java/com/github/sttk/sabi/internal/DataConnManagerTest.java
new file mode 100644
index 0000000..f39566d
--- /dev/null
+++ b/src/test/java/com/github/sttk/sabi/internal/DataConnManagerTest.java
@@ -0,0 +1,1352 @@
+package com.github.sttk.sabi.internal;
+
+import static com.github.sttk.sabi.DataConn.FailToCommitDataConn;
+import static com.github.sttk.sabi.DataConn.FailToPostCommitDataConn;
+import static com.github.sttk.sabi.DataConn.FailToPreCommitDataConn;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+
+import com.github.sttk.errs.Err;
+import com.github.sttk.sabi.AsyncGroup;
+import com.github.sttk.sabi.DataConn;
+import com.github.sttk.sabi.TxnFailureReport;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+
+public class DataConnManagerTest {
+ private DataConnManagerTest() {}
+
+ static enum Fail {
+ Not,
+ Commit,
+ PreCommit,
+ PostCommit,
+ Rollback,
+ PreCommitBecomeCommitted,
+ }
+
+ static class SyncDataConn implements DataConn {
+ int id;
+ boolean committed;
+ Fail fail;
+ List logger;
+
+ SyncDataConn(int id, List logger, Fail fail) {
+ this.id = id;
+ this.committed = false;
+ this.fail = fail;
+ this.logger = logger;
+ }
+
+ @Override
+ public void commit(AsyncGroup ag) throws Err {
+ if (this.fail == Fail.Commit) {
+ this.logger.add(String.format("SyncDataConn#commit %d failed", this.id));
+ throw new Err("ZZZ");
+ }
+ this.committed = true;
+ this.logger.add(String.format("SyncDataConn#commit %d", this.id));
+ }
+
+ @Override
+ public void preCommit(AsyncGroup ag) throws Err {
+ if (this.fail == Fail.PreCommit) {
+ this.logger.add(String.format("SyncDataConn#preCommit %d failed", this.id));
+ throw new Err("zzz");
+ }
+ this.logger.add(String.format("SyncDataConn#preCommit %d", this.id));
+ if (this.fail == Fail.PreCommitBecomeCommitted) {
+ this.committed = true;
+ }
+ }
+
+ @Override
+ public void postCommit(AsyncGroup ag) throws Err {
+ if (this.fail == Fail.PostCommit) {
+ this.logger.add(String.format("SyncDataConn#postCommit %d failed", this.id));
+ throw new Err("!!!");
+ }
+ this.logger.add(String.format("SyncDataConn#postCommit %d", this.id));
+ }
+
+ @Override
+ public boolean isCommitted() {
+ return this.committed;
+ }
+
+ @Override
+ public void rollback(AsyncGroup ag) throws Err {
+ if (this.fail == Fail.Rollback) {
+ this.logger.add(String.format("SyncDataConn#rollback %d failed", this.id));
+ throw new Err("???");
+ }
+ this.logger.add(String.format("SyncDataConn#rollback %d", this.id));
+ }
+
+ @Override
+ public void onTxnFailure(AsyncGroup ag, List reports) {
+ this.logger.add(String.format("SyncDataConn#onTxnFailure %d", this.id));
+ this.logger.add(String.format("TxnFailureReports=%s", reports.toString()));
+ }
+
+ @Override
+ public void close() {
+ this.logger.add(String.format("SyncDataConn#close %d", this.id));
+ }
+ }
+
+ static class AsyncDataConn implements DataConn {
+ int id;
+ boolean committed;
+ Fail fail;
+ List logger;
+
+ AsyncDataConn(int id, List logger, Fail fail) {
+ this.id = id;
+ this.committed = false;
+ this.fail = fail;
+ this.logger = logger;
+ }
+
+ @Override
+ public void commit(AsyncGroup ag) throws Err {
+ ag.add(
+ () -> {
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+ if (this.fail == Fail.Commit) {
+ this.logger.add(String.format("AsyncDataConn#commit %d failed", this.id));
+ throw new Err("YYY");
+ }
+ this.committed = true;
+ this.logger.add(String.format("AsyncDataConn#commit %d", this.id));
+ });
+ }
+
+ @Override
+ public void preCommit(AsyncGroup ag) throws Err {
+ ag.add(
+ () -> {
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+ if (this.fail == Fail.PreCommit) {
+ this.logger.add(String.format("AsyncDataConn#preCommit %d failed", this.id));
+ throw new Err("yyy");
+ }
+ this.logger.add(String.format("AsyncDataConn#preCommit %d", this.id));
+ if (this.fail == Fail.PreCommitBecomeCommitted) {
+ this.committed = true;
+ }
+ });
+ }
+
+ @Override
+ public void postCommit(AsyncGroup ag) throws Err {
+ ag.add(
+ () -> {
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+ if (this.fail == Fail.PostCommit) {
+ this.logger.add(String.format("AsyncDataConn#postCommit %d failed", this.id));
+ throw new Err("!!!");
+ }
+ this.logger.add(String.format("AsyncDataConn#postCommit %d", this.id));
+ });
+ }
+
+ @Override
+ public boolean isCommitted() {
+ return this.committed;
+ }
+
+ @Override
+ public void rollback(AsyncGroup ag) throws Err {
+ ag.add(
+ () -> {
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+ if (this.fail == Fail.Rollback) {
+ this.logger.add(String.format("AsyncDataConn#rollback %d failed", this.id));
+ throw new Err("???");
+ }
+ this.logger.add(String.format("AsyncDataConn#rollback %d", this.id));
+ });
+ }
+
+ @Override
+ public void onTxnFailure(AsyncGroup ag, List reports) {
+ ag.add(
+ () -> {
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+ this.logger.add(String.format("AsyncDataConn#onTxnFailure %d", this.id));
+ this.logger.add(String.format("TxnFailureReports=%s", reports.toString()));
+ });
+ }
+
+ @Override
+ public void close() {
+ this.logger.add(String.format("AsyncDataConn#close %d", this.id));
+ }
+ }
+
+ ///
+
+ @Test
+ void testNew() {
+ var manager = new DataConnManager();
+ assertThat(manager.list).isEmpty();
+ assertThat(manager.indexMap).isEmpty();
+ }
+
+ @Test
+ void testNewWithCommitOrder() {
+ var manager = new DataConnManager(List.of("bar", "baz", "foo"));
+ assertThat(manager.list).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isNull();
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(2);
+ assertThat(manager.indexMap.get("bar")).isEqualTo(0);
+ assertThat(manager.indexMap.get("baz")).isEqualTo(1);
+ }
+
+ @Test
+ void testNewAndAdd() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ assertThat(manager.list).isEmpty();
+ assertThat(manager.indexMap).isEmpty();
+
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+ assertThat(manager.list).hasSize(1);
+ assertThat(manager.indexMap).hasSize(1);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(0);
+ assertThat(manager.list.get(0).conn).isEqualTo(conn1);
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+ assertThat(manager.list).hasSize(2);
+ assertThat(manager.indexMap).hasSize(2);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(0);
+ assertThat(manager.indexMap.get("bar")).isEqualTo(1);
+ assertThat(manager.list.get(0).conn).isEqualTo(conn1);
+ assertThat(manager.list.get(1).conn).isEqualTo(conn2);
+ }
+
+ @Test
+ void testAndAddWhenOverlappingName() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ assertThat(manager.list).isEmpty();
+ assertThat(manager.indexMap).isEmpty();
+
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+ assertThat(manager.list).hasSize(1);
+ assertThat(manager.indexMap).hasSize(1);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(0);
+ assertThat(manager.list.get(0).conn).isEqualTo(conn1);
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn2));
+ assertThat(manager.list).hasSize(1);
+ assertThat(manager.indexMap).hasSize(1);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(0);
+ assertThat(manager.list.get(0).conn).isEqualTo(conn1);
+ }
+
+ @Test
+ void testNewWithCommitOrderAndAdd() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager(List.of("bar", "baz", "foo"));
+ assertThat(manager.list).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isNull();
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(2);
+ assertThat(manager.indexMap.get("bar")).isEqualTo(0);
+ assertThat(manager.indexMap.get("baz")).isEqualTo(1);
+
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isEqualTo(conn1);
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.list.get(0).conn).isEqualTo(conn2);
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isEqualTo(conn1);
+
+ var conn3 = new SyncDataConn(3, logger, Fail.Not);
+ manager.add(new DataConnContainer("qux", conn3));
+ assertThat(manager.indexMap).hasSize(4);
+ assertThat(manager.list.get(0).conn).isEqualTo(conn2);
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isEqualTo(conn1);
+ assertThat(manager.list.get(3).conn).isEqualTo(conn3);
+ }
+
+ @Test
+ void testNewWithCommitOrderAndAddWhenOverlappingName() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager(List.of("bar", "baz", "foo"));
+ assertThat(manager.list).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isNull();
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.indexMap.get("foo")).isEqualTo(2);
+ assertThat(manager.indexMap.get("bar")).isEqualTo(0);
+ assertThat(manager.indexMap.get("baz")).isEqualTo(1);
+
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isEqualTo(conn1);
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn2));
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isEqualTo(conn1);
+
+ var conn3 = new SyncDataConn(3, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn3));
+ assertThat(manager.indexMap).hasSize(3);
+ assertThat(manager.list.get(0).conn).isNull();
+ assertThat(manager.list.get(1).conn).isNull();
+ assertThat(manager.list.get(2).conn).isEqualTo(conn1);
+ }
+
+ @Test
+ void testNewFailureReports() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+ assertThat(reportBuilders).isEmpty();
+
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+ assertThat(reportBuilders).hasSize(2);
+
+ var reports = reportBuilders.stream().map(b -> b.build()).collect(Collectors.toList());
+ assertThat(reports.get(0).dataConnName).isEqualTo("foo");
+ assertThat(reports.get(0).dataConnType).isEqualTo(SyncDataConn.class.getName());
+ assertThat(reports.get(1).dataConnName).isEqualTo("bar");
+ assertThat(reports.get(1).dataConnType).isEqualTo(AsyncDataConn.class.getName());
+ }
+
+ @Test
+ void testCommitAndRollbackOk() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ manager.commit(reportBuilders);
+ manager.rollback(reportBuilders);
+ } catch (Err err) {
+ fail(err);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(12);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#postCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitWithOrderAndRollbackOk() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager(List.of("bar", "baz", "foo"));
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new SyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var conn3 = new SyncDataConn(3, logger, Fail.Not);
+ manager.add(new DataConnContainer("qux", conn3));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ manager.commit(reportBuilders);
+ manager.rollback(reportBuilders);
+ } catch (Err err) {
+ fail(err);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(18);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 3");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 3");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 3");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:qux dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:qux dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 3");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:qux dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 3");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 2");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailFirstSyncPreCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.PreCommit);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.PreCommit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPreCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("zzz");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(9);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String zzz, file = DataConnManagerTest.java, line = 57 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String zzz, file = DataConnManagerTest.java, line = 57 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailFirstAsyncPreCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new AsyncDataConn(1, logger, Fail.PreCommit);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new SyncDataConn(2, logger, Fail.PreCommit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPreCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(2);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("zzz");
+ ee = rsn.errors().get(1);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("yyy");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(10);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 2 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 1 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String yyy, file = DataConnManagerTest.java, line = 140 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String zzz, file = DataConnManagerTest.java, line = 57 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String yyy, file = DataConnManagerTest.java, line = 140 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String zzz, file = DataConnManagerTest.java, line = 57 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailSecondPreCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.PreCommit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPreCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("yyy");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(10);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String yyy, file = DataConnManagerTest.java, line = 140 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:LogicFailure Err:com.github.sttk.errs.Err { reason = java.lang.String yyy, file = DataConnManagerTest.java, line = 140 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailFirstSyncCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Commit);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Commit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("ZZZ");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(11);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ZZZ, file = DataConnManagerTest.java, line = 47 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ZZZ, file = DataConnManagerTest.java, line = 47 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailFirstAsyncCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new AsyncDataConn(1, logger, Fail.Commit);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new SyncDataConn(2, logger, Fail.Commit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(2);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("ZZZ");
+ ee = rsn.errors().get(1);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("YYY");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(12);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 2 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 1 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ZZZ, file = DataConnManagerTest.java, line = 47 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ZZZ, file = DataConnManagerTest.java, line = 47 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailSecondCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Commit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("YYY");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(11);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailFirstSyncPostCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.PostCommit);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.PostCommit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPostCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(2);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("!!!");
+ ee = rsn.errors().get(1);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("!!!");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(12);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 1 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#postCommit 2 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 69 }} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 159 }} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 69 }} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 159 }} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailFirstAsyncPostCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new AsyncDataConn(2, logger, Fail.PostCommit);
+ manager.add(new DataConnContainer("bar", conn1));
+
+ var conn2 = new SyncDataConn(1, logger, Fail.PostCommit);
+ manager.add(new DataConnContainer("foo", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPostCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(2);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("!!!");
+ ee = rsn.errors().get(1);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("!!!");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(12);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 1 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#postCommit 2 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 159 }} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 69 }} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 159 }} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 69 }} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackButFailSecondPostCommit() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.PostCommit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPostCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("!!!");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(12);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#postCommit 2 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 159 }} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:PostCommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String !!!, file = DataConnManagerTest.java, line = 159 }} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testOnlyRollbackAndFirstIsSync() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ manager.rollback(reportBuilders);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testOnlyRollbackAndFirstIsAsync() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new AsyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new SyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ manager.rollback(reportBuilders);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testOnlyRollbackAndSecondRollbackFailed() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new AsyncDataConn(1, logger, Fail.Not);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new SyncDataConn(2, logger, Fail.Rollback);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ manager.rollback(reportBuilders);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 2 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:RollbackFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ???, file = DataConnManagerTest.java, line = 83 }}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:RollbackFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ???, file = DataConnManagerTest.java, line = 83 }}}]");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testOnlyRollbackAndFirstRollbackFailed() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Rollback);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Not);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ manager.rollback(reportBuilders);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 1 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:RollbackFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ???, file = DataConnManagerTest.java, line = 83 }}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:RollbackFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ???, file = DataConnManagerTest.java, line = 83 }}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackAndFirstRollbackFailedThenSecondRollbackFailed() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Commit);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Rollback);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(0);
+ assertThat(ee.name).isEqualTo("foo");
+ assertThat(ee.err.getReason()).isEqualTo("ZZZ");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(11);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ZZZ, file = DataConnManagerTest.java, line = 47 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:RollbackFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ???, file = DataConnManagerTest.java, line = 180 }}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ZZZ, file = DataConnManagerTest.java, line = 47 }} rollback:{State:NoneByRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByUncommitted Err:null} rollback:{State:RollbackFailure Err:com.github.sttk.errs.Err { reason = java.lang.String ???, file = DataConnManagerTest.java, line = 180 }}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackAndSecondCommitFailedThenFirstRollbackFailed() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.Rollback);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Commit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("YYY");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(11);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackAndPreCommitBecomeCommittedAndOk() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.PreCommitBecomeCommitted);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.PreCommitBecomeCommitted);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(10);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#postCommit 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testCommitAndRollbackAndPreCommitBecomeCommittedButFailed() {
+ var logger = new ArrayList();
+
+ var manager = new DataConnManager();
+ try {
+ var conn1 = new SyncDataConn(1, logger, Fail.PreCommitBecomeCommitted);
+ manager.add(new DataConnContainer("foo", conn1));
+
+ var conn2 = new AsyncDataConn(2, logger, Fail.Commit);
+ manager.add(new DataConnContainer("bar", conn2));
+
+ var reportBuilders = new ArrayList();
+ manager.prepareTxnFailureReportBuilders(reportBuilders);
+
+ try {
+ manager.commit(reportBuilders);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("YYY");
+ }
+ default -> fail(err);
+ }
+ manager.rollback(reportBuilders);
+ } catch (Exception e) {
+ fail(e);
+ }
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(10);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#commit 2 failed");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#onTxnFailure 1");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#onTxnFailure 2");
+ assertThat(iter.next())
+ .isEqualTo(
+ "TxnFailureReports=[{dataConnName:foo dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$SyncDataConn cause:{State:NoneByCommitted Err:null} rollback:{State:NoneByNotRolledBack Err:null}}, {dataConnName:bar dataConnType:com.github.sttk.sabi.internal.DataConnManagerTest$AsyncDataConn cause:{State:CommitFailure Err:com.github.sttk.errs.Err { reason = java.lang.String YYY, file = DataConnManagerTest.java, line = 123 }} rollback:{State:NoneByRolledBack Err:null}}]");
+ assertThat(iter.next()).isEqualTo("AsyncDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataConn#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+}
diff --git a/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java b/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java
index 3227e6a..b5bd205 100644
--- a/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java
+++ b/src/test/java/com/github/sttk/sabi/internal/DataHubInnerTest.java
@@ -1,2080 +1,1596 @@
package com.github.sttk.sabi.internal;
+import static com.github.sttk.sabi.DataConn.FailToCommitDataConn;
+import static com.github.sttk.sabi.DataConn.FailToPostCommitDataConn;
+import static com.github.sttk.sabi.DataConn.FailToPreCommitDataConn;
+import static com.github.sttk.sabi.DataHub.CreatedDataConnIsNull;
+import static com.github.sttk.sabi.DataHub.FailToCastDataConn;
+import static com.github.sttk.sabi.DataHub.FailToCastDataHub;
+import static com.github.sttk.sabi.DataHub.FailToCreateDataConn;
+import static com.github.sttk.sabi.DataHub.FailToSetupGlobalDataSrcs;
+import static com.github.sttk.sabi.DataHub.FailToSetupLocalDataSrcs;
+import static com.github.sttk.sabi.DataHub.NoDataSrcToCreateDataConn;
+import static com.github.sttk.sabi.Sabi.setup;
+import static com.github.sttk.sabi.Sabi.uses;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.assertj.core.api.Assertions.fail;
import com.github.sttk.errs.Err;
import com.github.sttk.sabi.AsyncGroup;
import com.github.sttk.sabi.DataConn;
import com.github.sttk.sabi.DataHub;
import com.github.sttk.sabi.DataSrc;
+import com.github.sttk.sabi.TxnFailureReport;
import java.util.ArrayList;
import java.util.List;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
public class DataHubInnerTest {
private DataHubInnerTest() {}
- static final int FAIL__NOT = 0;
- static final int FAIL__SETUP = 1;
- static final int FAIL__CREATE_DATA_CONN = 2;
- static final int FAIL__COMMIT = 3;
- static final int FAIL__PRE_COMMIT = 4;
-
- final void suppressWarnings_unused(Object a) {}
+ enum Failure {
+ None,
+ PreCommit,
+ Commit,
+ PostCommit,
+ Rollback,
+ Setup,
+ CreateDataConn,
+ CreatedDataConnIsNull,
+ }
- static class SyncDataSrc implements DataSrc {
- private int id;
- private int fail;
- private List logger;
+ static class MyDataConn implements DataConn {
+ int id;
+ Failure failure;
+ boolean committed;
+ List logger;
- SyncDataSrc(int id, int fail, List logger) {
+ MyDataConn(int id, Failure failure, List logger) {
this.id = id;
- this.fail = fail;
+ this.failure = failure;
this.logger = logger;
+ this.committed = false;
}
@Override
- public void setup(AsyncGroup ag) throws Err {
- if (this.fail == FAIL__SETUP) {
- this.logger.add(String.format("SyncDataSrc %d failed to setup", this.id));
- throw new Err("XXX");
- }
- this.logger.add(String.format("SyncDataSrc %d setupped", this.id));
+ public boolean isCommitted() {
+ return this.committed;
}
@Override
- public void close() {
- this.logger.add(String.format("SyncDataSrc %d closed", this.id));
+ public void preCommit(AsyncGroup ag) throws Err {
+ if (this.failure == Failure.PreCommit) {
+ this.logger.add(String.format("MyDataConn#preCommit %d failed", this.id));
+ throw new Err("pre commit error");
+ }
+ this.logger.add(String.format("MyDataConn#preCommit %d", this.id));
}
@Override
- public DataConn createDataConn() throws Err {
- if (this.fail == FAIL__CREATE_DATA_CONN) {
- this.logger.add(String.format("SyncDataSrc %d failed to create a DataConn", this.id));
- throw new Err("xxx");
+ public void commit(AsyncGroup ag) throws Err {
+ if (this.failure == Failure.Commit) {
+ this.logger.add(String.format("MyDataConn#commit %d failed", this.id));
+ throw new Err("commit error");
}
- this.logger.add(String.format("SyncDataSrc %d created DataConn", this.id));
- var conn = new SyncDataConn(this.id, this.fail, this.logger);
- return conn;
+ this.logger.add(String.format("MyDataConn#commit %d", this.id));
}
- }
-
- static class AsyncDataSrc implements DataSrc {
- private int id;
- private int fail;
- private List logger;
- AsyncDataSrc(int id, int fail, List logger) {
- this.id = id;
- this.fail = fail;
- this.logger = logger;
+ @Override
+ public void postCommit(AsyncGroup ag) throws Err {
+ if (this.failure == Failure.PostCommit) {
+ this.logger.add(String.format("MyDataConn#postCommit %d failed", this.id));
+ throw new Err("post commit error");
+ }
+ this.logger.add(String.format("MyDataConn#postCommit %d", this.id));
}
@Override
- public void setup(AsyncGroup ag) throws Err {
- ag.add(
- () -> {
- try {
- Thread.sleep(50);
- } catch (Exception e) {
- }
- if (this.fail == FAIL__SETUP) {
- this.logger.add(String.format("AsyncDataSrc %d failed to setup", this.id));
- throw new Err("YYY");
- }
- this.logger.add(String.format("AsyncDataSrc %d setupped", this.id));
- });
+ public void rollback(AsyncGroup ag) throws Err {
+ if (this.failure == Failure.Rollback) {
+ this.logger.add(String.format("MyDataConn#rollback %d failed", this.id));
+ throw new Err("rollback error");
+ }
+ this.logger.add(String.format("MyDataConn#rollback %d", this.id));
}
@Override
- public void close() {
- this.logger.add(String.format("AsyncDataSrc %d closed", this.id));
+ public void onTxnFailure(AsyncGroup ag, List repots) {
+ this.logger.add(String.format("MyDataConn#onTxnFailure %d", this.id));
}
@Override
- public DataConn createDataConn() throws Err {
- if (this.fail == FAIL__CREATE_DATA_CONN) {
- this.logger.add(String.format("AsyncDataSrc %d failed to create a DataConn", this.id));
- throw new Err("yyy");
- }
- this.logger.add(String.format("AsyncDataSrc %d created DataConn", this.id));
- var conn = new AsyncDataConn(this.id, this.fail, this.logger);
- return conn;
+ public void close() {
+ this.logger.add(String.format("MyDataConn#close %d", this.id));
}
}
- static class SyncDataConn implements DataConn {
- private int id;
- private int fail;
- private boolean committed;
- private List logger;
+ static class MyDataSrc implements DataSrc {
+ int id;
+ Failure failure;
+ List logger;
- SyncDataConn(int id, int fail, List logger) {
+ MyDataSrc(int id, Failure failure, List logger) {
this.id = id;
- this.fail = fail;
+ this.failure = failure;
this.logger = logger;
}
@Override
- public void commit(AsyncGroup ag) throws Err {
- if (this.fail == FAIL__COMMIT) {
- this.logger.add(String.format("SyncDataConn %d failed to commit", this.id));
- throw new Err("ZZZ");
- }
- this.committed = true;
- this.logger.add(String.format("SyncDataConn %d committed", this.id));
- }
-
- @Override
- public void preCommit(AsyncGroup ag) throws Err {
- if (this.fail == FAIL__PRE_COMMIT) {
- this.logger.add(String.format("SyncDataConn %d failed to pre commit", this.id));
- throw new Err("zzz");
+ public void setup(AsyncGroup ag) throws Err {
+ if (this.failure == Failure.Setup) {
+ this.logger.add(String.format("MyDataSrc#setup %d failed", this.id));
+ throw new Err("setup error");
}
- this.logger.add(String.format("SyncDataConn %d pre committed", this.id));
+ this.logger.add(String.format("MyDataSrc#setup %d", this.id));
}
@Override
- public void postCommit(AsyncGroup ag) {
- this.logger.add(String.format("SyncDataConn %d post committed", this.id));
+ public void close() {
+ this.logger.add(String.format("MyDataSrc#close %d", this.id));
}
@Override
- public boolean shouldForceBack() {
- return this.committed;
+ public DataConn createDataConn() throws Err {
+ if (this.failure == Failure.CreateDataConn) {
+ this.logger.add(String.format("MyDataSrc#createDataConn %d failed", this.id));
+ throw new Err("eeee");
+ }
+ if (this.failure == Failure.CreatedDataConnIsNull) {
+ this.logger.add(String.format("MyDataSrc#createDataConn %d is null", this.id));
+ return null;
+ }
+ this.logger.add(String.format("MyDataSrc#createDataConn %d", this.id));
+ return new MyDataConn(this.id, this.failure, this.logger);
}
+ }
- @Override
- public void rollback(AsyncGroup ag) {
- this.logger.add(String.format("SyncDataConn %d rollbacked", this.id));
- }
+ static class BadDataConn implements DataConn {
+ BadDataConn(int id, Failure failure, List logger) {}
@Override
- public void forceBack(AsyncGroup ag) {
- this.logger.add(String.format("SyncDataConn %d forced back", this.id));
+ public boolean isCommitted() {
+ return true;
}
@Override
- public void close() {
- this.logger.add(String.format("SyncDataConn %d closed", this.id));
- }
- }
-
- static class AsyncDataConn implements DataConn {
- private int id;
- private int fail;
- private boolean committed;
- private List logger;
-
- AsyncDataConn(int id, int fail, List logger) {
- this.id = id;
- this.fail = fail;
- this.logger = logger;
- }
+ public void preCommit(AsyncGroup ag) throws Err {}
@Override
- public void commit(AsyncGroup ag) throws Err {
- if (this.fail == FAIL__COMMIT) {
- this.logger.add(String.format("AsyncDataConn %d failed to commit", this.id));
- throw new Err("VVV");
- }
- this.committed = true;
- this.logger.add(String.format("AsyncDataConn %d committed", this.id));
- }
+ public void commit(AsyncGroup ag) throws Err {}
@Override
- public void preCommit(AsyncGroup ag) throws Err {
- if (this.fail == FAIL__PRE_COMMIT) {
- this.logger.add(String.format("AsyncDataConn %d failed to pre commit", this.id));
- throw new Err("vvv");
- }
- this.logger.add(String.format("AsyncDataConn %d pre committed", this.id));
- }
+ public void postCommit(AsyncGroup ag) throws Err {}
@Override
- public void postCommit(AsyncGroup ag) {
- this.logger.add(String.format("AsyncDataConn %d post committed", this.id));
- }
+ public void rollback(AsyncGroup ag) throws Err {}
@Override
- public boolean shouldForceBack() {
- return this.committed;
- }
+ public void onTxnFailure(AsyncGroup ag, List repots) {}
@Override
- public void rollback(AsyncGroup ag) {
- this.logger.add(String.format("AsyncDataConn %d rollbacked", this.id));
- }
+ public void close() {}
+ }
- @Override
- public void forceBack(AsyncGroup ag) {
- this.logger.add(String.format("AsyncDataConn %d forced back", this.id));
- }
+ static interface FailToCastData {}
- @Override
- public void close() {
- this.logger.add(String.format("AsyncDataConn %d closed", this.id));
+ int countDs(List list) {
+ int n = 0;
+ for (var cont : list) {
+ if (cont.ds != null) {
+ n++;
+ }
}
+ return n;
}
- static void resetGlobalVariables() {
+ static void resetGlobals() {
+ DataHubInner.GLOBAL_DATA_SRC_MANAGER.close();
DataHubInner.GLOBAL_DATA_SRCS_FIXED.set(false);
- DataHubInner.GLOBAL_DATA_SRC_LIST.closeDataSrcs();
}
- @Nested
- class TestOfGlobalFunctions {
- @BeforeEach
- void beforeEach() {
- resetGlobalVariables();
+ ///
+
+ @Test
+ void testNewDataHubImpl() {
+ var hub = new DataHubInner();
+ try {
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
+ } finally {
+ hub.closeLocals();
}
+ }
- @AfterEach
- void afterEach() {
- resetGlobalVariables();
+ @Test
+ void testNewDataHubWithCommitOrder() {
+ var hub = new DataHubInner(List.of("bar", "qux", "foo"));
+ try {
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).hasSize(3);
+ assertThat(hub.dataConnManager.indexMap).hasSize(3);
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
+ } finally {
+ hub.closeLocals();
}
+ }
- @Test
- void setup_and_shutdown() {
- var logger = new ArrayList();
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
-
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- var ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("bar");
- ptr = ptr.next;
- assertThat(ptr).isNull();
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
-
- ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("bar");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ @Test
+ void testUsesAndOk() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.useLocal("bar", new MyDataSrc(2, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(2);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ hub.begin();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
}
+ }
- @Test
- void fail_to_setup() {
- var logger = new ArrayList();
+ @Test
+ void testUsesButAlreadyFixed() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(1);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ hub.begin();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(1);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(1);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ hub.useLocal("bar", new MyDataSrc(2, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(1);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(1);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
+ }
+ }
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__SETUP, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__SETUP, logger));
+ @Test
+ void testDisuseAndOk() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.useLocal("bar", new MyDataSrc(2, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(2);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- var ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("bar");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ hub.disuseLocal("foo");
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(1);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToSetupGlobalDataSrcs r -> {
- assertThat(r.errors()).hasSize(2);
- assertThat(r.errors().get("foo").getReason()).isEqualTo("YYY");
- assertThat(r.errors().get("bar").getReason()).isEqualTo("XXX");
- }
- default -> fail();
- }
- } catch (Exception e) {
- fail(e);
- }
+ hub.disuseLocal("bar");
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- assertThat(logger)
- .containsExactly("SyncDataSrc 2 failed to setup", "AsyncDataSrc 1 failed to setup");
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
}
+ }
- @Test
- void cannot_add_global_data_srcs_after_setup() {
- var logger = new ArrayList();
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ @Test
+ void testDisuseAndFix() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.useLocal("bar", new MyDataSrc(2, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(2);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
+ hub.disuseLocal("foo");
- var ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(1);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ hub.disuseLocal("bar");
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.useLocal("bar", new MyDataSrc(2, Failure.None, logger));
+
+ hub.begin();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ hub.disuseLocal("foo");
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ hub.disuseLocal("bar");
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ hub.end();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
+ hub.disuseLocal("foo");
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(1);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(1);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ hub.disuseLocal("bar");
- } catch (Exception e) {
- fail(e);
- }
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- assertThat(logger).containsExactly("AsyncDataSrc 1 setupped", "AsyncDataSrc 1 closed");
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
}
+ }
- @Test
- void do_nothing_if_executing_setup_twice() {
- var logger = new ArrayList();
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
-
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
-
- var ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ @Test
+ void testBeginIfEmpty() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.begin();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ hub.end();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
+ }
+ }
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
+ @Test
+ void testBeginAndOk() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.useLocal("bar", new MyDataSrc(2, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(2);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ hub.begin();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isTrue();
+
+ hub.end();
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(0);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(2);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).hasSize(2);
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
+ }
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
+ assertThat(logger).hasSize(4);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
- ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
+ @Test
+ void testBeginButFailed() {
+ var logger = new ArrayList();
+
+ var hub = new DataHubInner();
+ try {
+ hub.useLocal("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.useLocal("bar", new MyDataSrc(2, Failure.Setup, logger));
+ hub.useLocal("baz", new MyDataSrc(3, Failure.None, logger));
+
+ assertThat(countDs(hub.localDataSrcManager.listUnready)).isEqualTo(3);
+ assertThat(countDs(hub.localDataSrcManager.listReady)).isEqualTo(0);
+ assertThat(hub.localDataSrcManager.local).isTrue();
+ assertThat(hub.dataSrcMap).isEmpty();
+ assertThat(hub.dataConnManager.list).isEmpty();
+ assertThat(hub.dataConnManager.indexMap).isEmpty();
+ assertThat(hub.dataConnMap).isEmpty();
+ assertThat(hub.fixed).isFalse();
- } catch (Exception e) {
- fail(e);
+ try {
+ hub.begin();
+ fail();
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToSetupLocalDataSrcs rsn -> {
+ assertThat(rsn.errors()).hasSize(1);
+ var ee = rsn.errors().get(0);
+ assertThat(ee.index).isEqualTo(1);
+ assertThat(ee.name).isEqualTo("bar");
+ assertThat(ee.err.getReason()).isEqualTo("setup error");
+ }
+ default -> fail(err);
+ }
+ } finally {
+ hub.end();
}
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
-
- assertThat(logger).containsExactly("AsyncDataSrc 1 setupped", "AsyncDataSrc 1 closed");
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ hub.closeLocals();
}
+
+ assertThat(logger).hasSize(3);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2 failed");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Nested
- class TestOfDataHubLocal {
- @BeforeEach
- void beforeEach() {
- resetGlobalVariables();
- }
-
- @AfterEach
- void afterEach() {
- resetGlobalVariables();
- }
-
+ class RunTest {
@Test
- void new_and_close_with_no_global_data_srcs() {
- var hub = new DataHubInner();
-
- assertThat(hub.localDataSrcList.notSetupHead).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(0);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
+ void testRunAndOk() {
+ var logger = new ArrayList();
- hub.close();
- }
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
- @Test
- void new_and_close_with_global_data_srcs() {
- var logger = new ArrayList();
+ hub.run(
+ data -> {
+ logger.add("execute logic");
+ });
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
-
- var ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("bar");
- ptr = ptr.next;
- assertThat(ptr).isNull();
-
- var hub = new DataHubInner();
-
- assertThat(hub.localDataSrcList.notSetupHead).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
-
- ptr = DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("bar");
- ptr = ptr.next;
- assertThat(ptr).isNull();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(5);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void uses_and_disuses() {
+ void testRunButFailedToRunLogic() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- assertThat(hub.localDataSrcList.notSetupHead).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.uses("baz", new SyncDataSrc(3, FAIL__NOT, logger));
- var ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.uses("qux", new AsyncDataSrc(4, FAIL__NOT, logger));
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.disuses("foo"); // do nothing because of global
- hub.disuses("bar"); // do nothing because of global
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.disuses("baz");
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.disuses("qux");
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.close();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
+
+ hub.run(
+ data -> {
+ logger.add("execute logic but fail");
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case String s -> {
+ assertThat(s).isEqualTo("logic error but fail");
+ }
+ default -> fail(err);
+ }
} catch (Exception e) {
fail(e);
}
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.notSetupHead).isNull();
- assertThat(DataHubInner.GLOBAL_DATA_SRC_LIST.didSetupHead).isNull();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 3 closed",
- "AsyncDataSrc 4 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(5);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic but fail");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void cannot_add_and_remove_data_src_between_begin_and_end() {
+ void testRunButFailToCastToSpecifiedDataHub() {
var logger = new ArrayList();
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- assertThat(hub.localDataSrcList.notSetupHead).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(0);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.uses("baz", new SyncDataSrc(1, FAIL__NOT, logger));
- var ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.localDataSrcList.didSetupHead).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(0);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- try {
- hub.begin();
- } catch (Exception e) {
- fail(e);
- }
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(1);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isTrue();
-
- hub.uses("foo", new AsyncDataSrc(2, FAIL__NOT, logger));
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(1);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isTrue();
-
- hub.disuses("baz");
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(1);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isTrue();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
- hub.end();
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(1);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.uses("foo", new AsyncDataSrc(2, FAIL__NOT, logger));
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(1);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.disuses("baz");
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("foo");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNull();
- assertThat(hub.dataConnList.head).isNull();
- assertThat(hub.dataSrcMap).hasSize(0);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.close();
+ hub.run((FailToCastData data) -> {});
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCastDataHub r -> {
+ assertThat(r.fromType()).isEqualTo(DataHub.class.getName());
+ }
+ default -> fail(err);
+ }
} catch (Exception e) {
fail(e);
}
- assertThat(logger).containsExactly("SyncDataSrc 1 setupped", "SyncDataSrc 1 closed");
+ assertThat(logger).hasSize(4);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void begin_and_end() {
+ void testRunButFailToSetup() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new SyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new AsyncDataSrc(4, FAIL__NOT, logger));
-
- var ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(2);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- try {
- hub.begin();
- } catch (Exception e) {
- fail(e);
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.Setup, logger));
+
+ hub.run(
+ data -> {
+ logger.add("execute logic");
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToSetupLocalDataSrcs r -> {
+ assertThat(r.errors()).hasSize(1);
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("foo");
+ assertThat(r.errors().get(0).err.toString())
+ .isEqualTo(
+ "com.github.sttk.errs.Err { reason = java.lang.String setup error, file = DataHubInnerTest.java, line = 123 }");
+ }
+ default -> fail(err);
}
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(4);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isTrue();
-
- hub.end();
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(4);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 3 setupped",
- "AsyncDataSrc 4 setupped",
- "AsyncDataSrc 4 closed",
- "SyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(1);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1 failed");
+ assertThat(iter.hasNext()).isFalse();
}
+ }
+ @Nested
+ class TxnTest {
@Test
- void begin_and_end_but_fail_sync() {
+ void testTxnAndNoDataAccessAndOk() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__SETUP, logger));
-
- try {
- hub.begin();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToSetupLocalDataSrcs rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("qux");
- assertThat(e2.getReason()).isEqualTo("XXX");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- var ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(3);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isTrue();
-
- hub.end();
-
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(3);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.close();
+ hub.txn(
+ data -> {
+ logger.add("execute logic");
+ });
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 failed to setup",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(5);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void begin_and_end_but_fail_async() {
+ void testTxnAndHasDataAccessAndOk() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__SETUP, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- try {
- hub.begin();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToSetupLocalDataSrcs rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("baz");
- assertThat(e2.getReason()).isEqualTo("YYY");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
- var ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(3);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isTrue();
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- hub.end();
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- ptr = hub.localDataSrcList.notSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("baz");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- ptr = hub.localDataSrcList.didSetupHead;
- assertThat(ptr).isNotNull();
- assertThat(ptr.name).isEqualTo("qux");
- ptr = ptr.next;
- assertThat(ptr).isNull();
- assertThat(hub.dataSrcMap).hasSize(3);
- assertThat(hub.dataConnMap).hasSize(0);
- assertThat(hub.fixed).isFalse();
-
- hub.close();
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
+ });
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 failed to setup",
- "SyncDataSrc 4 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(15);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void commit() {
+ void testTxnButFailedToRunLogic() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- try {
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- ///
-
- conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
- conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
- hub.commit();
- } catch (Exception e) {
- fail(e);
+ throw new Err("logic error");
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case String s -> {
+ assertThat(s).isEqualTo("logic error");
+ }
+ default -> fail(err);
}
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 pre committed",
- "AsyncDataConn 1 committed",
- "SyncDataConn 2 committed",
- "AsyncDataConn 3 committed",
- "SyncDataConn 4 committed",
- "AsyncDataConn 1 post committed",
- "SyncDataConn 2 post committed",
- "AsyncDataConn 3 post committed",
- "SyncDataConn 4 post committed",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(13);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void fail_to_cast_new_data_conn() {
+ void testTxnButFailedToPreCommit() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.PreCommit, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.PreCommit, logger));
- hub.uses("bar", new SyncDataSrc(2, FAIL__NOT, logger));
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- try {
- hub.begin();
- } catch (Exception e) {
- fail(e);
- }
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- try {
- hub.getDataConn("foo", SyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCastDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("foo");
- assertThat(rsn.castToType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$SyncDataConn");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
+ });
- try {
- hub.getDataConn("bar", AsyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCastDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("bar");
- assertThat(rsn.castToType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$AsyncDataConn");
- }
- default -> fail(e);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPreCommitDataConn r -> {
+ assertThat(r.errors()).hasSize(1);
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("foo");
+ assertThat(r.errors().get(0).err.getReason()).isEqualTo("pre commit error");
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(14);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 1 failed");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void fail_to_cast_reused_data_conn() {
+ void testTxnButFailedToCommit() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("bar", new SyncDataSrc(2, FAIL__NOT, logger));
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.Commit, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.Commit, logger));
- try {
- hub.begin();
- } catch (Exception e) {
- fail(e);
- }
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isInstanceOf(AsyncDataConn.class);
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isInstanceOf(SyncDataConn.class);
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
+ });
- try {
- hub.getDataConn("foo", SyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCastDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("foo");
- assertThat(rsn.castToType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$SyncDataConn");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
-
- try {
- hub.getDataConn("bar", AsyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCastDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("bar");
- assertThat(rsn.castToType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$AsyncDataConn");
- }
- default -> fail(e);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCommitDataConn r -> {
+ assertThat(r.errors()).hasSize(1);
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("foo");
+ assertThat(r.errors().get(0).err.getReason()).isEqualTo("commit error");
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(16);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 1 failed");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void fail_to_create_data_conn() {
+ void testTxnButFailedToPostCommit() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.PostCommit, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.PostCommit, logger));
- hub.uses("bar", new SyncDataSrc(2, FAIL__CREATE_DATA_CONN, logger));
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- try {
- hub.begin();
- } catch (Exception e) {
- fail(e);
- }
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isInstanceOf(AsyncDataConn.class);
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
+ });
- try {
- hub.getDataConn("bar", AsyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCreateDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("bar");
- assertThat(rsn.dataConnType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$AsyncDataConn");
- }
- default -> fail(e);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToPostCommitDataConn r -> {
+ assertThat(r.errors()).hasSize(2);
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("foo");
+ assertThat(r.errors().get(0).err.getReason()).isEqualTo("post commit error");
+ assertThat(r.errors().get(1).index).isEqualTo(1);
+ assertThat(r.errors().get(1).name).isEqualTo("bar");
+ assertThat(r.errors().get(1).err.getReason()).isEqualTo("post commit error");
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 failed to create a DataConn",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(17);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 1 failed");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 2 failed");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void fail_to_create_data_conn_because_of_no_data_src() {
+ void testButFailedToRollback() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.Rollback, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.Rollback, logger));
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- hub.uses("bar", new SyncDataSrc(2, FAIL__NOT, logger));
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- try {
- hub.begin();
- } catch (Exception e) {
- fail(e);
- }
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
- try {
- hub.getDataConn("baz", SyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.NoDataSrcToCreateDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("baz");
- assertThat(rsn.dataConnType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$SyncDataConn");
- }
- default -> fail(e);
- }
- } catch (Exception e) {
- fail(e);
- }
+ throw new Err("logic error");
+ });
- try {
- hub.getDataConn("qux", AsyncDataConn.class);
- fail();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.NoDataSrcToCreateDataConn rsn -> {
- assertThat(rsn.name()).isEqualTo("qux");
- assertThat(rsn.dataConnType())
- .isEqualTo("com.github.sttk.sabi.internal.DataHubInnerTest$AsyncDataConn");
- }
- default -> fail(e);
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case String s -> {
+ assertThat(s).isEqualTo("logic error");
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 2 setupped",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(13);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 1 failed");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 2 failed");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void commit_when_no_data_conn() {
+ void testWithCommitOrder() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
+ try (var hub = new DataHub("bar", "foo")) {
+ hub.uses("foo", new MyDataSrc(1, Failure.Rollback, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.Rollback, logger));
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- try {
- hub.begin();
- hub.commit();
- hub.end();
- } catch (Exception e) {
- fail(e);
- }
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", MyDataConn.class);
+ });
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(15);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void commit_but_fail_global_sync() {
+ void testTxnButFailToCastToSpecifiedDataHub() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__COMMIT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("bar");
- assertThat(e2.getReason()).isEqualTo("ZZZ");
- }
- default -> fail();
+ hub.txn((FailToCastData data) -> {});
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCastDataHub r -> {
+ assertThat(r.fromType()).isEqualTo(DataHub.class.getName());
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 pre committed",
- "AsyncDataConn 1 committed",
- "SyncDataConn 2 failed to commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(4);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void commit_but_fail_global_async() {
+ void testTxnButFailToSetup() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__COMMIT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("foo");
- assertThat(e2.getReason()).isEqualTo("VVV");
- }
- default -> fail();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.Setup, logger));
+
+ hub.txn(
+ (FailToCastData data) -> {
+ logger.add("execute logic");
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToSetupLocalDataSrcs r -> {
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("foo");
+ assertThat(r.errors().get(0).err.getReason()).isEqualTo("setup error");
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 pre committed",
- "AsyncDataConn 1 failed to commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(1);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1 failed");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void commit_but_fail_local_sync() {
+ void testGetDataConnCached() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__COMMIT, logger));
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
- hub.begin();
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("foo", MyDataConn.class);
+ });
+ } catch (Exception e) {
+ fail(e);
+ }
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#preCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#commit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#postCommit 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
+ @Test
+ void testGetDataConnAndNoDataSrcToCreateDataConn() {
+ var logger = new ArrayList();
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("qux");
- assertThat(e2.getReason()).isEqualTo("ZZZ");
- }
- default -> fail();
+ try (var hub = new DataHub()) {
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
+
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case NoDataSrcToCreateDataConn r -> {
+ assertThat(r.name()).isEqualTo("foo");
+ assertThat(r.dataConnType()).isEqualTo(MyDataConn.class.getName());
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 pre committed",
- "AsyncDataConn 1 committed",
- "SyncDataConn 2 committed",
- "AsyncDataConn 3 committed",
- "SyncDataConn 4 failed to commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(1);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void commit_but_fail_local_async() {
+ void testGetDataConnAndCreatedDataConnIsNull() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__COMMIT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("baz");
- assertThat(e2.getReason()).isEqualTo("VVV");
- }
- default -> fail();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.CreatedDataConnIsNull, logger));
+
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
+
+ @SuppressWarnings("unused")
+ var dc = data.getDataConn("foo", MyDataConn.class);
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case CreatedDataConnIsNull r -> {
+ assertThat(r.name()).isEqualTo("foo");
+ assertThat(r.dataConnType()).isEqualTo(MyDataConn.class.getName());
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 pre committed",
- "AsyncDataConn 1 committed",
- "SyncDataConn 2 committed",
- "AsyncDataConn 3 failed to commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(4);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1 is null");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void pre_commit_but_fail_global_sync() {
+ void testGetDataConnAndFailedToCreateDataConn() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__PRE_COMMIT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToPreCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("bar");
- assertThat(e2.getReason()).isEqualTo("zzz");
- }
- default -> fail();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.CreateDataConn, logger));
+
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
+
+ @SuppressWarnings("unused")
+ var dc = data.getDataConn("foo", MyDataConn.class);
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCreateDataConn r -> {
+ assertThat(r.name()).isEqualTo("foo");
+ assertThat(r.dataConnType()).isEqualTo(MyDataConn.class.getName());
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 failed to pre commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(4);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1 failed");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void pre_commit_but_fail_global_async() {
+ void testGetDataConnAndFailedToCastDataConn() {
var logger = new ArrayList();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__PRE_COMMIT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToPreCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("bar");
- assertThat(e2.getReason()).isEqualTo("zzz");
- }
- default -> fail();
+ try (var hub = new DataHub()) {
+ hub.uses("foo", new MyDataSrc(1, Failure.None, logger));
+ hub.uses("bar", new MyDataSrc(2, Failure.None, logger));
+
+ hub.txn(
+ (DataHub data) -> {
+ logger.add("execute logic");
+
+ @SuppressWarnings("unused")
+ var dc1 = data.getDataConn("foo", MyDataConn.class);
+
+ @SuppressWarnings("unused")
+ var dc2 = data.getDataConn("bar", BadDataConn.class);
+ });
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToCastDataConn r -> {
+ assertThat(r.name()).isEqualTo("bar");
+ assertThat(r.fromDataConnType()).isEqualTo(MyDataConn.class.getName());
+ assertThat(r.toDataConnType()).isEqualTo(BadDataConn.class.getName());
}
- } catch (Exception e) {
- fail(e);
+ default -> fail(err);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 failed to pre commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(13);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("execute logic");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#createDataConn 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#rollback 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 1");
+ assertThat(iter.next()).isEqualTo("MyDataConn#onTxnFailure 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataConn#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
+ }
+
+ @Nested
+ class TestGlobals {
@Test
- void pre_commit_but_fail_local_sync() {
+ void testUsesAndSetupAndOk() {
var logger = new ArrayList();
-
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__PRE_COMMIT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToPreCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("qux");
- assertThat(e2.getReason()).isEqualTo("zzz");
- }
- default -> fail();
- }
- } catch (Exception e) {
- fail(e);
+ try {
+ resetGlobals();
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+
+ uses("foo", new MyDataSrc(1, Failure.None, logger));
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(1);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(0);
+
+ try (var ac = setup()) {
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(0);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(1);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
+ } finally {
+ resetGlobals();
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 failed to pre commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(2);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void pre_commit_but_fail_local_async() {
+ void testUsesAndSetupButFail() {
var logger = new ArrayList();
+ try {
+ resetGlobals();
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
- hub.uses("baz", new AsyncDataSrc(3, FAIL__PRE_COMMIT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
+ uses("foo", new MyDataSrc(1, Failure.Setup, logger));
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(1);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(0);
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- try {
- hub.commit();
- } catch (Err e) {
- switch (e.getReason()) {
- case DataHub.FailToPreCommitDataConn rsn -> {
- assertThat(rsn.errors()).hasSize(1);
- var e2 = rsn.errors().get("baz");
- assertThat(e2.getReason()).isEqualTo("vvv");
+ try (var ac = setup()) {
+ fail();
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToSetupGlobalDataSrcs r -> {
+ assertThat(r.errors()).hasSize(1);
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("foo");
+ assertThat(r.errors().get(0).err.getReason()).isEqualTo("setup error");
}
- default -> fail();
+ default -> fail(err);
}
- } catch (Exception e) {
- fail(e);
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(0);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(0);
}
-
- hub.end();
-
- hub.close();
} catch (Exception e) {
fail(e);
+ } finally {
+ resetGlobals();
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 failed to pre commit",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(1);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1 failed");
+ assertThat(iter.hasNext()).isFalse();
}
@Test
- void rollback() {
+ void testUsesAndSetupButAlreadyFixedBefore() {
var logger = new ArrayList();
-
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
-
- hub.begin();
-
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
-
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
-
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
-
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
-
- hub.rollback();
- hub.end();
-
- hub.close();
+ try {
+ resetGlobals();
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+
+ try (var ac = setup()) {
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+
+ uses("foo", new MyDataSrc(1, Failure.Setup, logger));
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+ }
} catch (Exception e) {
fail(e);
+ } finally {
+ resetGlobals();
}
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 rollbacked",
- "SyncDataConn 2 rollbacked",
- "AsyncDataConn 3 rollbacked",
- "SyncDataConn 4 rollbacked",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
+ assertThat(logger).hasSize(0);
}
@Test
- void force_back() {
+ void testUsesAndSetupWithOrderAndOk() {
var logger = new ArrayList();
+ try {
+ resetGlobals();
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+
+ uses("foo", new MyDataSrc(1, Failure.None, logger));
+ uses("bar", new MyDataSrc(2, Failure.None, logger));
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(2);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(0);
+
+ try (var ac = setup("bar", "foo")) {
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(0);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(2);
+ }
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ resetGlobals();
+ }
- DataHubInner.usesGlobal("foo", new AsyncDataSrc(1, FAIL__NOT, logger));
- DataHubInner.usesGlobal("bar", new SyncDataSrc(2, FAIL__NOT, logger));
-
- try (var ac = DataHubInner.setupGlobals()) {
- suppressWarnings_unused(ac);
- var hub = new DataHubInner();
-
- hub.uses("baz", new AsyncDataSrc(3, FAIL__NOT, logger));
- hub.uses("qux", new SyncDataSrc(4, FAIL__NOT, logger));
+ assertThat(logger).hasSize(4);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 1");
+ assertThat(iter.next()).isEqualTo("MyDataSrc#close 2");
+ assertThat(iter.hasNext()).isFalse();
+ }
- hub.begin();
+ @Test
+ void testUsesAndSetupWithOrderButFail() {
+ var logger = new ArrayList();
+ try {
+ resetGlobals();
- var conn1 = hub.getDataConn("foo", AsyncDataConn.class);
- assertThat(conn1).isNotNull();
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
- var conn2 = hub.getDataConn("bar", SyncDataConn.class);
- assertThat(conn2).isNotNull();
+ uses("foo", new MyDataSrc(1, Failure.Setup, logger));
+ uses("bar", new MyDataSrc(2, Failure.Setup, logger));
- var conn3 = hub.getDataConn("baz", AsyncDataConn.class);
- assertThat(conn3).isNotNull();
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(2);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(0);
- var conn4 = hub.getDataConn("qux", SyncDataConn.class);
- assertThat(conn4).isNotNull();
+ try (var ac = setup("bar", "foo")) {
+ fail();
+ } catch (Err err) {
+ switch (err.getReason()) {
+ case FailToSetupGlobalDataSrcs r -> {
+ assertThat(r.errors()).hasSize(1);
+ assertThat(r.errors().get(0).index).isEqualTo(0);
+ assertThat(r.errors().get(0).name).isEqualTo("bar");
+ assertThat(r.errors().get(0).err.getReason()).isEqualTo("setup error");
+ }
+ default -> fail(err);
+ }
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).hasSize(0);
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).hasSize(0);
+ }
+ } catch (Exception e) {
+ fail(e);
+ } finally {
+ resetGlobals();
+ }
- hub.commit();
- hub.rollback();
- hub.end();
+ assertThat(logger).hasSize(1);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("MyDataSrc#setup 2 failed");
+ assertThat(iter.hasNext()).isFalse();
+ }
- hub.close();
+ @Test
+ void testUsesAndSetupWithOrderButAlreadyFixedBefore() {
+ var logger = new ArrayList();
+ try {
+ resetGlobals();
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+
+ try (var ac = setup("bar", "foo")) {
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+
+ uses("foo", new MyDataSrc(1, Failure.Setup, logger));
+
+ assertThat(DataHubInner.GLOBAL_DATA_SRCS_FIXED.get()).isTrue();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.local).isFalse();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listUnready).isEmpty();
+ assertThat(DataHubInner.GLOBAL_DATA_SRC_MANAGER.listReady).isEmpty();
+ }
} catch (Exception e) {
fail(e);
+ } finally {
+ resetGlobals();
}
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "SyncDataSrc 4 setupped",
- "AsyncDataSrc 3 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "AsyncDataSrc 3 created DataConn",
- "SyncDataSrc 4 created DataConn",
- "AsyncDataConn 1 pre committed",
- "SyncDataConn 2 pre committed",
- "AsyncDataConn 3 pre committed",
- "SyncDataConn 4 pre committed",
- "AsyncDataConn 1 committed",
- "SyncDataConn 2 committed",
- "AsyncDataConn 3 committed",
- "SyncDataConn 4 committed",
- "AsyncDataConn 1 post committed",
- "SyncDataConn 2 post committed",
- "AsyncDataConn 3 post committed",
- "SyncDataConn 4 post committed",
- "AsyncDataConn 1 forced back",
- "SyncDataConn 2 forced back",
- "AsyncDataConn 3 forced back",
- "SyncDataConn 4 forced back",
- "SyncDataConn 4 closed",
- "AsyncDataConn 3 closed",
- "SyncDataConn 2 closed",
- "AsyncDataConn 1 closed",
- "SyncDataSrc 4 closed",
- "AsyncDataSrc 3 closed",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
}
}
}
diff --git a/src/test/java/com/github/sttk/sabi/internal/DataSrcListTest.java b/src/test/java/com/github/sttk/sabi/internal/DataSrcListTest.java
deleted file mode 100644
index da381b7..0000000
--- a/src/test/java/com/github/sttk/sabi/internal/DataSrcListTest.java
+++ /dev/null
@@ -1,1000 +0,0 @@
-package com.github.sttk.sabi.internal;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import com.github.sttk.errs.Err;
-import com.github.sttk.sabi.AsyncGroup;
-import com.github.sttk.sabi.DataConn;
-import com.github.sttk.sabi.DataSrc;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import org.junit.jupiter.api.Test;
-
-public class DataSrcListTest {
- private DataSrcListTest() {}
-
- static class SyncDataSrc implements DataSrc {
- int id;
- boolean willFail;
- List logger;
-
- SyncDataSrc(int id, List logger, boolean willFail) {
- this.id = id;
- this.willFail = willFail;
- this.logger = logger;
- }
-
- @Override
- public void setup(AsyncGroup ag) throws Err {
- if (this.willFail) {
- logger.add(String.format("SyncDataSrc %d failed to setup", this.id));
- throw new Err("XXX");
- }
- logger.add(String.format("SyncDataSrc %d setupped", this.id));
- }
-
- @Override
- public void close() {
- logger.add(String.format("SyncDataSrc %d closed", this.id));
- }
-
- @Override
- public DataConn createDataConn() throws Err {
- logger.add(String.format("SyncDataSrc %d created DataConn", this.id));
- var conn = new SyncDataConn();
- return conn;
- }
- }
-
- static class AsyncDataSrc implements DataSrc {
- int id;
- boolean willFail;
- List logger;
-
- AsyncDataSrc(int id, List logger, boolean willFail) {
- this.id = id;
- this.willFail = willFail;
- this.logger = logger;
- }
-
- @Override
- public void setup(AsyncGroup ag) throws Err {
- ag.add(
- () -> {
- try {
- Thread.sleep(50);
- } catch (Exception e) {
- }
- if (this.willFail) {
- logger.add(String.format("AsyncDataSrc %d failed to setup", this.id));
- throw new Err("XXX");
- }
- logger.add(String.format("AsyncDataSrc %d setupped", this.id));
- });
- }
-
- @Override
- public void close() {
- logger.add(String.format("AsyncDataSrc %d closed", this.id));
- }
-
- @Override
- public DataConn createDataConn() throws Err {
- logger.add(String.format("AsyncDataSrc %d created DataConn", this.id));
- var conn = new AsyncDataConn();
- return conn;
- }
- }
-
- static class SyncDataConn implements DataConn {
- @Override
- public void commit(AsyncGroup ag) throws Err {}
-
- @Override
- public void preCommit(AsyncGroup ag) throws Err {}
-
- @Override
- public void postCommit(AsyncGroup ag) {}
-
- @Override
- public boolean shouldForceBack() {
- return false;
- }
-
- @Override
- public void rollback(AsyncGroup ag) {}
-
- @Override
- public void forceBack(AsyncGroup ag) {}
-
- @Override
- public void close() {}
- }
-
- static class AsyncDataConn implements DataConn {
- @Override
- public void commit(AsyncGroup ag) throws Err {}
-
- @Override
- public void preCommit(AsyncGroup ag) throws Err {}
-
- @Override
- public void postCommit(AsyncGroup ag) {}
-
- @Override
- public boolean shouldForceBack() {
- return false;
- }
-
- @Override
- public void rollback(AsyncGroup ag) {}
-
- @Override
- public void forceBack(AsyncGroup ag) {}
-
- @Override
- public void close() {}
- }
-
- @Test
- void test_new() {
- var dsList = new DataSrcList(false);
-
- assertThat(dsList.local).isEqualTo(false);
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
- }
-
- @Test
- void test_appendContainerPtrNotSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrNotSetup(ptr1);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr1);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isNull();
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrNotSetup(ptr2);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr2);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isNull();
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeHeadContainerPtrNotSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrNotSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrNotSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrNotSetup(ptr1);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr2);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr2.prev).isNull();
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeMiddleContainerPtrNotSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrNotSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrNotSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrNotSetup(ptr2);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr1);
- assertThat(ptr3.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeLastContainerPtrNotSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrNotSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrNotSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr2);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeAllContainerPtrNotSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrNotSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrNotSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrNotSetup(ptr1);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr2);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr2.prev).isNull();
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrNotSetup(ptr2);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr3);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr3.prev).isNull();
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrNotSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeAndCloseLocalContainerPtrNotSetupByName() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrNotSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrNotSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrNotSetup(ptr3);
-
- var ds4 = new SyncDataSrc(4, logger, false);
- var ptr4 = new DataSrcContainer(false, "qux", ds4);
- dsList.appendContainerPtrNotSetup(ptr4);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr4);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isEqualTo(ptr4);
- assertThat(ptr4.prev).isEqualTo(ptr3);
- assertThat(ptr4.next).isNull();
-
- dsList.removeAndCloseContainerPtrNotSetupByName("bar");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr1);
- assertThat(dsList.notSetupLast).isEqualTo(ptr4);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr1);
- assertThat(ptr3.next).isEqualTo(ptr4);
- assertThat(ptr4.prev).isEqualTo(ptr3);
- assertThat(ptr4.next).isNull();
-
- dsList.removeAndCloseContainerPtrNotSetupByName("foo");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr3);
- assertThat(dsList.notSetupLast).isEqualTo(ptr4);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr3.prev).isNull();
- assertThat(ptr3.next).isEqualTo(ptr4);
- assertThat(ptr4.prev).isEqualTo(ptr3);
- assertThat(ptr4.next).isNull();
-
- dsList.removeAndCloseContainerPtrNotSetupByName("qux");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isEqualTo(ptr3);
- assertThat(dsList.notSetupLast).isEqualTo(ptr3);
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- assertThat(ptr3.prev).isNull();
- assertThat(ptr3.next).isNull();
-
- dsList.removeAndCloseContainerPtrNotSetupByName("baz");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- dsList.closeDataSrcs();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 closed",
- "SyncDataSrc 1 closed",
- "SyncDataSrc 4 closed",
- "SyncDataSrc 3 closed");
- }
-
- @Test
- void test_appendContainerPtrDidSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr1);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isNull();
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr2);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isNull();
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeHeadContainerPtrDidSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrDidSetup(ptr1);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr2);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr2.prev).isNull();
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeMiddleContainerPtrDidSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrDidSetup(ptr2);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr1);
- assertThat(ptr3.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeLastContainerPtrDidSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr2);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeAllContainerPtrDidSetup() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrDidSetup(ptr1);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr2);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr2.prev).isNull();
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrDidSetup(ptr2);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr3);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr3.prev).isNull();
- assertThat(ptr3.next).isNull();
-
- dsList.removeContainerPtrDidSetup(ptr3);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_removeAndCloseLocalContainerPtrDidSetupByName() {
- var dsList = new DataSrcList(false);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- var ds4 = new SyncDataSrc(4, logger, false);
- var ptr4 = new DataSrcContainer(false, "qux", ds4);
- dsList.appendContainerPtrDidSetup(ptr4);
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr4);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr2);
- assertThat(ptr2.prev).isEqualTo(ptr1);
- assertThat(ptr2.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr2);
- assertThat(ptr3.next).isEqualTo(ptr4);
- assertThat(ptr4.prev).isEqualTo(ptr3);
- assertThat(ptr4.next).isNull();
-
- dsList.removeAndCloseContainerPtrDidSetupByName("bar");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr1);
- assertThat(dsList.didSetupLast).isEqualTo(ptr4);
-
- assertThat(ptr1.prev).isNull();
- assertThat(ptr1.next).isEqualTo(ptr3);
- assertThat(ptr3.prev).isEqualTo(ptr1);
- assertThat(ptr3.next).isEqualTo(ptr4);
- assertThat(ptr4.prev).isEqualTo(ptr3);
- assertThat(ptr4.next).isNull();
-
- dsList.removeAndCloseContainerPtrDidSetupByName("foo");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr3);
- assertThat(dsList.didSetupLast).isEqualTo(ptr4);
-
- assertThat(ptr3.prev).isNull();
- assertThat(ptr3.next).isEqualTo(ptr4);
- assertThat(ptr4.prev).isEqualTo(ptr3);
- assertThat(ptr4.next).isNull();
-
- dsList.removeAndCloseContainerPtrDidSetupByName("qux");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isEqualTo(ptr3);
- assertThat(dsList.didSetupLast).isEqualTo(ptr3);
-
- assertThat(ptr3.prev).isNull();
- assertThat(ptr3.next).isNull();
-
- dsList.removeAndCloseContainerPtrDidSetupByName("baz");
-
- assertThat(dsList.local).isFalse();
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.notSetupLast).isNull();
- assertThat(dsList.didSetupHead).isNull();
- assertThat(dsList.didSetupLast).isNull();
-
- dsList.closeDataSrcs();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 closed",
- "SyncDataSrc 1 closed",
- "SyncDataSrc 4 closed",
- "SyncDataSrc 3 closed");
- }
-
- @Test
- void test_copyContainerPtrsDidSetupInto() {
- var dsList = new DataSrcList(false);
-
- var m = new HashMap();
- dsList.copyContainerPtrsDidSetupInto(m);
- assertThat(m).hasSize(0);
-
- var logger = new ArrayList();
-
- var ds1 = new SyncDataSrc(1, logger, false);
- var ptr1 = new DataSrcContainer(false, "foo", ds1);
- dsList.appendContainerPtrDidSetup(ptr1);
-
- var ds2 = new SyncDataSrc(2, logger, false);
- var ptr2 = new DataSrcContainer(false, "bar", ds2);
- dsList.appendContainerPtrDidSetup(ptr2);
-
- var ds3 = new SyncDataSrc(3, logger, false);
- var ptr3 = new DataSrcContainer(false, "baz", ds3);
- dsList.appendContainerPtrDidSetup(ptr3);
-
- m = new HashMap();
- dsList.copyContainerPtrsDidSetupInto(m);
-
- assertThat(m).hasSize(3);
- assertThat(m.get("foo")).isEqualTo(ptr1);
- assertThat(m.get("bar")).isEqualTo(ptr2);
- assertThat(m.get("baz")).isEqualTo(ptr3);
-
- dsList.closeDataSrcs();
- }
-
- @Test
- void test_setupAndCreateDataConnAndClose() {
- var logger = new ArrayList();
-
- var dsList = new DataSrcList(false);
-
- var dsAsync = new AsyncDataSrc(1, logger, false);
- dsList.addDataSrc("foo", dsAsync);
-
- var dsSync = new SyncDataSrc(2, logger, false);
- dsList.addDataSrc("bar", dsSync);
-
- var errMap = dsList.setupDataSrcs();
- assertThat(errMap).isEmpty();
-
- var ptr = dsList.didSetupHead;
- try {
- var conn = ptr.ds.createDataConn();
- assertThat(conn).isNotNull();
- } catch (Err e) {
- fail(e);
- }
-
- ptr = ptr.next;
- try {
- var conn = ptr.ds.createDataConn();
- assertThat(conn).isNotNull();
- } catch (Err e) {
- fail(e);
- }
-
- dsList.closeDataSrcs();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped",
- "AsyncDataSrc 1 setupped",
- "AsyncDataSrc 1 created DataConn",
- "SyncDataSrc 2 created DataConn",
- "SyncDataSrc 2 closed",
- "AsyncDataSrc 1 closed");
- }
-
- @Test
- void test_failToSetupSyncAndClose() {
- var logger = new ArrayList();
-
- var dsList = new DataSrcList(true);
-
- var dsAsync = new AsyncDataSrc(1, logger, false);
- dsList.addDataSrc("foo", dsAsync);
-
- var dsSync = new SyncDataSrc(2, logger, true);
- dsList.addDataSrc("bar", dsSync);
-
- var errMap = dsList.setupDataSrcs();
- assertThat(errMap).hasSize(1);
-
- var err = errMap.get("bar");
- assertThat(err.getReason()).isEqualTo("XXX");
-
- dsList.closeDataSrcs();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 failed to setup", "AsyncDataSrc 1 setupped", "AsyncDataSrc 1 closed");
- }
-
- @Test
- void test_failToSetupAsyncAndClose() {
- var logger = new ArrayList();
-
- var dsList = new DataSrcList(true);
-
- var dsAsync = new AsyncDataSrc(1, logger, true);
- dsList.addDataSrc("foo", dsAsync);
-
- var dsSync = new SyncDataSrc(2, logger, false);
- dsList.addDataSrc("bar", dsSync);
-
- var errMap = dsList.setupDataSrcs();
- assertThat(errMap).hasSize(1);
-
- var err = errMap.get("foo");
- assertThat(err.getReason()).isEqualTo("XXX");
-
- dsList.closeDataSrcs();
-
- assertThat(logger)
- .containsExactly(
- "SyncDataSrc 2 setupped", "AsyncDataSrc 1 failed to setup", "SyncDataSrc 2 closed");
- }
-
- @Test
- void test_noDataSrc() {
- var dsList = new DataSrcList(false);
-
- var errMap = dsList.setupDataSrcs();
- assertThat(errMap).hasSize(0);
-
- dsList.closeDataSrcs();
-
- assertThat(dsList.notSetupHead).isNull();
- assertThat(dsList.didSetupHead).isNull();
- }
-}
diff --git a/src/test/java/com/github/sttk/sabi/internal/DataSrcManagerTest.java b/src/test/java/com/github/sttk/sabi/internal/DataSrcManagerTest.java
new file mode 100644
index 0000000..7e24631
--- /dev/null
+++ b/src/test/java/com/github/sttk/sabi/internal/DataSrcManagerTest.java
@@ -0,0 +1,660 @@
+package com.github.sttk.sabi.internal;
+
+import static com.github.sttk.sabi.internal.DataConnManagerTest.AsyncDataConn;
+import static com.github.sttk.sabi.internal.DataConnManagerTest.SyncDataConn;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.github.sttk.errs.Err;
+import com.github.sttk.sabi.AsyncGroup;
+import com.github.sttk.sabi.DataConn;
+import com.github.sttk.sabi.DataSrc;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+public class DataSrcManagerTest {
+ private DataSrcManagerTest() {}
+
+ static enum Fail {
+ Not,
+ Setup,
+ CreateDataConn,
+ }
+
+ static class SyncDataSrc implements DataSrc {
+ int id;
+ List logger;
+ Fail fail;
+
+ SyncDataSrc(int id, List logger, Fail fail) {
+ logger.add(String.format("SyncDataSrc#new %d", id));
+ this.id = id;
+ this.logger = logger;
+ this.fail = fail;
+ }
+
+ @Override
+ public void setup(AsyncGroup ag) throws Err {
+ if (this.fail == Fail.Setup) {
+ this.logger.add(String.format("SyncDataSrc#setup %d failed", this.id));
+ throw new Err("XXX");
+ }
+ this.logger.add(String.format("SyncDataSrc#setup %d", this.id));
+ }
+
+ @Override
+ public void close() {
+ this.logger.add(String.format("SyncDataSrc#close %d", this.id));
+ }
+
+ @Override
+ public DataConn createDataConn() throws Err {
+ if (this.fail == Fail.CreateDataConn) {
+ this.logger.add(String.format("SyncDataSrc#createDataConn %d failed", this.id));
+ throw new Err("XXX");
+ }
+ this.logger.add(String.format("SyncDataSrc#createDataConn %d", this.id));
+ return new SyncDataConn(this.id, this.logger, DataConnManagerTest.Fail.Not);
+ }
+ }
+
+ static class AsyncDataSrc implements DataSrc {
+ int id;
+ List logger;
+ Fail fail;
+
+ AsyncDataSrc(int id, List logger, Fail fail) {
+ logger.add(String.format("AsyncDataSrc#new %d", id));
+ this.id = id;
+ this.logger = logger;
+ this.fail = fail;
+ }
+
+ @Override
+ public void setup(AsyncGroup ag) throws Err {
+ ag.add(
+ () -> {
+ if (this.fail == Fail.Setup) {
+ this.logger.add(String.format("AsyncDataSrc#setup %d failed", this.id));
+ throw new Err("XXX");
+ }
+ this.logger.add(String.format("AsyncDataSrc#setup %d", this.id));
+ });
+ }
+
+ @Override
+ public void close() {
+ this.logger.add(String.format("AsyncDataSrc#close %d", this.id));
+ }
+
+ @Override
+ public DataConn createDataConn() throws Err {
+ if (this.fail == Fail.CreateDataConn) {
+ this.logger.add(String.format("AsyncDataSrc#createDataConn %d failed", this.id));
+ throw new Err("XXX");
+ }
+ this.logger.add(String.format("AsyncDataSrc#createDataConn %d", this.id));
+ return new AsyncDataConn(this.id, this.logger, DataConnManagerTest.Fail.Not);
+ }
+ }
+
+ ///
+
+ @Test
+ void testNew() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).isEmpty();
+ assertThat(manager.listReady).isEmpty();
+
+ manager = new DataSrcManager(false);
+ assertThat(manager.local).isFalse();
+ assertThat(manager.listUnready).isEmpty();
+ assertThat(manager.listReady).isEmpty();
+ }
+
+ @Test
+ void testAdd() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ var ds1 = new SyncDataSrc(1, logger, Fail.Not);
+ manager.add("foo", ds1);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(1);
+ assertThat(manager.listReady).hasSize(0);
+
+ assertThat(manager.listUnready.get(0).name).isEqualTo("foo");
+
+ var ds2 = new AsyncDataSrc(2, logger, Fail.Not);
+ manager.add("bar", ds2);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(2);
+ assertThat(manager.listReady).hasSize(0);
+
+ assertThat(manager.listUnready.get(0).name).isEqualTo("foo");
+ assertThat(manager.listUnready.get(1).name).isEqualTo("bar");
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(2);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#new 2");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testRemove() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ var ds1 = new SyncDataSrc(1, logger, Fail.Not);
+ manager.add("foo", ds1);
+
+ var ds2 = new AsyncDataSrc(2, logger, Fail.Not);
+ manager.add("bar", ds2);
+
+ var errors = manager.setup();
+ assertThat(errors).hasSize(0);
+
+ var ds3 = new SyncDataSrc(3, logger, Fail.Not);
+ manager.add("baz", ds3);
+
+ var ds4 = new AsyncDataSrc(4, logger, Fail.Not);
+ manager.add("qux", ds4);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(2);
+ assertThat(manager.listReady).hasSize(2);
+
+ manager.remove("baz");
+ manager.remove("foo");
+ manager.remove("qux");
+ manager.remove("bar");
+
+ } finally {
+ // manager.close(); // to see Close logs by remove
+ }
+
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#new 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 3");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#new 4");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#close 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#close 2");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void testClose() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ var ds1 = new SyncDataSrc(1, logger, Fail.Not);
+ manager.add("foo", ds1);
+
+ var ds2 = new AsyncDataSrc(2, logger, Fail.Not);
+ manager.add("bar", ds2);
+
+ var errors = manager.setup();
+ assertThat(errors).hasSize(0);
+
+ var ds3 = new SyncDataSrc(3, logger, Fail.Not);
+ manager.add("baz", ds3);
+
+ var ds4 = new AsyncDataSrc(4, logger, Fail.Not);
+ manager.add("qux", ds4);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(2);
+ assertThat(manager.listReady).hasSize(2);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(8);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#new 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 3");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#new 4");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void setupNoDataSrc() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(0);
+ assertThat(manager.listReady).hasSize(0);
+
+ var errors = manager.setup();
+ assertThat(errors).isEmpty();
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(0);
+ assertThat(manager.listReady).hasSize(0);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).isEmpty();
+ var iter = logger.iterator();
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void setupAndOk() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ var ds1 = new SyncDataSrc(1, logger, Fail.Not);
+ manager.add("foo", ds1);
+
+ var ds2 = new AsyncDataSrc(2, logger, Fail.Not);
+ manager.add("bar", ds2);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(2);
+ assertThat(manager.listReady).hasSize(0);
+
+ var errors = manager.setup();
+ assertThat(errors).hasSize(0);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(0);
+ assertThat(manager.listReady).hasSize(2);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(6);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#new 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#setup 2");
+ assertThat(iter.next()).isEqualTo("AsyncDataSrc#close 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void setupButError() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ var ds1 = new SyncDataSrc(1, logger, Fail.Not);
+ manager.add("foo", ds1);
+
+ var ds2 = new SyncDataSrc(2, logger, Fail.Setup);
+ manager.add("bar", ds2);
+
+ var ds3 = new SyncDataSrc(3, logger, Fail.Setup);
+ manager.add("bar", ds3);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(3);
+ assertThat(manager.listReady).hasSize(0);
+
+ var errors = manager.setup();
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(3);
+ assertThat(manager.listReady).hasSize(0);
+
+ assertThat(errors).hasSize(1);
+ assertThat(errors.get(0).index).isEqualTo(1);
+ assertThat(errors.get(0).name).isEqualTo("bar");
+ assertThat(errors.get(0).err.toString())
+ .isEqualTo(
+ "com.github.sttk.errs.Err { reason = java.lang.String XXX, file = DataSrcManagerTest.java, line = 41 }");
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(6);
+ var iter = logger.iterator();
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 1");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 2");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#new 3");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#setup 1");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#setup 2 failed");
+ assertThat(iter.next()).isEqualTo("SyncDataSrc#close 1");
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void setupWithOrderNoDtaSrc() {
+ var logger = new ArrayList();
+
+ var manager = new DataSrcManager(true);
+ try {
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(0);
+ assertThat(manager.listReady).hasSize(0);
+
+ var errors = manager.setupWithOrder(List.of("bar", "foo"));
+ assertThat(errors).hasSize(0);
+
+ assertThat(manager.local).isTrue();
+ assertThat(manager.listUnready).hasSize(0);
+ assertThat(manager.listReady).hasSize(0);
+ } finally {
+ manager.close();
+ }
+
+ assertThat(logger).hasSize(0);
+ var iter = logger.iterator();
+ assertThat(iter.hasNext()).isFalse();
+ }
+
+ @Test
+ void setupWithOrderAndOk() {
+ var logger = new ArrayList