TransactionProxy is vulnerable to wrong use of close() after ready().
Fix this up so we detect this condition and make it explicit what
happens when.
Change-Id: Id966b57ce53cdc92bc3e53cb0d38b77303ba1c54
Signed-off-by: Robert Varga <rovarga@cisco.com>
+ private static enum TransactionState {
+ OPEN,
+ READY,
+ CLOSED,
+ }
+
static final Mapper<Throwable, Throwable> SAME_FAILURE_TRANSFORMER =
new Mapper<Throwable, Throwable>() {
@Override
static final Mapper<Throwable, Throwable> SAME_FAILURE_TRANSFORMER =
new Mapper<Throwable, Throwable>() {
@Override
private final ActorContext actorContext;
private final String transactionChainId;
private final SchemaContext schemaContext;
private final ActorContext actorContext;
private final String transactionChainId;
private final SchemaContext schemaContext;
- private boolean inReadyState;
+ private TransactionState state = TransactionState.OPEN;
private volatile boolean initialized;
private Semaphore operationLimiter;
private volatile boolean initialized;
private Semaphore operationLimiter;
private void checkModificationState() {
Preconditions.checkState(transactionType != TransactionType.READ_ONLY,
"Modification operation on read-only transaction is not allowed");
private void checkModificationState() {
Preconditions.checkState(transactionType != TransactionType.READ_ONLY,
"Modification operation on read-only transaction is not allowed");
- Preconditions.checkState(!inReadyState,
+ Preconditions.checkState(state == TransactionState.OPEN,
"Transaction is sealed - further modifications are not allowed");
}
"Transaction is sealed - further modifications are not allowed");
}
@Override
public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
@Override
public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ private boolean seal(final TransactionState newState) {
+ if (state == TransactionState.OPEN) {
+ state = newState;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@Override
public DOMStoreThreePhaseCommitCohort ready() {
@Override
public DOMStoreThreePhaseCommitCohort ready() {
+ Preconditions.checkState(transactionType != TransactionType.READ_ONLY,
+ "Read-only transactions cannot be readied");
- checkModificationState();
-
- inReadyState = true;
+ final boolean success = seal(TransactionState.READY);
+ Preconditions.checkState(success, "Transaction %s is %s, it cannot be readied", getIdentifier(), state);
LOG.debug("Tx {} Readying {} transactions for commit", getIdentifier(),
txFutureCallbackMap.size());
LOG.debug("Tx {} Readying {} transactions for commit", getIdentifier(),
txFutureCallbackMap.size());
@Override
public void close() {
@Override
public void close() {
+ if (!seal(TransactionState.CLOSED)) {
+ if (state == TransactionState.CLOSED) {
+ // Idempotent no-op as per AutoCloseable recommendation
+ return;
+ }
+
+ throw new IllegalStateException(String.format("Transaction %s is ready, it cannot be closed",
+ getIdentifier()));
+ }
+
for (TransactionFutureCallback txFutureCallback : txFutureCallbackMap.values()) {
txFutureCallback.enqueueTransactionOperation(new TransactionOperation() {
@Override
for (TransactionFutureCallback txFutureCallback : txFutureCallbackMap.values()) {
txFutureCallback.enqueueTransactionOperation(new TransactionOperation() {
@Override