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