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