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