Switch to Objects.requireNonNull
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / DOMDataBrokerTransactionChainImpl.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, 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
9 package org.opendaylight.mdsal.dom.broker;
10
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.Collection;
18 import java.util.Map;
19 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
20 import java.util.concurrent.atomic.AtomicLong;
21 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
22 import org.opendaylight.mdsal.common.api.CommitInfo;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
25 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
26 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
27 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
28 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * NormalizedNode implementation of {@link org.opendaylight.mdsal.common.api.TransactionChain} which is backed
34  * by several {@link DOMStoreTransactionChain} differentiated by provided
35  * {@link org.opendaylight.mdsal.common.api.LogicalDatastoreType} type.
36  *
37  */
38 final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransactionFactory<DOMStoreTransactionChain>
39         implements DOMTransactionChain {
40     private enum State {
41         RUNNING,
42         CLOSING,
43         CLOSED,
44         FAILED,
45     }
46
47     private static final AtomicIntegerFieldUpdater<DOMDataBrokerTransactionChainImpl> COUNTER_UPDATER =
48             AtomicIntegerFieldUpdater.newUpdater(DOMDataBrokerTransactionChainImpl.class, "counter");
49     private static final AtomicReferenceFieldUpdater<DOMDataBrokerTransactionChainImpl, State> STATE_UPDATER =
50             AtomicReferenceFieldUpdater.newUpdater(DOMDataBrokerTransactionChainImpl.class, State.class, "state");
51     private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class);
52     private final AtomicLong txNum = new AtomicLong();
53     private final AbstractDOMDataBroker broker;
54     private final DOMTransactionChainListener listener;
55     private final long chainId;
56
57     private volatile State state = State.RUNNING;
58     private volatile int counter = 0;
59
60     /**
61      *Constructor with args.
62      *
63      * @param chainId
64      *            ID of transaction chain
65      * @param chains
66      *            Backing {@link DOMStoreTransactionChain}s.
67      * @param coordinator
68      *            Commit Coordinator which should be used to coordinate commits
69      *            of transaction
70      *            produced by this chain.
71      * @param listener
72      *            Listener, which listens on transaction chain events.
73      * @throws NullPointerException
74      *             If any of arguments is null.
75      */
76     DOMDataBrokerTransactionChainImpl(final long chainId,
77             final Map<LogicalDatastoreType, DOMStoreTransactionChain> chains,
78             final AbstractDOMDataBroker broker, final DOMTransactionChainListener listener) {
79         super(chains);
80         this.chainId = chainId;
81         this.broker = requireNonNull(broker);
82         this.listener = requireNonNull(listener);
83     }
84
85     private void checkNotFailed() {
86         Preconditions.checkState(state != State.FAILED, "Transaction chain has failed");
87     }
88
89     @Override
90     protected Object newTransactionIdentifier() {
91         return "DOM-CHAIN-" + chainId + "-" + txNum.getAndIncrement();
92     }
93
94     @Override
95     protected FluentFuture<? extends CommitInfo> commit(final DOMDataTreeWriteTransaction transaction,
96             final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
97         checkNotFailed();
98         checkNotClosed();
99
100         final FluentFuture<? extends CommitInfo> ret = broker.commit(transaction, cohorts);
101
102         COUNTER_UPDATER.incrementAndGet(this);
103         ret.addCallback(new FutureCallback<CommitInfo>() {
104             @Override
105             public void onSuccess(final CommitInfo result) {
106                 transactionCompleted();
107             }
108
109             @Override
110             public void onFailure(final Throwable throwable) {
111                 transactionFailed(transaction, throwable);
112             }
113         }, MoreExecutors.directExecutor());
114
115         return ret;
116     }
117
118     @Override
119     public void close() {
120         final boolean success = STATE_UPDATER.compareAndSet(this, State.RUNNING, State.CLOSING);
121         if (!success) {
122             LOG.debug("Chain {} is no longer running", this);
123             return;
124         }
125
126         super.close();
127         for (DOMStoreTransactionChain subChain : getTxFactories().values()) {
128             subChain.close();
129         }
130
131         if (counter == 0) {
132             finishClose();
133         }
134     }
135
136     private void finishClose() {
137         state = State.CLOSED;
138         listener.onTransactionChainSuccessful(this);
139     }
140
141     private void transactionCompleted() {
142         if (COUNTER_UPDATER.decrementAndGet(this) == 0 && state == State.CLOSING) {
143             finishClose();
144         }
145     }
146
147     private void transactionFailed(final DOMDataTreeWriteTransaction tx, final Throwable cause) {
148         state = State.FAILED;
149         LOG.debug("Transaction chain {} failed.", this, cause);
150         listener.onTransactionChainFailed(this, tx, cause);
151     }
152 }