594de1565beb7c78d094ca1f64a653ed066be513
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / ShardedDOMWriteTransactionAdapter.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.FluentFuture;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import java.util.Collections;
16 import java.util.EnumMap;
17 import java.util.List;
18 import java.util.Map;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.common.api.CommitInfo;
21 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
23 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerException;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 public class ShardedDOMWriteTransactionAdapter implements DOMDataTreeWriteTransaction {
35     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMWriteTransactionAdapter.class);
36
37     private final Map<LogicalDatastoreType, DOMDataTreeProducer> producerMap;
38     private final Map<LogicalDatastoreType, DOMDataTreeCursorAwareTransaction> transactionMap;
39     private final Map<LogicalDatastoreType, DOMDataTreeWriteCursor> cursorMap;
40
41     private final DOMDataTreeService treeService;
42     private final Object txIdentifier;
43     private boolean finished = false;
44     private boolean initialized = false;
45
46     ShardedDOMWriteTransactionAdapter(final Object identifier, final DOMDataTreeService transactionDelegator) {
47         this.treeService = Preconditions.checkNotNull(transactionDelegator);
48         this.txIdentifier = Preconditions.checkNotNull(identifier);
49         this.producerMap = new EnumMap<>(LogicalDatastoreType.class);
50         this.transactionMap = new EnumMap<>(LogicalDatastoreType.class);
51         this.cursorMap = new EnumMap<>(LogicalDatastoreType.class);
52     }
53
54     @Override
55     public boolean cancel() {
56         LOG.debug("{}: Cancelling transaction");
57         if (finished) {
58             return false;
59         }
60
61         // We close cursor, cancel transactions and close producers and
62         // mark transaction as finished
63         cursorMap.values().forEach(DOMDataTreeWriteCursor::close);
64         transactionMap.values().forEach(domDataTreeCursorAwareTransaction ->
65                 Preconditions.checkState(domDataTreeCursorAwareTransaction.cancel()));
66         closeProducers();
67         finished = true;
68         return true;
69     }
70
71     @Override
72     public @NonNull FluentFuture<? extends @NonNull CommitInfo> commit() {
73         checkRunning();
74         LOG.debug("{}: Submitting transaction", txIdentifier);
75         if (!initialized) {
76             // If underlying producers, transactions and cursors are
77             // not even initialized just seal this transaction and
78             // return immediate future
79             finished = true;
80             return CommitInfo.emptyFluentFuture();
81         }
82         // First we need to close cursors
83         cursorMap.values().forEach(DOMDataTreeWriteCursor::close);
84         final FluentFuture<List<CommitInfo>> aggregatedSubmit = FluentFuture.from(Futures.allAsList(
85                 transactionMap.get(LogicalDatastoreType.CONFIGURATION).commit(),
86                 transactionMap.get(LogicalDatastoreType.OPERATIONAL).commit()));
87
88         // Now we can close producers and mark transaction as finished
89         closeProducers();
90         finished = true;
91
92         return aggregatedSubmit.transform(unused -> CommitInfo.empty(), MoreExecutors.directExecutor());
93     }
94
95     @Override
96     public Object getIdentifier() {
97         return txIdentifier;
98     }
99
100     @Override
101     public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path,
102                     final NormalizedNode<?, ?> data) {
103         checkRunning();
104         LOG.debug("{}: Invoking put operation at {}:{} with payload {}", txIdentifier, store, path);
105         if (!initialized) {
106             initializeDataTreeProducerLayer(path.getParent());
107         }
108
109         final DOMDataTreeWriteCursor cursor = cursorMap.get(store);
110         cursor.write(path.getLastPathArgument(), data);
111     }
112
113     @Override
114     public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
115                       final NormalizedNode<?, ?> data) {
116         checkRunning();
117         LOG.debug("{}: Invoking merge operation at {}:{} with payload {}", txIdentifier, store, path);
118         if (!initialized) {
119             initializeDataTreeProducerLayer(path.getParent());
120         }
121
122         final DOMDataTreeWriteCursor cursor = cursorMap.get(store);
123         cursor.merge(path.getLastPathArgument(), data);
124     }
125
126     @Override
127     public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
128         checkRunning();
129         LOG.debug("{}: Invoking delete operation at {}:{}", txIdentifier, store, path);
130         if (!initialized) {
131             initializeDataTreeProducerLayer(path.getParent());
132         }
133
134         final DOMDataTreeWriteCursor cursor = cursorMap.get(store);
135         cursor.delete(path.getLastPathArgument());
136     }
137
138     // TODO initialize producer, transaction and cursor for only
139     // for necessary data store at one time
140     private void initializeDataTreeProducerLayer(final YangInstanceIdentifier path) {
141         Preconditions.checkState(producerMap.isEmpty(), "Producers already initialized");
142         Preconditions.checkState(transactionMap.isEmpty(), "Transactions already initialized");
143         Preconditions.checkState(cursorMap.isEmpty(), "Cursors already initialized");
144
145         LOG.debug("{}: Creating data tree producers on path {}", txIdentifier, path);
146         producerMap.put(LogicalDatastoreType.CONFIGURATION,
147                 treeService.createProducer(
148                         Collections.singleton(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, path))));
149         producerMap.put(LogicalDatastoreType.OPERATIONAL,
150                 treeService.createProducer(
151                         Collections.singleton(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path))));
152
153         LOG.debug("{}: Creating DOMDataTreeCursorAwareTransactions delegates", txIdentifier, path);
154         transactionMap.put(LogicalDatastoreType.CONFIGURATION,
155                 producerMap.get(LogicalDatastoreType.CONFIGURATION).createTransaction(true));
156         transactionMap.put(LogicalDatastoreType.OPERATIONAL,
157                 producerMap.get(LogicalDatastoreType.OPERATIONAL).createTransaction(true));
158
159         LOG.debug("{}: Creating DOMDataTreeWriteCursors delegates");
160         cursorMap.put(LogicalDatastoreType.CONFIGURATION,
161                 transactionMap.get(LogicalDatastoreType.CONFIGURATION)
162                         .createCursor(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, path)));
163         cursorMap.put(LogicalDatastoreType.OPERATIONAL,
164                 transactionMap.get(LogicalDatastoreType.OPERATIONAL)
165                         .createCursor(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path)));
166
167         initialized = true;
168     }
169
170     private void checkRunning() {
171         Preconditions.checkState(!finished, "{}: Transaction already finished");
172     }
173
174     private void closeProducers() {
175         producerMap.values().forEach(domDataTreeProducer -> {
176             try {
177                 domDataTreeProducer.close();
178             } catch (final DOMDataTreeProducerException e) {
179                 throw new IllegalStateException("Trying to close DOMDataTreeProducer with open transaction", e);
180             }
181         });
182     }
183 }