blob: e447d236c696e6ea70c520b3288c3d4a76fea319 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.sparql.core;
import org.apache.jena.query.ReadWrite ;
import org.apache.jena.query.TxnType;
import org.apache.jena.sparql.JenaTransactionException;
import org.apache.jena.system.Txn;
/** Interface that encapsulates the begin/abort|commit/end operations.
* <p>The read lifecycle is:
* <pre> begin(READ) ... end()</pre>
* <p>{@code commit} and {@code abort} are allowed.
* <p>The write lifecycle is:
* <pre> begin(WRITE) ... abort() or commit()</pre>
* <p>{@code end()} is optional but preferred.
* <p>
* Helper code is available {@link Txn} so, for example:
* <pre>Txn.executeRead(dataset, {@literal ()->} { ... sparql query ... });</pre>
* <pre>Txn.executeWrite(dataset, {@literal ()->} { ... sparql update ... });</pre>
* or use one of <tt>Txn.calculateRead</tt> and <tt>Txn.executeWrite</tt>
* to return a value for the transaction block.
* <p>
* Directly called, code might look like:
* <pre>
* Transactional object = ...
* object.begin(TxnMode.READ) ;
* try {
* ... actions inside a read transaction ...
* } finally { object.end() ; }
* </pre>
* or
* <pre>
* Transactional object = ...
* object.begin(TxnMode.WRITE) ;
* try {
* ... actions inside a write transaction ...
* object.commit() ;
* } finally {
* // This causes an abort if {@code commit} has not been called.
* object.end() ;
* }
* </pre>
*/
public interface Transactional
{
/**
* Start a transaction which is READ mode and which will switch to WRITE if an update
* is attempted but only if no intermediate transaction has performed an update.
* <p>
* See {@link #begin(TxnType)} for more details an options.
* <p>
* May not be implemented. See {@link #begin(ReadWrite)} is guaranteed to be provided.
*/
public default void begin() { begin(TxnType.READ_PROMOTE); }
/**
* Start a transaction.<br/>
* READ or WRITE transactions start in that state and do not change for the
* lifetime of the transaction.
* <ul>
*
* <li>{@code WRITE}: this guarantees a WRITE will complete if {@code commit()} is
* called. The same as {@code begin(ReadWrite.WRITE)}.
*
* <li>{@code READ}: the transaction can not promote to WRITE,ensuring read-only
* access to the data. The same as {@code begin(ReadWrite.READ)}.
*
* <li>{@code READ_PROMOTE}: the transaction will go from "read" to "write" if an
* update is attempted and if the dataset has not been changed by another write
* transaction. See also {@link #promote}.
*
* <li>{@code READ_COMMITTED_PROMOTE}: Use this with care. The promotion will
* succeed but changes from other transactions become visible.
*
* </ul>
*
* Read committed: at the point transaction attempts promotion from "read" to
* "write", the system checks if the dataset has change since the transaction started
* (called {@code begin}). If {@code READ_PROMOTE}, the dataset must not have
* changed; if {@code READ_COMMITTED_PROMOTE} any intermediate changes are
* visible but the application can not assume any data it has read in the
* transaction is the same as it was at the point the transaction started.
* <p>
* This operation is optional and some implementations may throw
* a {@link JenaTransactionException} exception for some or all {@link TxnType} values.
* <p>
* See {@link #begin(ReadWrite)} for a form that is required of implementations.
*/
public void begin(TxnType type);
/** Start either a READ or WRITE transaction. */
public void begin(ReadWrite readWrite) ;
/**
* Attempt to promote a transaction from "read" to "write" when the transaction
* started with a "promote" mode ({@code READ_PROMOTE} or
* {@code READ_COMMITTED_PROMOTE}).
* <p>
* Returns "true" if the transaction is in write mode after the call. The method
* always succeeds of the transaction is already "write".
* <p>
* A {@code READ_COMMITTED_PROMOTE} can always be promoted, but the call may need to
* wait.
* <p>
* This method returns true if a {@code READ_PROMOTE} or
* {@code READ_COMMITTED_PROMOTE} is promoted.
* <p>
* This method returns false if a {@code READ_PROMOTE} can't be promoted - the
* transaction is still valid and in "read" mode. Any further calls to
* {@code promote()} will also return false.
* <p>
* <p>
* This method returns false if there is an attempt to promote a "READ" transaction.
*/
public default boolean promote() {
if ( transactionMode() == ReadWrite.WRITE )
return true;
TxnType txnType = transactionType();
if ( txnType == null )
throw new JenaTransactionException("txnType");
switch(txnType) {
case WRITE : return true;
case READ : return false;
case READ_PROMOTE : return promote(Promote.ISOLATED);
case READ_COMMITTED_PROMOTE : return promote(Promote.READ_COMMITTED);
}
throw new JenaTransactionException("Can't determine promote '"+txnType+"'transaction");
}
public enum Promote { ISOLATED, READ_COMMITTED } ;
/**
* Attempt to promote a transaction from "read" mode to "write" and the transaction. This
* method allows the form of promotion to be specified. The transaction must not have been started
* with {@code READ}, which is read-only.
* <p>
* An argument of {@code READ_PROMOTE} treats the promotion as if the transaction was started
* with {@code READ_PROMOTE} (any other writer commiting since the transaction started
* blocks promotion) and {@code READ_COMMITTED_PROMOTE} treats the promotion as if the transaction was started
* with {@code READ_COMMITTED_PROMOTE} (intemediate writer commits become visible).
* <p>
* Returns "true" if the transaction is in write mode after the call. The method
* always succeeds of the transaction is already "write".
* <p>
* This method returns true if a {@code READ_PROMOTE} or
* {@code READ_COMMITTED_PROMOTE} is promoted.
* <p>
* This method returns false if a {@code READ_PROMOTE} can't be promoted - the
* transaction is still valid and in "read" mode.
* <p>
* This method throws an exception if there is an attempt to promote a {@code READ}
* transaction.
*/
public boolean promote(Promote mode);
/** Commit a transaction - finish the transaction and make any changes permanent (if a "write" transaction) */
public void commit() ;
/** Abort a transaction - finish the transaction and undo any changes (if a "write" transaction) */
public void abort() ;
/** Finish the transaction - if a write transaction and commit() has not been called, then abort */
public void end() ;
/** Return the current mode of the transaction - "read" or "write".
* If the caller is not in a transaction, this method returns null.
*/
public ReadWrite transactionMode();
/** Return the transaction type used in {@code begin(TxnType)}.
* If the caller is not in a transaction, this method returns null.
*/
public TxnType transactionType();
/** Say whether inside a transaction. */
public boolean isInTransaction() ;
}