Bug 319: Fixed two-phase commit verification of data.
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / impl / SchemaAwareDataStoreAdapter.java
1 package org.opendaylight.controller.sal.dom.broker.impl;
2
3 import java.util.ArrayList;
4 import java.util.Comparator;
5 import java.util.Map.Entry;
6 import java.util.concurrent.Future;
7
8 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
9 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
10 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
11 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
12 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
13 import org.opendaylight.controller.sal.core.api.data.DataStore;
14 import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
15 import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.common.RpcResult;
18 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
19 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.Node;
21 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
22 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
24 import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import com.google.common.base.Predicate;
29 import com.google.common.collect.FluentIterable;
30
31 import static com.google.common.base.Preconditions.*;
32
33 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
34         DataStore, //
35         SchemaServiceListener, //
36         AutoCloseable {
37
38     private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
39
40     private SchemaContext schema = null;
41     private boolean validationEnabled = false;
42     private DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
43
44     @Override
45     public boolean containsConfigurationPath(InstanceIdentifier path) {
46         try {
47             getDelegateReadLock().lock();
48             return getDelegate().containsConfigurationPath(path);
49
50         } finally {
51             getDelegateReadLock().unlock();
52         }
53     }
54
55     @Override
56     public boolean containsOperationalPath(InstanceIdentifier path) {
57         try {
58             getDelegateReadLock().lock();
59             return getDelegate().containsOperationalPath(path);
60
61         } finally {
62             getDelegateReadLock().unlock();
63         }
64     }
65
66     @Override
67     public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
68         try {
69             getDelegateReadLock().lock();
70             return getDelegate().getStoredConfigurationPaths();
71
72         } finally {
73             getDelegateReadLock().unlock();
74         }
75     }
76
77     @Override
78     public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
79         try {
80             getDelegateReadLock().lock();
81             return getDelegate().getStoredOperationalPaths();
82
83         } finally {
84             getDelegateReadLock().unlock();
85         }
86     }
87
88     @Override
89     public CompositeNode readConfigurationData(InstanceIdentifier path) {
90         return reader.readConfigurationData(path);
91     }
92
93     @Override
94     public CompositeNode readOperationalData(InstanceIdentifier path) {
95         return reader.readOperationalData(path);
96     }
97
98     @Override
99     public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
100             DataModification<InstanceIdentifier, CompositeNode> modification) {
101         validateAgainstSchema(modification);
102         NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
103         cleanedUp.status = TransactionStatus.SUBMITED;
104         return retrieveDelegate().requestCommit(cleanedUp);
105     }
106
107     public boolean isValidationEnabled() {
108         return validationEnabled;
109     }
110
111     public void setValidationEnabled(boolean validationEnabled) {
112         this.validationEnabled = validationEnabled;
113     }
114
115     private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
116         if (!validationEnabled) {
117             return;
118         }
119
120         if (schema == null) {
121             LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
122             return;
123         }
124     }
125
126     @Override
127     protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
128         // NOOP
129     }
130
131     @Override
132     public void onGlobalContextUpdated(SchemaContext context) {
133         this.schema = context;
134     }
135
136     @Override
137     public void close() throws Exception {
138         this.schema = null;
139     }
140
141     protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
142             boolean config) {
143         long startTime = System.nanoTime();
144         try {
145             DataSchemaNode node = schemaNodeFor(path);
146             return YangDataOperations.merge(node, stored, modified, config);
147         } finally {
148             // System.out.println("Merge time: " + ((System.nanoTime() -
149             // startTime) / 1000.0d));
150         }
151     }
152
153     private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
154         checkState(schema != null, "YANG Schema is not available");
155         return YangSchemaUtils.getSchemaNode(schema, path);
156     }
157
158     private NormalizedDataModification prepareMergedTransaction(
159             DataModification<InstanceIdentifier, CompositeNode> original) {
160         // NOOP for now
161         NormalizedDataModification normalized = new NormalizedDataModification(original);
162         for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
163             normalized.putConfigurationData(entry.getKey(), entry.getValue());
164         }
165         for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
166             normalized.putOperationalData(entry.getKey(), entry.getValue());
167         }
168         for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
169             normalized.removeConfigurationData(entry);
170         }
171         for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
172             normalized.removeOperationalData(entry);
173         }
174         return normalized;
175     }
176
177     private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
178         @Override
179         public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
180             InstanceIdentifier o1Key = o1.getKey();
181             InstanceIdentifier o2Key = o2.getKey();
182             return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
183         }
184     };
185
186     private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
187
188         @Override
189         public CompositeNode readConfigurationData(final InstanceIdentifier path) {
190             getDelegateReadLock().lock();
191             try {
192                 if (path.getPath().isEmpty()) {
193                     return null;
194                 }
195                 QName qname = null;
196                 CompositeNode original = getDelegate().readConfigurationData(path);
197                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
198                 if (original != null) {
199                     childNodes.addAll(original.getChildren());
200                     qname = original.getNodeType();
201                 } else {
202                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
203                 }
204
205                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
206                         .filter(new Predicate<InstanceIdentifier>() {
207                             @Override
208                             public boolean apply(InstanceIdentifier input) {
209                                 if (path.contains(input)) {
210                                     int nesting = input.getPath().size() - path.getPath().size();
211                                     if (nesting == 1) {
212                                         return true;
213                                     }
214                                 }
215                                 return false;
216                             }
217                         });
218                 for (InstanceIdentifier instanceIdentifier : directChildren) {
219                     childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
220                 }
221                 if (original == null && childNodes.isEmpty()) {
222                     return null;
223                 }
224
225                 return new CompositeNodeTOImpl(qname, null, childNodes);
226             } finally {
227                 getDelegateReadLock().unlock();
228             }
229         }
230
231         @Override
232         public CompositeNode readOperationalData(final InstanceIdentifier path) {
233             getDelegateReadLock().lock();
234             try {
235                 if (path.getPath().isEmpty()) {
236                     return null;
237                 }
238                 QName qname = null;
239                 CompositeNode original = getDelegate().readOperationalData(path);
240                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
241                 if (original != null) {
242                     childNodes.addAll(original.getChildren());
243                     qname = original.getNodeType();
244                 } else {
245                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
246                 }
247
248                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
249                         .filter(new Predicate<InstanceIdentifier>() {
250                             @Override
251                             public boolean apply(InstanceIdentifier input) {
252                                 if (path.contains(input)) {
253                                     int nesting = input.getPath().size() - path.getPath().size();
254                                     if (nesting == 1) {
255                                         return true;
256                                     }
257                                 }
258                                 return false;
259                             }
260                         });
261
262                 for (InstanceIdentifier instanceIdentifier : directChildren) {
263                     childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
264                 }
265                 if (original == null && childNodes.isEmpty()) {
266                     return null;
267                 }
268
269                 return new CompositeNodeTOImpl(qname, null, childNodes);
270             } finally {
271                 getDelegateReadLock().unlock();
272             }
273         }
274     }
275
276     private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
277
278         private Object identifier;
279         private TransactionStatus status;
280
281         public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
282             super(getDelegate());
283             identifier = original;
284             status = TransactionStatus.NEW;
285         }
286
287         @Override
288         public Object getIdentifier() {
289             return this.identifier;
290         }
291
292         @Override
293         public TransactionStatus getStatus() {
294             return status;
295         }
296
297         @Override
298         public Future<RpcResult<TransactionStatus>> commit() {
299             throw new UnsupportedOperationException("Commit should not be invoked on this");
300         }
301
302         @Override
303         protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
304                 CompositeNode modified) {
305             return mergeData(path, stored, modified, true);
306         }
307
308         @Override
309         protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
310                 CompositeNode modified) {
311             return mergeData(path, stored, modified, false);
312         }
313     }
314
315 }