/*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.sql2;
import java.time.Duration;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.logging.Logger;
import java.util.stream.Collector;
/**
* <p>
* A set of {@link Operation}s that share certain properties, are managed as a
* unit, and are executed as a unit. The {@link Operation}s created by an
* {@link OperationGroup} and submitted are the member {@link Operation}s of
* that {@link OperationGroup}. An {@link OperationGroup} is not a transaction
* and is not related to a transaction in any way.</p>
*
* <p>
* An {@link OperationGroup} conceptually has a collection of member
* {@link Operation}s. When an {@link OperationGroup} is submitted it is placed
* in the collection of the {@link OperationGroup} of which it is a member. The
* member {@link OperationGroup} is executed according to the attributes of the
* {@link OperationGroup} of which it is a member. The member {@link Operation}s
* of an {@link OperationGroup} are executed according to the attributes of that
* {@link OperationGroup}.</p>
*
* <p>
* How an {@link OperationGroup} is executed depends on its attributes.</p>
*
* <p>
* If an {@link OperationGroup} has a condition and the value of that condition
* is {@link Boolean#TRUE} then execute the member {@link Operation}s as below.
* If it is {@link Boolean#FALSE} then the {@link OperationGroup} is completed
* with the value null. If the condition completed exceptionally then the
* {@link OperationGroup} is completed exceptionally with a
* {@link SqlSkippedException} that has that exception as its cause.</p>
*
* <p>
* If the {@link OperationGroup} is sequential the member {@link Operation}s are
* executed in the order they were submitted. If it is parallel, they may be
* executed in any order including simultaneously.</p>
*
* <p>
* If an {@link OperationGroup} is dependent and a member {@link Operation}
* completes exceptionally the remaining member {@link Operation}s in the
* collection are completed exceptionally with a {@link SqlSkippedException}
* that has the initial {@link Exception} as its cause and the {@link OperationGroup}
* is completed exceptionally with the initial {@link Exception}. A member
* {@link Operation} in-flight may either complete normally or be completed
* exceptionally but must complete one way or the other. [NOTE: Too strong?]</p>
*
* <p>
* After a call to {@link OperationGroup#submitHoldingForMoreMembers} the
* {@link OperationGroup} is submitted and held. After a call to
* {@link OperationGroup#releaseProhibitingMoreMembers} the {@link OperationGroup}
* is no longer held and is still submitted. Holding permits member {@link Operation}s
* to be executed at the same time additional member {@link Operation}s are
* submitted. Collecting the member {@link Operation}s' results does not begin
* until the {@link OperationGroup} is no longer held.</p>
*
* <p>
* If an {@link OperationGroup} is held additional member {@link Operation}s may
* be submitted. If an {@link OperationGroup} is not held, no additional member
* {@link Operation}s may be submitted after the {@link OperationGroup} is
* submitted. If an {@link OperationGroup} is held it will be completed only after
* it is released or if conditional and the condition is not {@link Boolean#TRUE}.
* If a {@link OperationGroup} is dependent, held, one of its member
* {@link Operation}s completed exceptionally, and its queue is empty then the
* {@link OperationGroup} is released.</p>
*
* <p>
* The result of this {@link OperationGroup} is the result of collecting the
* results of its member {@link Operation}s. If the {@link OperationGroup} is
* dependent and one of its member {@link Operation}s completes exceptionally,
* the {@link OperationGroup} is completed exceptionally.</p>
*
* <p>
* An implementation of this class must be thread safe as result and error
* handlers running asynchronously may be accessing an {@link OperationGroup} in
* parallel with each other and with a user thread.</p>
* <p>
* ISSUE: Currently no way to create a nested {@link OperationGroup}. That is an
* intentional limitation but may be a simplification we can live with. Or not.</p>
*
* @param <S> The type of the result of the member {@link Operation}s
* @param <T> The type of the collected results the member {@link Operation}s
*/
public interface OperationGroup<S, T> extends Operation<T> {
/**
* Mark this {@link OperationGroup} as parallel. If this method is not called
* the {@link OperationGroup} is sequential. If an {@link OperationGroup} is
* parallel, member {@link Operation}s may be executed in any order including
* in parallel. If an {@link OperationGroup} is sequential, the default,
* member {@link Operation}s are executed strictly in the order they are
* submitted.
*
* Note: There is no covariant override of this method in {@link Session}
* as there is only a small likelihood of needing it.
*
* @return this {@link OperationGroup}
* @throws IllegalStateException if this {@link OperationGroup} has been
* submitted, any member {@link Operation}s have been created, or this method
* has been called previously
*/
public OperationGroup<S, T> parallel();
/**
* Mark this {@link OperationGroup} as independent. If this method is not
* called the {@link OperationGroup} is dependent, the default. If an
* {@link OperationGroup} is independent then failure of one member
* {@link Operation} does not affect the execution of other member
* {@link Operation}s. If an {@link OperationGroup} is dependent then failure
* of one member {@link Operation} will cause all member {@link Operation}s
* remaining in the queue to be completed exceptionally with a
* {@link SqlSkippedException} with the cause set to the original exception.
*
* The result of this {@link OperationGroup}'s execution is the result of collecting the
* results of the member {@link Operation}s that complete normally.
*
* Note: There is no covariant override of this method in {@link Session}
* as there is only a small likelihood of needing it.
*
* @return this {@link OperationGroup}
* @throws IllegalStateException if this {@link OperationGroup} has been
* submitted, any member {@link Operation}s have been created, or this method
* has been called previously
*/
public OperationGroup<S, T> independent();
/**
* Define a condition that determines whether the member {@link Operation}s of
* this {@link OperationGroup} are executed or not. If and when this
* {@link OperationGroup} is executed then if the condition argument is
* completed with {@link Boolean#TRUE} the member {@link Operation}s are
* executed. If {@link Boolean#FALSE} or if it is completed exceptionally the
* member {@link Operation}s are not executed but are removed from the queue.
* After all member {@link Operation}s have been removed from the queue this
* {@link OperationGroup} is completed with {@code 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?
*
* @param condition a {@link CompletionStage} the value of which determines
* whether this {@link OperationGroup} is executed or not
* @return this OperationGroup
* @throws IllegalStateException if this {@link OperationGroup} has been
* submitted, any member {@link Operation}s have been created, or this method
* has been called previously
*/
public OperationGroup<S, T> conditional(CompletionStage<Boolean> condition);
/**
* Mark this {@link OperationGroup} as submitted and held. It can be executed but cannot be
* completed. A {@link OperationGroup} that is held remains in the queue even
* if all of its current member {@link Operation}s have completed. So long as
* the {@link OperationGroup} is held new member {@link Operation}s can be
* submitted. A {@link OperationGroup} that is held must be released before it
* can be completed and removed from the queue.
*
* If the {@link OperationGroup} is dependent and one of its member {@link Operation}s
* completes exceptionally and its queue is empty the {@link OperationGroup}
* is completed.
*
* Note: There is no covariant override of this method in Session as there
* is only a small likelihood of needing it.
*
* ISSUE: Need a better name.
*
* @return a Submission for this OperationGroup
* @throws IllegalStateException if this {@link OperationGroup} has been
* submitted
*/
public Submission<T> submitHoldingForMoreMembers();
/**
* Allow this {@link OperationGroup} to be completed and removed from the
* queue once all of its member {@link Operation}s have been completed. After
* this method is called no additional member {@link Operation}s can be
* submitted. Once all member {@link Operation}s have been removed from the
* queue this {@link OperationGroup} will be completed and removed from the
* queue.
*
* Note: There is no covariant override of this method in Session as there
* is only a small likelihood of needing it.
*
* ISSUE: Need a better name.
*
* @return the same Submission that was returned by {@link OperationGroup#submitHoldingForMoreMembers}
* @throws IllegalStateException if this {@link OperationGroup} has been
* completed or is not held.
*/
public Submission<T> releaseProhibitingMoreMembers();
/**
* Provides a {@link Collector} to reduce the results of the member
* {@link Operation}s. The result of this {@link OperationGroup} is the result
* of calling finisher on the final accumulated result.If the
* {@link Collector} is {@link Collector.Characteristics#UNORDERED} the member
* {@link Operation} results may be accumulated out of order.If the
* {@link Collector} is {@link Collector.Characteristics#CONCURRENT} then the
* member {@link Operation} results may be split into subsets that are reduced
* separately and then combined. If this {@link OperationGroup} is sequential,
* the characteristics of the {@link Collector} only affect how the results of
* the member {@link Operation}s are collected; the member {@link Operation}s
* are executed sequentially regardless. If this {@link OperationGroup} is
* parallel the characteristics of the {@link Collector} may influence the
* execution order of the member {@link Operation}s.
*
* The default value is
* {@code Collector.of(()->null, (a,t)->{}, (l,r)->null, a->null)}.
*
* @param c the Collector. Not null.
* @return This OperationGroup
* @throws IllegalStateException if called more than once or if this
* {@link OperationGroup} has been submitted
*/
public OperationGroup<S, T> collect(Collector<S, ?, T> c);
/**
* Return a new member {@link PrimitiveOperation} that is never skipped.
* Skipping of member {@link Operation}s stops with a catchOperation and the
* subsequent {@link 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.
*
* @return an {@link PrimitiveOperation} that is never skipped;
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held or if the {@link OperationGroup} is parallel or
* independent.
*/
public PrimitiveOperation<S> catchOperation();
/**
* Creates and submits a catch Operation. Convenience method.
*
* @return this OperationGroup
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held or if the {@link OperationGroup} is parallel or
* independent.
*/
public default OperationGroup<S, T> catchErrors() {
catchOperation().submit();
return this;
}
/**
* Return a new {@link ArrayRowCountOperation}.
* <p>
* Usage Note: Frequently use of this method will require a type witness to
* enable correct type inferencing.
* <pre><code>
* session.<b><List<Integer>></b>arrayCountOperation(sql)
* .set ...
* .collect ...
* .submit ...
* </code></pre>
*
* @param <R> the result type of the returned {@link ArrayRowCountOperation}
* @param sql SQL to be executed. Must return an update count.
* @return a new {@link ArrayRowCountOperation} that is a member of this
* {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public <R extends S> ArrayRowCountOperation<R> arrayRowCountOperation(String sql);
/**
* Return a new {@link ParameterizedRowCountOperation}.
*
* @param <R> the result type of the returned {@link CountOperation}
* @param sql SQL to be executed. Must return an update count.
* @return an new {@link ParameterizedRowCountOperation} that is a member of this
* {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public <R extends S> ParameterizedRowCountOperation<R> rowCountOperation(String sql);
/**
* Return a new {@link 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.
*
* @param sql SQL for the {@link Operation}.
* @return a new {@link Operation} that is a member of this
* {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public Operation<S> operation(String sql);
/**
* Return a new {@link OutOperation} that is a member {@link Operation} of this
* {@link OperationGroup}. The SQL must return a set of zero or more out
* parameters or function results.
*
* @param <R> the result type of the returned {@link OutOperation}
* @param sql SQL for the {@link Operation}. Must return zero or more out
* parameters or function results.
* @return a new {@link OutOperation} that is a member of this
* {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public <R extends S> OutOperation<R> outOperation(String sql);
/**
* Return a new {@link ParameterizedRowOperation} that is a member
* {@link Operation} of this {@link OperationGroup}.
*
* @param <R> the type of the result of the returned
* {@link ParameterizedRowOperation}
* @param sql SQL for the {@link Operation}. Must return a row sequence.
* @return a new {@link ParameterizedRowOperation} that is a member of this
* {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public <R extends S> ParameterizedRowOperation<R> rowOperation(String sql);
/**
* Return a new {@link ParameterizedRowPublisherOperation} that is a member
* {@link Operation} of this {@link OperationGroup}.
*
* @param <R> the type of the result of the returned
* {@link ParameterizedRowPublisherOperation}
* @param sql SQL for the {@link Operation}. Must return a row sequence.
* @return a new {@link ParameterizedRowPublisherOperation} that is a member
* of this {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public <R extends S> ParameterizedRowPublisherOperation<R> rowPublisherOperation(String sql);
/**
* Return a new {@link MultiOperation} that is a member
* {@link Operation} of this {@link OperationGroup}.
*
* @param <R> the type of the result of the returned
* {@link MultiOperation}
* @param sql SQL for the {@link Operation}
* @return a new {@link MultiOperation} that is a member of this
* {@link OperationGroup}
* @throws IllegalStateException if the {@link OperationGroup} has been
* submitted and is not held
*/
public <R extends S> MultiOperation<R> multiOperation(String sql);
/**
* Return a new {@link Operation} that ends the database transaction. This
* {@link Operation} is a member of the {@link OperationGroup}. The
* transaction is ended with a commit unless the {@link Transaction} has been
* {@link Transaction#setRollbackOnly} in which case the transaction is ended
* with a rollback.
*
* <p>
* 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.</p>
*
* The type argument {@link S} of the containing {@link OperationGroup} must
* be a supertype of {@link TransactionOutcome}.
*
* @param trans the TransactionEnd that determines whether the Operation does a
* database commit or a database rollback.
* @return an {@link Operation} that will end the database transaction.
* @throws IllegalStateException if this {@link OperationGroup} has been
* submitted and is not held or is parallel.
*/
public Operation<TransactionOutcome> endTransactionOperation(TransactionEnd trans);
/**
* Convenience method that creates and submits a endTransaction
* {@link Operation} that commits by default but can be set to rollback by
* calling {@link TransactionEnd#setRollbackOnly}. The endTransaction Operation
* is never skipped.
*
* @param trans the TransactionEnd that determines whether the {@link Operation} is a
* database commit or a database rollback.
* @return a {@link CompletionStage} that is completed with the outcome of the
* transaction
* @throws IllegalStateException if this {@link OperationGroup} has been
* submitted and is not held or is parallel.
*/
public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionEnd trans) {
catchErrors();
return this.endTransactionOperation(trans).submit().getCompletionStage();
}
/**
* Return a new {@link LocalOperation} that is a member {@link Operation} of
* this {@link OperationGroup}.
*
* @param <R> value type of the returned local {@link Operation}
* @return a LocalOperation
* @throws IllegalStateException if this {@link OperationGroup} has been submitted and
* is not held
*/
public <R extends S> LocalOperation<R> localOperation();
/**
* Supply a {@link Logger} for the implementation of this
* {@link 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 {@link Operation}s of
* this {@link OperationGroup} will use the same {@link Logger} except a
* member {@link OperationGroup} that is supplied with a different
* {@link Logger} uses that {@link Logger}.
*
* Supplying a {@link Logger} configured with a
* {@link java.util.logging.MemoryHandler} with the
* {@link java.util.logging.MemoryHandler#pushLevel} set to
* {@link java.util.logging.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 {@link OperationGroup} set to {@link java.util.logging.Level#INFO},
* the creation of member {@link Operation}s at the
* {@link java.util.logging.Level#CONFIG} level, and execution of member
* {@link Operation}s at the {@link java.util.logging.Level#FINE} level.
* Detailed information about the execution of member {@link Operation}s may
* be logged at the {@link java.util.logging.Level#FINER} and
* {@link java.util.logging.Level#FINEST} levels. Errors in the execution of
* user code should be logged at the {@link java.util.logging.Level#WARNING}
* Level. Errors in the implementation code should be logged at the
* {@link java.util.logging.Level#SEVERE} Level.
*
* @param logger used by the implementation to log significant events
* @return this {@link OperationGroup}
*/
public OperationGroup<S, T> logger(Logger logger);
// Covariant overrides
@Override
public OperationGroup<S, T> timeout(Duration minTime);
@Override
public OperationGroup<S, T> onError(Consumer<Throwable> handler);
}