Merge "Fix a race PingPongTransactionChain"
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / impl / SchemaAwareDataStoreAdapter.java
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.controller.sal.dom.broker.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import com.google.common.base.Predicate;
12 import com.google.common.collect.FluentIterable;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Iterables;
15 import java.util.ArrayList;
16 import java.util.Comparator;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.concurrent.Future;
22 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
23 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
24 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
25 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
26 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
27 import org.opendaylight.controller.sal.core.api.data.DataStore;
28 import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
29 import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.common.RpcResult;
32 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
33 import org.opendaylight.yangtools.yang.data.api.Node;
34 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
36 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
37 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @Deprecated
46 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements DataStore, SchemaContextListener, AutoCloseable {
47
48     private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
49
50     private SchemaContext schema = null;
51     private boolean validationEnabled = false;
52     private final DataReader<YangInstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
53
54     @Override
55     public boolean containsConfigurationPath(final YangInstanceIdentifier path) {
56         try {
57             getDelegateReadLock().lock();
58             return getDelegate().containsConfigurationPath(path);
59
60         } finally {
61             getDelegateReadLock().unlock();
62         }
63     }
64
65     @Override
66     public boolean containsOperationalPath(final YangInstanceIdentifier path) {
67         try {
68             getDelegateReadLock().lock();
69             return getDelegate().containsOperationalPath(path);
70
71         } finally {
72             getDelegateReadLock().unlock();
73         }
74     }
75
76     @Override
77     public Iterable<YangInstanceIdentifier> getStoredConfigurationPaths() {
78         try {
79             getDelegateReadLock().lock();
80             return getDelegate().getStoredConfigurationPaths();
81
82         } finally {
83             getDelegateReadLock().unlock();
84         }
85     }
86
87     @Override
88     public Iterable<YangInstanceIdentifier> getStoredOperationalPaths() {
89         try {
90             getDelegateReadLock().lock();
91             return getDelegate().getStoredOperationalPaths();
92
93         } finally {
94             getDelegateReadLock().unlock();
95         }
96     }
97
98     @Override
99     public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
100         return reader.readConfigurationData(path);
101     }
102
103     @Override
104     public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
105         return reader.readOperationalData(path);
106     }
107
108     @Override
109     public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<YangInstanceIdentifier, CompositeNode> requestCommit(
110             final DataModification<YangInstanceIdentifier, CompositeNode> modification) {
111         validateAgainstSchema(modification);
112         NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
113         cleanedUp.status = TransactionStatus.SUBMITED;
114         return retrieveDelegate().requestCommit(cleanedUp);
115     }
116
117     public boolean isValidationEnabled() {
118         return validationEnabled;
119     }
120
121     public void setValidationEnabled(final boolean validationEnabled) {
122         this.validationEnabled = validationEnabled;
123     }
124
125     private void validateAgainstSchema(final DataModification<YangInstanceIdentifier, CompositeNode> modification) {
126         if (!validationEnabled) {
127             return;
128         }
129
130         if (schema == null) {
131             LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
132             return;
133         }
134     }
135
136     @Override
137     protected void onDelegateChanged(final DataStore oldDelegate, final DataStore newDelegate) {
138         // NOOP
139     }
140
141     @Override
142     public void onGlobalContextUpdated(final SchemaContext context) {
143         this.schema = context;
144     }
145
146     @Override
147     public void close() throws Exception {
148         this.schema = null;
149     }
150
151     protected CompositeNode mergeData(final YangInstanceIdentifier path, final CompositeNode stored, final CompositeNode modified,
152             final boolean config) {
153         // long startTime = System.nanoTime();
154         try {
155             DataSchemaNode node = schemaNodeFor(path);
156             return YangDataOperations.merge(node, stored, modified, config);
157         } finally {
158             // System.out.println("Merge time: " + ((System.nanoTime() -
159             // startTime) / 1000.0d));
160         }
161     }
162
163     private DataSchemaNode schemaNodeFor(final YangInstanceIdentifier path) {
164         checkState(schema != null, "YANG Schema is not available");
165         return YangSchemaUtils.getSchemaNode(schema, path);
166     }
167
168     private NormalizedDataModification prepareMergedTransaction(
169             final DataModification<YangInstanceIdentifier, CompositeNode> original) {
170         NormalizedDataModification normalized = new NormalizedDataModification(original);
171         LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
172                 original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
173         LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
174                 original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
175         LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
176                 original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
177
178         for (YangInstanceIdentifier entry : original.getRemovedConfigurationData()) {
179             normalized.deepRemoveConfigurationData(entry);
180         }
181         for (YangInstanceIdentifier entry : original.getRemovedOperationalData()) {
182             normalized.deepRemoveOperationalData(entry);
183         }
184         for (Entry<YangInstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
185             normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
186         }
187         for (Entry<YangInstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
188             normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
189         }
190         return normalized;
191     }
192
193     private Iterable<YangInstanceIdentifier> getConfigurationSubpaths(final YangInstanceIdentifier entry) {
194         // FIXME: This should be replaced by index
195         Iterable<YangInstanceIdentifier> paths = getStoredConfigurationPaths();
196
197         return getChildrenPaths(entry, paths);
198
199     }
200
201     public Iterable<YangInstanceIdentifier> getOperationalSubpaths(final YangInstanceIdentifier entry) {
202         // FIXME: This should be indexed
203         Iterable<YangInstanceIdentifier> paths = getStoredOperationalPaths();
204
205         return getChildrenPaths(entry, paths);
206     }
207
208     private static final Iterable<YangInstanceIdentifier> getChildrenPaths(final YangInstanceIdentifier entry,
209             final Iterable<YangInstanceIdentifier> paths) {
210         ImmutableSet.Builder<YangInstanceIdentifier> children = ImmutableSet.builder();
211         for (YangInstanceIdentifier potential : paths) {
212             if (entry.contains(potential)) {
213                 children.add(entry);
214             }
215         }
216         return children.build();
217     }
218
219     private final Comparator<Entry<YangInstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<YangInstanceIdentifier, CompositeNode>>() {
220         @Override
221         public int compare(final Entry<YangInstanceIdentifier, CompositeNode> o1, final Entry<YangInstanceIdentifier, CompositeNode> o2) {
222             YangInstanceIdentifier o1Key = o1.getKey();
223             YangInstanceIdentifier o2Key = o2.getKey();
224             return Integer.compare(Iterables.size(o1Key.getPathArguments()), Iterables.size(o2Key.getPathArguments()));
225         }
226     };
227
228     private class MergeFirstLevelReader implements DataReader<YangInstanceIdentifier, CompositeNode> {
229
230         @Override
231         public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
232             getDelegateReadLock().lock();
233             try {
234                 if (Iterables.isEmpty(path.getPathArguments())) {
235                     return null;
236                 }
237                 QName qname = null;
238                 CompositeNode original = getDelegate().readConfigurationData(path);
239                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
240                 if (original != null) {
241                     childNodes.addAll(original.getValue());
242                     qname = original.getNodeType();
243                 } else {
244                     qname = path.getLastPathArgument().getNodeType();
245                 }
246
247                 FluentIterable<YangInstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
248                         .filter(new Predicate<YangInstanceIdentifier>() {
249                             @Override
250                             public boolean apply(final YangInstanceIdentifier input) {
251                                 if (path.contains(input)) {
252                                     int nesting = Iterables.size(input.getPathArguments()) - Iterables.size(path.getPathArguments());
253                                     if (nesting == 1) {
254                                         return true;
255                                     }
256                                 }
257                                 return false;
258                             }
259                         });
260                 for (YangInstanceIdentifier instanceIdentifier : directChildren) {
261                     childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
262                 }
263                 if (original == null && childNodes.isEmpty()) {
264                     return null;
265                 }
266
267                 return new CompositeNodeTOImpl(qname, null, childNodes);
268             } finally {
269                 getDelegateReadLock().unlock();
270             }
271         }
272
273         @Override
274         public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
275             getDelegateReadLock().lock();
276             try {
277                 if (Iterables.isEmpty(path.getPathArguments())) {
278                     return null;
279                 }
280                 QName qname = null;
281                 CompositeNode original = getDelegate().readOperationalData(path);
282                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
283                 if (original != null) {
284                     childNodes.addAll(original.getValue());
285                     qname = original.getNodeType();
286                 } else {
287                     qname = path.getLastPathArgument().getNodeType();
288                 }
289
290                 FluentIterable<YangInstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
291                         .filter(new Predicate<YangInstanceIdentifier>() {
292                             @Override
293                             public boolean apply(final YangInstanceIdentifier input) {
294                                 if (path.contains(input)) {
295                                     int nesting = Iterables.size(input.getPathArguments()) - Iterables.size(path.getPathArguments());
296                                     if (nesting == 1) {
297                                         return true;
298                                     }
299                                 }
300                                 return false;
301                             }
302                         });
303
304                 for (YangInstanceIdentifier instanceIdentifier : directChildren) {
305                     childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
306                 }
307                 if (original == null && childNodes.isEmpty()) {
308                     return null;
309                 }
310
311                 return new CompositeNodeTOImpl(qname, null, childNodes);
312             } finally {
313                 getDelegateReadLock().unlock();
314             }
315         }
316     }
317
318     private class NormalizedDataModification extends AbstractDataModification<YangInstanceIdentifier, CompositeNode> {
319
320         private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
321         private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
322         private final Object identifier;
323         private TransactionStatus status;
324
325         public NormalizedDataModification(final DataModification<YangInstanceIdentifier, CompositeNode> original) {
326             super(getDelegate());
327             identifier = original;
328             status = TransactionStatus.NEW;
329         }
330
331         /**
332          *
333          * Ensures all subpaths are removed - this currently does slow lookup in
334          * all keys.
335          *
336          * @param entry
337          */
338         public void deepRemoveOperationalData(final YangInstanceIdentifier entry) {
339             Iterable<YangInstanceIdentifier> paths = getOperationalSubpaths(entry);
340             removeOperationalData(entry);
341             for (YangInstanceIdentifier potential : paths) {
342                 removeOperationalData(potential);
343             }
344         }
345
346         public void deepRemoveConfigurationData(final YangInstanceIdentifier entry) {
347             Iterable<YangInstanceIdentifier> paths = getConfigurationSubpaths(entry);
348             removeConfigurationData(entry);
349             for (YangInstanceIdentifier potential : paths) {
350                 removeConfigurationData(potential);
351             }
352         }
353
354         public void putDeepConfigurationData(final YangInstanceIdentifier entryKey, final CompositeNode entryData) {
355             this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
356         }
357
358         public void putDeepOperationalData(final YangInstanceIdentifier entryKey, final CompositeNode entryData) {
359             this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
360         }
361
362         @Override
363         public Object getIdentifier() {
364             return this.identifier;
365         }
366
367         @Override
368         public TransactionStatus getStatus() {
369             return status;
370         }
371
372         @Override
373         public Future<RpcResult<TransactionStatus>> commit() {
374             throw new UnsupportedOperationException("Commit should not be invoked on this");
375         }
376
377         @Override
378         protected CompositeNode mergeConfigurationData(final YangInstanceIdentifier path, final CompositeNode stored,
379                 final CompositeNode modified) {
380             return mergeData(path, stored, modified, true);
381         }
382
383         @Override
384         protected CompositeNode mergeOperationalData(final YangInstanceIdentifier path, final CompositeNode stored,
385                 final CompositeNode modified) {
386             return mergeData(path, stored, modified, false);
387         }
388
389         private void putData(final YangInstanceIdentifier entryKey, final CompositeNode entryData, final String dataStoreIdentifier) {
390             if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
391                 switch (dataStoreIdentifier) {
392                 case (CONFIGURATIONAL_DATA_STORE_MARKER):
393                     this.putConfigurationData(entryKey, entryData);
394                 break;
395                 case (OPERATIONAL_DATA_STORE_MARKER):
396                     this.putOperationalData(entryKey, entryData);
397                 break;
398
399                 default:
400                     LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
401                     throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
402                 }
403             }
404         }
405
406         private void putCompositeNodeData(final YangInstanceIdentifier entryKey, final CompositeNode entryData,
407                 final String dataStoreIdentifier) {
408             this.putData(entryKey, entryData, dataStoreIdentifier);
409
410             for (Node<?> child : entryData.getValue()) {
411                 YangInstanceIdentifier subEntryId = YangInstanceIdentifier.builder(entryKey).node(child.getNodeType())
412                         .toInstance();
413                 if (child instanceof CompositeNode) {
414                     DataSchemaNode subSchema = schemaNodeFor(subEntryId);
415                     CompositeNode compNode = (CompositeNode) child;
416                     YangInstanceIdentifier instanceId = null;
417
418                     if (subSchema instanceof ListSchemaNode) {
419                         ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
420                         Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
421                                 (CompositeNode) child);
422                         if (mapOfSubValues != null) {
423                             instanceId = YangInstanceIdentifier.builder(entryKey)
424                                     .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
425                         }
426                     } else if (subSchema instanceof ContainerSchemaNode) {
427                         ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
428                         instanceId = YangInstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
429                     }
430                     if (instanceId != null) {
431                         this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
432                     }
433                 }
434             }
435         }
436
437         private Map<QName, Object> getValuesFromListSchema(final ListSchemaNode listSchema, final CompositeNode entryData) {
438             List<QName> keyDef = listSchema.getKeyDefinition();
439             if (keyDef != null && !keyDef.isEmpty()) {
440                 Map<QName, Object> map = new HashMap<QName, Object>();
441                 for (QName key : keyDef) {
442                     List<Node<?>> data = entryData.get(key);
443                     if (data != null && !data.isEmpty()) {
444                         for (Node<?> nodeData : data) {
445                             if (nodeData instanceof SimpleNode<?>) {
446                                 map.put(key, data.get(0).getValue());
447                             }
448                         }
449                     }
450                 }
451                 return map;
452             }
453             return null;
454         }
455     }
456 }