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