/*
* 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 java.util.Objects ;
import org.apache.jena.query.ReadWrite ;
import org.apache.jena.shared.Lock ;
import org.apache.jena.shared.LockMRPlusSW ;
import org.apache.jena.shared.LockMRSW ;
import org.apache.jena.shared.LockMutex ;
import org.apache.jena.sparql.JenaTransactionException ;
/** An implementation of Tranactional that provides MRSW locking but no abort.
* This is often the best you can do for a system that does not itself provide transactions.
* @apiNote
* To use with implementation inheritance, for when you don't inherit:
* <pre>
* private final Transactional txn = TransactionalLock.createMRSW() ;
* {@literal @}Override public void begin(ReadWrite mode) { txn.begin(mode) ; }
* {@literal @}Override public void commit() { txn.commit() ; }
* {@literal @}Override public void abort() { txn.abort() ; }
* {@literal @}Override public boolean isInTransaction() { return txn.isInTransaction() ; }
* {@literal @}Override public void end() { txn.end(); }
* {@literal @}Override public boolean supportsTransactions() { return true ; }
* {@literal @}Override public boolean supportsTransactionAbort() { return false ; }
* </pre>
*/
public class TransactionalLock implements Transactional {
/*
private final Transactional txn = TransactionalLock.createMRSW() ;
@Override public void begin(ReadWrite mode) { txn.begin(mode) ; }
@Override public void commit() { txn.commit() ; }
@Override public void abort() { txn.abort() ; }
@Override public boolean isInTransaction() { return txn.isInTransaction() ; }
@Override public void end() { txn.end(); }
@Override public boolean supportsTransactions() { return true ; }
@Override public boolean supportsTransactionAbort() { return false ; }
*/
private final ThreadLocal<ReadWrite> txnMode = ThreadLocal.withInitial( ()->null ) ;
private final Lock lock ;
/** Create a Transactional using the given lock */
public static TransactionalLock create(Lock lock) {
return new TransactionalLock(lock) ;
}
/** Create a Transactional using a MR+SW (Multiple Reader AND a Single Writer) lock */
public static TransactionalLock createMRPlusSW() {
return create(new LockMRPlusSW()) ;
}
/** Create a Transactional using a MRSW (Multiple Reader OR a Single Writer) lock */
public static TransactionalLock createMRSW() {
return create(new LockMRSW()) ;
}
/** Create a Transactional using a mutex (exclusive - one at a time) lock */
public static TransactionalLock createMutex() {
return create(new LockMutex()) ;
}
private TransactionalLock(Lock lock) {
this.lock = lock ;
}
@Override
public void begin(ReadWrite readWrite) {
if ( isInTransaction() )
error("Already in a transaction") ;
boolean isRead = readWrite.equals(ReadWrite.READ) ;
lock.enterCriticalSection(isRead) ;
txnMode.set(readWrite) ;
}
@Override
public void commit() {
endOnce() ;
}
@Override
public void abort() {
// if ( isTransactionType(ReadWrite.WRITE) )
// error("Transactional.abort()") ;
endOnce() ;
}
@Override
public boolean isInTransaction() {
return txnMode.get() != null ;
}
public boolean isTransactionType(ReadWrite mode) {
return Objects.equals(mode, txnMode.get()) ;
}
private ReadWrite getTransactionType(ReadWrite mode) {
return txnMode.get() ;
}
@Override
public void end() {
if ( isTransactionType(ReadWrite.WRITE) )
error("Write transaction - no commit or abort before end()") ;
endOnce() ;
}
private void endOnce() {
if ( isInTransaction() ) {
lock.leaveCriticalSection() ;
txnMode.remove();
}
}
private void error(String msg) {
throw new JenaTransactionException(msg) ;
}
}