src/jdk.incubator.adba/share/classes/jdk/incubator/sql2/OperationGroup.java
author lancea
Mon, 09 Jul 2018 15:09:06 -0400
branchJDK-8188051-branch
changeset 56824 62e92191354d
parent 56797 fb523d4d9185
child 56828 64304e37e9b1
permissions -rw-r--r--
JDK-8188051-branch latest updates

/*
 * 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>&lt;List&lt;Integer&gt;&gt;</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);
  
}