6ead33c0e05e20c5e1f671836e00e08241633950
[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.CheckedFuture;
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.Collections;
17 import java.util.EnumMap;
18 import java.util.List;
19 import java.util.Map;
20 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
21 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
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 CheckedFuture<Void, TransactionCommitFailedException> submit() {
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 Futures.immediateCheckedFuture(null);
81         }
82         // First we need to close cursors
83         cursorMap.values().forEach(DOMDataTreeWriteCursor::close);
84         final ListenableFuture<List<Void>> aggregatedSubmit = Futures.allAsList(
85                 transactionMap.get(LogicalDatastoreType.CONFIGURATION).submit(),
86                 transactionMap.get(LogicalDatastoreType.OPERATIONAL).submit());
87
88         // Now we can close producers and mark transaction as finished
89         closeProducers();
90         finished = true;
91
92         return Futures.makeChecked(
93                 Futures.transform(aggregatedSubmit, input -> input.get(0), MoreExecutors.directExecutor()),
94                 TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
95     }
96
97     @Override
98     public Object getIdentifier() {
99         return txIdentifier;
100     }
101
102     @Override
103     public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path,
104                     final NormalizedNode<?, ?> data) {
105         checkRunning();
106         LOG.debug("{}: Invoking put operation at {}:{} with payload {}", txIdentifier, store, path);
107         if (!initialized) {
108             initializeDataTreeProducerLayer(path.getParent());
109         }
110
111         final DOMDataTreeWriteCursor cursor = cursorMap.get(store);
112         cursor.write(path.getLastPathArgument(), data);
113     }
114
115     @Override
116     public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
117                       final NormalizedNode<?, ?> data) {
118         checkRunning();
119         LOG.debug("{}: Invoking merge operation at {}:{} with payload {}", txIdentifier, store, path);
120         if (!initialized) {
121             initializeDataTreeProducerLayer(path.getParent());
122         }
123
124         final DOMDataTreeWriteCursor cursor = cursorMap.get(store);
125         cursor.merge(path.getLastPathArgument(), data);
126     }
127
128     @Override
129     public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
130         checkRunning();
131         LOG.debug("{}: Invoking delete operation at {}:{}", txIdentifier, store, path);
132         if (!initialized) {
133             initializeDataTreeProducerLayer(path.getParent());
134         }
135
136         final DOMDataTreeWriteCursor cursor = cursorMap.get(store);
137         cursor.delete(path.getLastPathArgument());
138     }
139
140     // TODO initialize producer, transaction and cursor for only
141     // for necessary data store at one time
142     private void initializeDataTreeProducerLayer(final YangInstanceIdentifier path) {
143         Preconditions.checkState(producerMap.isEmpty(), "Producers already initialized");
144         Preconditions.checkState(transactionMap.isEmpty(), "Transactions already initialized");
145         Preconditions.checkState(cursorMap.isEmpty(), "Cursors already initialized");
146
147         LOG.debug("{}: Creating data tree producers on path {}", txIdentifier, path);
148         producerMap.put(LogicalDatastoreType.CONFIGURATION,
149                 treeService.createProducer(
150                         Collections.singleton(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, path))));
151         producerMap.put(LogicalDatastoreType.OPERATIONAL,
152                 treeService.createProducer(
153                         Collections.singleton(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path))));
154
155         LOG.debug("{}: Creating DOMDataTreeCursorAwareTransactions delegates", txIdentifier, path);
156         transactionMap.put(LogicalDatastoreType.CONFIGURATION,
157                 producerMap.get(LogicalDatastoreType.CONFIGURATION).createTransaction(true));
158         transactionMap.put(LogicalDatastoreType.OPERATIONAL,
159                 producerMap.get(LogicalDatastoreType.OPERATIONAL).createTransaction(true));
160
161         LOG.debug("{}: Creating DOMDataTreeWriteCursors delegates");
162         cursorMap.put(LogicalDatastoreType.CONFIGURATION,
163                 transactionMap.get(LogicalDatastoreType.CONFIGURATION)
164                         .createCursor(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, path)));
165         cursorMap.put(LogicalDatastoreType.OPERATIONAL,
166                 transactionMap.get(LogicalDatastoreType.OPERATIONAL)
167                         .createCursor(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path)));
168
169         initialized = true;
170     }
171
172     private void checkRunning() {
173         Preconditions.checkState(!finished, "{}: Transaction already finished");
174     }
175
176     private void closeProducers() {
177         producerMap.values().forEach(domDataTreeProducer -> {
178             try {
179                 domDataTreeProducer.close();
180             } catch (final DOMDataTreeProducerException e) {
181                 throw new IllegalStateException("Trying to close DOMDataTreeProducer with open transaction", e);
182             }
183         });
184     }
185 }