4a88f463a67dd3822ab7d3decdd011b117bc23dd
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / ShardedDOMTransactionChainAdapter.java
1 /*
2  * Copyright (c) 2016 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 com.google.common.base.Preconditions;
12 import com.google.common.collect.ClassToInstanceMap;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.EnumMap;
20 import java.util.Map;
21 import java.util.concurrent.atomic.AtomicLong;
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24 import org.opendaylight.mdsal.common.api.AsyncTransaction;
25 import org.opendaylight.mdsal.common.api.CommitInfo;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.common.api.TransactionChainListener;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeLoopException;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerException;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeServiceExtension;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
39 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
40 import org.opendaylight.yangtools.concepts.ListenerRegistration;
41
42 public class ShardedDOMTransactionChainAdapter implements DOMTransactionChain {
43
44     private final DOMDataTreeService dataTreeService;
45     private final Object txChainIdentifier;
46     private final AtomicLong txNum = new AtomicLong();
47     private final TransactionChainListener txChainListener;
48     private final CachedDataTreeService cachedDataTreeService;
49     private TransactionChainWriteTransaction writeTx;
50     private TransactionChainReadTransaction readTx;
51     private ListenableFuture<? extends CommitInfo> writeTxCommitFuture;
52     private boolean finished = false;
53
54     public ShardedDOMTransactionChainAdapter(final Object txChainIdentifier,
55                                              final DOMDataTreeService dataTreeService,
56                                              final TransactionChainListener txChainListener) {
57         Preconditions.checkNotNull(dataTreeService);
58         Preconditions.checkNotNull(txChainIdentifier);
59         this.dataTreeService = dataTreeService;
60         this.txChainIdentifier = txChainIdentifier;
61         this.txChainListener = txChainListener;
62         this.cachedDataTreeService = new CachedDataTreeService(dataTreeService);
63     }
64
65     @Override
66     public DOMDataTreeReadTransaction newReadOnlyTransaction() {
67         checkRunning();
68         checkReadTxClosed();
69         checkWriteTxClosed();
70         readTx = new TransactionChainReadTransaction(newTransactionIdentifier(),
71                 new ShardedDOMReadTransactionAdapter(newTransactionIdentifier(), dataTreeService),
72                 writeTxCommitFuture, this);
73
74         return readTx;
75     }
76
77     @Override
78     public DOMDataTreeWriteTransaction newWriteOnlyTransaction() {
79         checkRunning();
80         checkWriteTxClosed();
81         checkReadTxClosed();
82         writeTx = new TransactionChainWriteTransaction(newTransactionIdentifier(),
83                 new ShardedDOMWriteTransactionAdapter(newTransactionIdentifier(),
84                         cachedDataTreeService), this);
85
86         return writeTx;
87     }
88
89     @Override
90     public DOMDataTreeReadWriteTransaction newReadWriteTransaction() {
91         checkRunning();
92         checkWriteTxClosed();
93         checkReadTxClosed();
94         ShardedDOMReadWriteTransactionAdapter adapter = new ShardedDOMReadWriteTransactionAdapter(
95                 newTransactionIdentifier(), cachedDataTreeService);
96         TransactionChainReadWriteTransaction readWriteTx = new TransactionChainReadWriteTransaction(
97                 newTransactionIdentifier(), adapter, adapter.getReadAdapter(), writeTxCommitFuture, this);
98
99         writeTx = readWriteTx;
100         return readWriteTx;
101     }
102
103     @Override
104     public void close() {
105         if (finished) {
106             // already closed, do nothing
107             return;
108         }
109
110         checkReadTxClosed();
111         checkWriteTxClosed();
112         Futures.addCallback(writeTxCommitFuture, new FutureCallback<CommitInfo>() {
113             @Override
114             public void onSuccess(@Nullable final CommitInfo result) {
115                 txChainListener.onTransactionChainSuccessful(ShardedDOMTransactionChainAdapter.this);
116             }
117
118             @Override
119             public void onFailure(final Throwable throwable) {
120                 // We don't have to do nothing here,
121                 // tx should take car of it
122             }
123         }, MoreExecutors.directExecutor());
124
125         cachedDataTreeService.closeProducers();
126         finished = true;
127     }
128
129     public void closeReadTransaction() {
130         readTx = null;
131     }
132
133     public void closeWriteTransaction(final ListenableFuture<? extends CommitInfo> commitFuture) {
134         writeTxCommitFuture = commitFuture;
135         writeTx = null;
136     }
137
138     private Object newTransactionIdentifier() {
139         return "DOM-CHAIN-" + txChainIdentifier + "-" + txNum.getAndIncrement();
140     }
141
142     private void checkWriteTxClosed() {
143         Preconditions.checkState(writeTx == null);
144     }
145
146     private void checkReadTxClosed() {
147         Preconditions.checkState(readTx == null);
148     }
149
150     private void checkRunning() {
151         Preconditions.checkState(!finished);
152     }
153
154     public void transactionFailed(final AsyncTransaction<?, ?> tx, final Throwable cause) {
155         txChainListener.onTransactionChainFailed(this, tx, cause);
156         if (writeTx != null) {
157             writeTx.cancel();
158         }
159         if (readTx != null) {
160             readTx.close();
161         }
162         cachedDataTreeService.closeProducers();
163         finished = true;
164     }
165
166     static class CachedDataTreeService implements DOMDataTreeService {
167
168         private final DOMDataTreeService delegateTreeService;
169         private final Map<LogicalDatastoreType, NoopCloseDataProducer> producersMap =
170                 new EnumMap<>(LogicalDatastoreType.class);
171
172         CachedDataTreeService(final DOMDataTreeService delegateTreeService) {
173             this.delegateTreeService = delegateTreeService;
174         }
175
176         void closeProducers() {
177             producersMap.values().forEach(NoopCloseDataProducer::closeDelegate);
178         }
179
180         @Nonnull
181         @Override
182         public <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(
183                 @Nonnull final T listener, @Nonnull final Collection<DOMDataTreeIdentifier> subtrees,
184                          final boolean allowRxMerges, @Nonnull final Collection<DOMDataTreeProducer> producers)
185                 throws DOMDataTreeLoopException {
186             return delegateTreeService.registerListener(listener, subtrees, allowRxMerges, producers);
187         }
188
189         @Override
190         @Deprecated
191         public Map<Class<? extends DOMDataTreeServiceExtension>, DOMDataTreeServiceExtension> getSupportedExtensions() {
192             return delegateTreeService.getSupportedExtensions();
193         }
194
195         @Override
196         public ClassToInstanceMap<DOMDataTreeServiceExtension> getExtensions() {
197             return delegateTreeService.getExtensions();
198         }
199
200         @Override
201         public DOMDataTreeProducer createProducer(@Nonnull final Collection<DOMDataTreeIdentifier> subtrees) {
202             Preconditions.checkState(subtrees.size() == 1);
203             NoopCloseDataProducer producer = null;
204             for (final DOMDataTreeIdentifier treeId : subtrees) {
205                 producer =
206                         new NoopCloseDataProducer(delegateTreeService.createProducer(Collections.singleton(treeId)));
207                 producersMap.putIfAbsent(treeId.getDatastoreType(),
208                         producer);
209             }
210             return producer;
211         }
212
213         static class NoopCloseDataProducer implements DOMDataTreeProducer {
214
215             private final DOMDataTreeProducer delegateTreeProducer;
216
217             NoopCloseDataProducer(final DOMDataTreeProducer delegateTreeProducer) {
218                 this.delegateTreeProducer = delegateTreeProducer;
219             }
220
221             @Nonnull
222             @Override
223             public DOMDataTreeCursorAwareTransaction createTransaction(final boolean isolated) {
224                 return delegateTreeProducer.createTransaction(isolated);
225             }
226
227             @Nonnull
228             @Override
229             public DOMDataTreeProducer createProducer(@Nonnull final Collection<DOMDataTreeIdentifier> subtrees) {
230                 return delegateTreeProducer.createProducer(subtrees);
231             }
232
233             @Override
234             public void close() throws DOMDataTreeProducerException {
235                 // noop
236             }
237
238             public void closeDelegate() {
239                 try {
240                     delegateTreeProducer.close();
241                 } catch (final DOMDataTreeProducerException e) {
242                     throw new IllegalStateException("Trying to close DOMDataTreeProducer with open transaction", e);
243                 }
244             }
245         }
246     }
247 }