Interface OperationGroup<S,​T>

  • Type Parameters:
    S - The type of the result of the member Operations
    T - The type of the collected results the member Operations
    All Superinterfaces:
    java.lang.AutoCloseable, Operation<T>, PrimitiveOperation<T>
    All Known Subinterfaces:
    Session

    public interface OperationGroup<S,​T>
    extends Operation<T>, java.lang.AutoCloseable

    A set of Operations that share certain properties, are managed as a unit, and are executed as a unit. The Operations created by an OperationGroup and submitted are the member Operations of that OperationGroup. An OperationGroup is not a transaction and is not related to a transaction in any way.

    An OperationGroup provides conditional execution, control of error response, and control of execution order.

    Execution of one or more Operations may depend on the result of a previous Operation. Depending on the result of that previous Operation some other Operations perhaps should not be executed. For example consider an account withdrawal. If the amount to withdraw exceeds the account balance the withdrawal Operations should not be executed and an overdraft Operation should be executed instead. It would be possible for the user thread to wait for the balance check Operation but that would block. Better would be to use the CompletionStage of the balance check Operation and submit the appropriate Operation in a subsequent stage. But this is a common pattern and it is better still to encapsulate that pattern, which conditional OperationGroup does.

    Not all Operations need to be executed in the order submitted. The most common example is a mass insert. The order in which the records are inserted doesn’t matter. A parallel OperationGroup gives the implementation the freedom to execute the Operations in any order. If some of the Operations have CompletionStage parameters this can be especially valuable.

    OperationGroup also allows control of error response. By default if one Operation fails all subsequent Operations are skipped. That’s not always right. Consider the mass insert case. Just because one insert fails doesn’t mean they should all fail. An independent OperationGroup does this; the failure of one Operation has no impact on the execution of the rest.

    As an OperationGroup is an Operation it must be submitted before its member Operations are executed. Submitting an OperationGroup allows its member Operations to be executed but does not prohibit more member Operations from being submitted. Member Operations may be submitted before and after the containing OperationGroup is submitted.

    The result of an OperationGroup depends on the results of its member Operations. Therefore an OperationGroup must know when all member Operations have been submitted. It cannot generate a result until all member Operations are completed. Since member Operations can be submitted after the OperationGroup has been submitted (see previous paragraph) submitting the containing OperationGroup is not sufficient to mark that all member Operations have been submitted. Calling close() signals that all member Operations have been submitted. After close is called, no more member Operations may be submitted and the OperationGroup will complete when all member Operations are complete.

    An OperationGroup conceptually has a collection of member Operations. When an OperationGroup is submitted it is placed in the collection of the OperationGroup of which it is a member. The member OperationGroup is executed according to the attributes of the OperationGroup of which it is a member. The member Operations of an OperationGroup are executed according to the attributes of that OperationGroup.

    How an OperationGroup is executed depends on its attributes.

    If an OperationGroup has a condition and the value of that condition is Boolean.TRUE then execute the member Operations as below. If it is Boolean.FALSE then the OperationGroup is completed with the value null. If the condition completed exceptionally then the OperationGroup is completed exceptionally with a SqlSkippedException that has that exception as its cause.

    If the OperationGroup is sequential the member Operations are executed in the order they were submitted. If it is parallel, they may be executed in any order including simultaneously.

    If an OperationGroup is dependent and a member Operation completes exceptionally the remaining member Operations in the collection are completed exceptionally with a SqlSkippedException that has the initial Exception as its cause and the OperationGroup is completed exceptionally with the initial Exception. A member Operation in-flight may either complete normally or be completed exceptionally but must complete one way or the other. [NOTE: Too strong?]

    The result of this OperationGroup is the result of collecting the results of its member Operations. If the OperationGroup is dependent and one of its member Operations completes exceptionally, the OperationGroup is completed exceptionally.

    An implementation of this class must be thread safe as result and error handlers running asynchronously may be accessing an OperationGroup in parallel with each other and with a user thread.

    ISSUE: Currently no way to create a nested OperationGroup. That is an intentional limitation but may be a simplification we can live with. Or not.

    • Method Detail

      • parallel

        OperationGroup<S,​T> parallel()
        Mark this OperationGroup as parallel. If this method is not called the OperationGroup is sequential. If an OperationGroup is parallel, member Operations may be executed in any order including in parallel. If an OperationGroup is sequential, the default, member Operations are executed strictly in the order they are submitted. Note: There is no covariant override of this method in Session as there is only a small likelihood of needing it.
        Returns:
        this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup has been submitted, any member Operations have been created, or this method has been called previously
      • independent

        OperationGroup<S,​T> independent()
        Mark this OperationGroup as independent. If this method is not called the OperationGroup is dependent, the default. If an OperationGroup is independent then failure of one member Operation does not affect the execution of other member Operations. If an OperationGroup is dependent then failure of one member Operation will cause all member Operations remaining in the queue to be completed exceptionally with a SqlSkippedException with the cause set to the original exception. The result of this OperationGroup's execution is the result of collecting the results of the member Operations that complete normally. Note: There is no covariant override of this method in Session as there is only a small likelihood of needing it.
        Returns:
        this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup has been submitted, any member Operations have been created, or this method has been called previously
      • conditional

        OperationGroup<S,​T> conditional​(java.util.concurrent.CompletionStage<java.lang.Boolean> condition)
        Define a condition that determines whether the member Operations of this OperationGroup are executed or not. If and when this OperationGroup is executed then if the condition argument is completed with Boolean.TRUE the member Operations are executed. If Boolean.FALSE or if it is completed exceptionally the member Operations are not executed but are removed from the queue. After all member Operations have been removed from the queue this OperationGroup is completed with null. Note: There is no covariant override of this method in Session as there is only a small likelihood of needing it. ISSUE: Should the member Operations be skipped or otherwise completed exceptionally?
        Parameters:
        condition - a CompletionStage the value of which determines whether this OperationGroup is executed or not
        Returns:
        this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup has been submitted, any member Operations have been created, or this method has been called previously
      • collect

        OperationGroup<S,​T> collect​(java.util.stream.Collector<S,​?,​T> c)
        Provides a Collector to reduce the results of the member Operations. The result of this OperationGroup is the result of calling finisher on the final accumulated result.If the Collector is Collector.Characteristics.UNORDERED the member Operation results may be accumulated out of order.If the Collector is Collector.Characteristics.CONCURRENT then the member Operation results may be split into subsets that are reduced separately and then combined. If this OperationGroup is sequential, the characteristics of the Collector only affect how the results of the member Operations are collected; the member Operations are executed sequentially regardless. If this OperationGroup is parallel the characteristics of the Collector may influence the execution order of the member Operations. The default value is Collector.of(()->null, (a,t)->{}, (l,r)->null, a->null).
        Parameters:
        c - the Collector. Not null.
        Returns:
        This OperationGroup
        Throws:
        java.lang.IllegalStateException - if called more than once or if this OperationGroup has been submitted
      • catchOperation

        PrimitiveOperation<S> catchOperation()
        Return a new member PrimitiveOperation that is never skipped. Skipping of member Operations stops with a catchOperation and the subsequent Operation is executed normally. The value of a catchOperation is always null. Since a catchOperation is never completed exceptionally, it has no error handler or timeout.
        Returns:
        an PrimitiveOperation that is never skipped;
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed or if this OperationGroup is parallel or independent.
      • catchErrors

        default OperationGroup<S,​T> catchErrors()
        Creates and submits a catch Operation. Convenience method.
        Returns:
        this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed or if this OperationGroup is parallel or independent.
      • arrayRowCountOperation

        <R extends SArrayRowCountOperation<R> arrayRowCountOperation​(java.lang.String sql)
        Return a new ArrayRowCountOperation.

        Usage Note: Frequently use of this method will require a type witness to enable correct type inferencing.

        
           session.<List<Integer>>arrayCountOperation(sql)
             .set ...
             .collect ...
             .submit ...
         
        Type Parameters:
        R - the result type of the returned ArrayRowCountOperation
        Parameters:
        sql - SQL to be executed. Must return an update count.
        Returns:
        a new ArrayRowCountOperation that is a member of this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed.
      • operation

        Operation<S> operation​(java.lang.String sql)
        Return a new Operation for a SQL that doesn't return any result, for example DDL. The result of this Operation is always null. The result of the returned Operation must be Void but specifying that here causes problems.
        Parameters:
        sql - SQL for the Operation.
        Returns:
        a new Operation that is a member of this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed.
      • outOperation

        <R extends SOutOperation<R> outOperation​(java.lang.String sql)
        Return a new OutOperation that is a member Operation of this OperationGroup. The SQL must return a set of zero or more out parameters or function results.
        Type Parameters:
        R - the result type of the returned OutOperation
        Parameters:
        sql - SQL for the Operation. Must return zero or more out parameters or function results.
        Returns:
        a new OutOperation that is a member of this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed.
      • multiOperation

        <R extends SMultiOperation<R> multiOperation​(java.lang.String sql)
        Return a new MultiOperation that is a member Operation of this OperationGroup.
        Type Parameters:
        R - the type of the result of the returned MultiOperation
        Parameters:
        sql - SQL for the Operation
        Returns:
        a new MultiOperation that is a member of this OperationGroup
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed.
      • endTransactionOperation

        Operation<TransactionOutcome> endTransactionOperation​(TransactionCompletion trans)
        Return a new Operation that ends the database transaction. This Operation is a member of the OperationGroup. The transaction is ended with a commit unless the TransactionCompletion has been TransactionCompletion.setRollbackOnly() in which case the transaction is ended with a rollback.

        An endTransaction Operation may be skipped. To insure that it will not be skipped it should immediately follow a catch Operation. All end transaction convenience methods do so.

        The type argument OperationGroup of the containing OperationGroup must be a supertype of TransactionOutcome.
        Parameters:
        trans - the TransactionCompletion that determines whether the Operation does a database commit or a database rollback.
        Returns:
        an Operation that will end the database transaction.
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed or is parallel.
      • commitMaybeRollback

        default java.util.concurrent.CompletionStage<TransactionOutcome> commitMaybeRollback​(TransactionCompletion trans)
        Convenience method that creates and submits a endTransaction Operation that commits by default but can be set to rollback by calling TransactionCompletion.setRollbackOnly(). The endTransaction Operation is never skipped.
        Parameters:
        trans - the TransactionCompletion that determines whether the Operation is a database commit or a database rollback.
        Returns:
        a CompletionStage that is completed with the outcome of the transaction
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed or is parallel.
      • localOperation

        <R extends SLocalOperation<R> localOperation()
        Return a new LocalOperation that is a member Operation of this OperationGroup.
        Type Parameters:
        R - value type of the returned local Operation
        Returns:
        a LocalOperation
        Throws:
        java.lang.IllegalStateException - if this OperationGroup is closed.
      • logger

        OperationGroup<S,​T> logger​(java.util.logging.Logger logger)
        Supply a Logger for the implementation of this OperationGroup to use to log significant events. Exactly what events are logged, at what Level the events are logged and with what parameters is implementation dependent. All member Operations of this OperationGroup will use the same Logger except a member OperationGroup that is supplied with a different Logger uses that Logger. Supplying a Logger configured with a MemoryHandler with the MemoryHandler.pushLevel set to Level.WARNING will result in no log output in normal operation. In the event of an error the actions leading up to the error will be logged. Implementation Note: Implementations are encouraged to log the creation of this OperationGroup set to Level.INFO, the creation of member Operations at the Level.CONFIG level, and execution of member Operations at the Level.FINE level. Detailed information about the execution of member Operations may be logged at the Level.FINER and Level.FINEST levels. Errors in the execution of user code should be logged at the Level.WARNING Level. Errors in the implementation code should be logged at the Level.SEVERE Level.
        Parameters:
        logger - used by the implementation to log significant events
        Returns:
        this OperationGroup
      • enquoteLiteral

        default java.lang.String enquoteLiteral​(java.lang.String val)
        Returns a String enclosed in single quotes. Any occurrence of a single quote within the string will be replaced by two single quotes.
        Examples of the conversion:
        ValueResult
        Hello 'Hello'
        G'Day 'G''Day'
        'G''Day' '''G''''Day'''
        I'''M 'I''''''M'
        Parameters:
        val - a character string. Not null
        Returns:
        A string enclosed by single quotes with every single quote converted to two single quotes. Not null
        Throws:
        java.lang.NullPointerException - if val is null
        java.lang.IllegalArgumentException - if val cannot be enquoted
        Implementation Note:
        ADBA driver implementations may need to provide their own implementation of this method in order to meet the requirements of the underlying datasource.
      • enquoteIdentifier

        default java.lang.String enquoteIdentifier​(java.lang.String identifier,
                                                   boolean alwaysQuote)
        Returns a SQL identifier. If identifier is a simple SQL identifier:
        • Return the original value if alwaysQuote is false
        • Return a delimited identifier if alwaysQuote is true
        If identifier is not a simple SQL identifier, identifier will be enclosed in double quotes if not already present. If the datasource does not support double quotes for delimited identifiers, the identifier should be enquoted by whatever mechanism the data source supports. If the datasource does not support delimited identifiers, an IllegalArgumentException should be thrown.

        A IllegalArgumentException will be thrown if identifier contains any characters invalid in a delimited identifier or the identifier length is invalid for the datasource.

        Parameters:
        identifier - a SQL identifier. Not null
        alwaysQuote - indicates if a simple SQL identifier should be returned as a quoted identifier
        Returns:
        A simple SQL identifier or a delimited identifier. Not null
        Throws:
        java.lang.NullPointerException - if identifier is null
        java.lang.IllegalArgumentException - if identifier can not be converted to a valid identifier
        Implementation Requirements:
        The default implementation uses the following criteria to determine a valid simple SQL identifier:
        • The string is not enclosed in double quotes
        • The first character is an alphabetic character from a through z, or from A through Z
        • The name only contains alphanumeric characters or the character "_"
        The default implementation will throw a SQLException if:
        • identifier contains a null character or double quote and is not a simple SQL identifier.
        • The length of identifier is less than 1 or greater than 128 characters
        Examples of the conversion:
        identifier alwaysQuote Result
        Hello false Hello
        Hello true "Hello"
        G'Day false "G'Day"
        "Bruce Wayne" false "Bruce Wayne"
        "Bruce Wayne" true "Bruce Wayne"
        GoodDay$ false "GoodDay$"
        Hello"World false IllegalArgumentException
        "Hello"World" false IllegalArgumentException
        Implementation Note:
        ADBA driver implementations may need to provide their own implementation of this method in order to meet the requirements of the underlying datasource.
      • isSimpleIdentifier

        default boolean isSimpleIdentifier​(java.lang.String identifier)
        Retrieves whether identifier is a simple SQL identifier.
        Parameters:
        identifier - a SQL identifier. Not null
        Returns:
        true if a simple SQL identifier, false otherwise
        Throws:
        java.lang.NullPointerException - if identifier is null
        Implementation Requirements:
        The default implementation uses the following criteria to determine a valid simple SQL identifier:
        • The string is not enclosed in double quotes
        • The first character is an alphabetic character from a through z, or from A through Z
        • The string only contains alphanumeric characters or the character "_"
        • The string is between 1 and 128 characters in length inclusive
        Examples of the conversion:
        identifier Simple Identifier
        Hello true
        G'Day false
        "Bruce Wayne" false
        GoodDay$ false
        Hello"World false
        "Hello"World" false
        Implementation Note:
        ADBA driver implementations may need to provide their own implementation of this method in order to meet the requirements of the underlying datasource.
      • enquoteNCharLiteral

        default java.lang.String enquoteNCharLiteral​(java.lang.String val)
        Returns a String representing a National Character Set Literal enclosed in single quotes and prefixed with a upper case letter N. Any occurrence of a single quote within the string will be replaced by two single quotes.
        Examples of the conversion:
        Value Result
        Hello N'Hello'
        G'Day N'G''Day'
        'G''Day' N'''G''''Day'''
        I'''M N'I''''''M'
        N'Hello' N'N''Hello'''
        Parameters:
        val - a character string. Not null
        Returns:
        the result of replacing every single quote character in the argument by two single quote characters where this entire result is then prefixed with 'N'. Not null.
        Throws:
        java.lang.NullPointerException - if val is null
        java.lang.IllegalArgumentException - if val cannot be enquoted
        Implementation Note:
        ADBA driver implementations may need to provide their own implementation of this method in order to meet the requirements of the underlying datasource. An implementation of enquoteNCharLiteral may accept a different set of characters than that accepted by the same drivers implementation of enquoteLiteral.
      • close

        void close()
        Allow this OperationGroup to be completed and removed from the queue once all of its member Operations have been completed. After this method is called no additional member Operations can be submitted. Once all member Operations have been removed from the queue this OperationGroup will be completed and removed from the queue.
        Specified by:
        close in interface java.lang.AutoCloseable
      • timeout

        OperationGroup<S,​T> timeout​(java.time.Duration minTime)
        The minimum time before this Operation might be canceled automatically. The default value is forever. The time is counted from the beginning of Operation execution. The Operation will not be canceled before minTime after the beginning of execution. Some time at least minTime after the beginning of execution, an attempt will be made to cancel the Operation if it has not yet completed. Implementations are encouraged to attempt to cancel within a reasonable time, though what is reasonable is implementation dependent.
        Specified by:
        timeout in interface Operation<S>
        Parameters:
        minTime - minimum time to wait before attempting to cancel
        Returns:
        this OperationGroup
      • onError

        OperationGroup<S,​T> onError​(java.util.function.Consumer<java.lang.Throwable> handler)
        Provides an error handler for this Operation. If execution of this Operation results in an error, before the Operation is completed, the handler is called with the Throwable as the argument. The type of the Throwable is implementation dependent.
        Specified by:
        onError in interface Operation<S>
        Returns:
        this OperationGroup