Implement managed transactions
[mdsal.git] / binding / mdsal-binding-util / src / main / java / org / opendaylight / mdsal / binding / util / ManagedNewTransactionRunnerImpl.java
1 /*
2  * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.util.concurrent.FluentFuture;
14 import java.util.function.Function;
15 import javax.annotation.CheckReturnValue;
16 import javax.inject.Inject;
17 import org.opendaylight.mdsal.binding.api.DataBroker;
18 import org.opendaylight.mdsal.binding.api.Transaction;
19 import org.opendaylight.mdsal.binding.api.TransactionChain;
20 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
21 import org.opendaylight.mdsal.binding.api.WriteTransaction;
22 import org.opendaylight.mdsal.common.api.CommitInfo;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * Implementation of {@link ManagedNewTransactionRunner}. This is based on {@link ManagedTransactionFactoryImpl} but
28  * re-implements operations based on read-write transactions to cancel transactions which don't end up making any
29  * changes to the datastore.
30  */
31 @Beta
32 // Do *NOT* mark this as @Singleton, because users choose their implementation
33 public class ManagedNewTransactionRunnerImpl extends ManagedTransactionFactoryImpl<DataBroker>
34         implements ManagedNewTransactionRunner {
35
36     private static final Logger LOG = LoggerFactory.getLogger(ManagedNewTransactionRunnerImpl.class);
37
38     @Inject
39     public ManagedNewTransactionRunnerImpl(DataBroker broker) {
40         // Early check to ensure the error message is understandable for the caller
41         super(requireNonNull(broker, "broker must not be null"));
42     }
43
44     // This is overridden to use this class’s commit method
45     @Override
46     @CheckReturnValue
47     public <D extends Datastore, E extends Exception, R> FluentFuture<R> applyWithNewReadWriteTransactionAndSubmit(
48             Class<D> datastoreType, InterruptibleCheckedFunction<TypedReadWriteTransaction<D>, R, E> txFunction) {
49         return super.applyWithNewTransactionAndSubmit(datastoreType, getTransactionFactory()::newReadWriteTransaction,
50             WriteTrackingTypedReadWriteTransactionImpl::new, txFunction::apply, this::commit);
51     }
52
53     @Override
54     public <R> R applyWithNewTransactionChainAndClose(Function<ManagedTransactionChain, R> chainConsumer) {
55         try (TransactionChain realTxChain = getTransactionFactory().createTransactionChain(
56             new TransactionChainListener() {
57                 @Override
58                 public void onTransactionChainFailed(TransactionChain chain, Transaction transaction, Throwable cause) {
59                     LOG.error("Error handling a transaction chain", cause);
60                 }
61
62                 @Override
63                 public void onTransactionChainSuccessful(TransactionChain chain) {
64                     // Nothing to do
65                 }
66             })) {
67             return chainConsumer.apply(new ManagedTransactionChainImpl(realTxChain));
68         }
69     }
70
71     // This is overridden to use this class’s commit method
72     @Override
73     @CheckReturnValue
74     public <D extends Datastore, E extends Exception> FluentFuture<? extends Object>
75         callWithNewReadWriteTransactionAndSubmit(Class<D> datastoreType,
76             InterruptibleCheckedConsumer<TypedReadWriteTransaction<D>, E> txConsumer) {
77         return callWithNewTransactionAndSubmit(datastoreType, getTransactionFactory()::newReadWriteTransaction,
78             WriteTrackingTypedReadWriteTransactionImpl::new, txConsumer::accept, this::commit);
79     }
80
81     // This is overridden to use this class’s commit method
82     @Override
83     @CheckReturnValue
84     public <D extends Datastore, E extends Exception> FluentFuture<? extends Object> callWithNewWriteOnlyTransactionAndSubmit(
85             Class<D> datastoreType, InterruptibleCheckedConsumer<TypedWriteTransaction<D>, E> txConsumer) {
86         return super.callWithNewTransactionAndSubmit(datastoreType, getTransactionFactory()::newWriteOnlyTransaction,
87             WriteTrackingTypedWriteTransactionImpl::new, txConsumer::accept, this::commit);
88     }
89
90     @CheckReturnValue
91     private FluentFuture<? extends CommitInfo> commit(WriteTransaction realTx, WriteTrackingTransaction wrappedTx) {
92         if (wrappedTx.isWritten()) {
93             // The transaction contains changes, commit it
94             return realTx.commit();
95         } else {
96             // The transaction only handled reads, cancel it
97             realTx.cancel();
98             return CommitInfo.emptyFluentFuture();
99         }
100     }
101 }