2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.dom.broker.impl;
10 import static com.google.common.base.Preconditions.checkState;
12 import java.util.ArrayList;
13 import java.util.Comparator;
14 import java.util.HashMap;
15 import java.util.List;
17 import java.util.Map.Entry;
18 import java.util.concurrent.Future;
20 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
21 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
22 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
23 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
24 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
25 import org.opendaylight.controller.sal.core.api.data.DataStore;
26 import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
27 import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.common.RpcResult;
30 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.Node;
33 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
34 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
35 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import com.google.common.base.Predicate;
44 import com.google.common.collect.FluentIterable;
45 import com.google.common.collect.ImmutableSet;
47 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
49 SchemaContextListener, //
52 private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
54 private SchemaContext schema = null;
55 private boolean validationEnabled = false;
56 private final DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
59 public boolean containsConfigurationPath(InstanceIdentifier path) {
61 getDelegateReadLock().lock();
62 return getDelegate().containsConfigurationPath(path);
65 getDelegateReadLock().unlock();
70 public boolean containsOperationalPath(InstanceIdentifier path) {
72 getDelegateReadLock().lock();
73 return getDelegate().containsOperationalPath(path);
76 getDelegateReadLock().unlock();
81 public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
83 getDelegateReadLock().lock();
84 return getDelegate().getStoredConfigurationPaths();
87 getDelegateReadLock().unlock();
92 public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
94 getDelegateReadLock().lock();
95 return getDelegate().getStoredOperationalPaths();
98 getDelegateReadLock().unlock();
103 public CompositeNode readConfigurationData(InstanceIdentifier path) {
104 return reader.readConfigurationData(path);
108 public CompositeNode readOperationalData(InstanceIdentifier path) {
109 return reader.readOperationalData(path);
113 public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
114 DataModification<InstanceIdentifier, CompositeNode> modification) {
115 validateAgainstSchema(modification);
116 NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
117 cleanedUp.status = TransactionStatus.SUBMITED;
118 return retrieveDelegate().requestCommit(cleanedUp);
121 public boolean isValidationEnabled() {
122 return validationEnabled;
125 public void setValidationEnabled(boolean validationEnabled) {
126 this.validationEnabled = validationEnabled;
129 private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
130 if (!validationEnabled) {
134 if (schema == null) {
135 LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
141 protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
146 public void onGlobalContextUpdated(SchemaContext context) {
147 this.schema = context;
151 public void close() throws Exception {
155 protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
157 // long startTime = System.nanoTime();
159 DataSchemaNode node = schemaNodeFor(path);
160 return YangDataOperations.merge(node, stored, modified, config);
162 // System.out.println("Merge time: " + ((System.nanoTime() -
163 // startTime) / 1000.0d));
167 private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
168 checkState(schema != null, "YANG Schema is not available");
169 return YangSchemaUtils.getSchemaNode(schema, path);
172 private NormalizedDataModification prepareMergedTransaction(
173 DataModification<InstanceIdentifier, CompositeNode> original) {
174 NormalizedDataModification normalized = new NormalizedDataModification(original);
175 LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
176 original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
177 LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
178 original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
179 LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
180 original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
182 for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
183 normalized.deepRemoveConfigurationData(entry);
185 for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
186 normalized.deepRemoveOperationalData(entry);
188 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
189 normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
191 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
192 normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
197 private Iterable<InstanceIdentifier> getConfigurationSubpaths(InstanceIdentifier entry) {
198 // FIXME: This should be replaced by index
199 Iterable<InstanceIdentifier> paths = getStoredConfigurationPaths();
201 return getChildrenPaths(entry, paths);
205 public Iterable<InstanceIdentifier> getOperationalSubpaths(InstanceIdentifier entry) {
206 // FIXME: This should be indexed
207 Iterable<InstanceIdentifier> paths = getStoredOperationalPaths();
209 return getChildrenPaths(entry, paths);
212 private static final Iterable<InstanceIdentifier> getChildrenPaths(InstanceIdentifier entry,
213 Iterable<InstanceIdentifier> paths) {
214 ImmutableSet.Builder<InstanceIdentifier> children = ImmutableSet.builder();
215 for (InstanceIdentifier potential : paths) {
216 if (entry.contains(potential)) {
220 return children.build();
223 private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
225 public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
226 InstanceIdentifier o1Key = o1.getKey();
227 InstanceIdentifier o2Key = o2.getKey();
228 return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
232 private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
235 public CompositeNode readConfigurationData(final InstanceIdentifier path) {
236 getDelegateReadLock().lock();
238 if (path.getPath().isEmpty()) {
242 CompositeNode original = getDelegate().readConfigurationData(path);
243 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
244 if (original != null) {
245 childNodes.addAll(original.getChildren());
246 qname = original.getNodeType();
248 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
251 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
252 .filter(new Predicate<InstanceIdentifier>() {
254 public boolean apply(InstanceIdentifier input) {
255 if (path.contains(input)) {
256 int nesting = input.getPath().size() - path.getPath().size();
264 for (InstanceIdentifier instanceIdentifier : directChildren) {
265 childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
267 if (original == null && childNodes.isEmpty()) {
271 return new CompositeNodeTOImpl(qname, null, childNodes);
273 getDelegateReadLock().unlock();
278 public CompositeNode readOperationalData(final InstanceIdentifier path) {
279 getDelegateReadLock().lock();
281 if (path.getPath().isEmpty()) {
285 CompositeNode original = getDelegate().readOperationalData(path);
286 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
287 if (original != null) {
288 childNodes.addAll(original.getChildren());
289 qname = original.getNodeType();
291 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
294 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
295 .filter(new Predicate<InstanceIdentifier>() {
297 public boolean apply(InstanceIdentifier input) {
298 if (path.contains(input)) {
299 int nesting = input.getPath().size() - path.getPath().size();
308 for (InstanceIdentifier instanceIdentifier : directChildren) {
309 childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
311 if (original == null && childNodes.isEmpty()) {
315 return new CompositeNodeTOImpl(qname, null, childNodes);
317 getDelegateReadLock().unlock();
322 private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
324 private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
325 private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
326 private final Object identifier;
327 private TransactionStatus status;
329 public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
330 super(getDelegate());
331 identifier = original;
332 status = TransactionStatus.NEW;
337 * Ensures all subpaths are removed - this currently does slow lookup in
342 public void deepRemoveOperationalData(InstanceIdentifier entry) {
343 Iterable<InstanceIdentifier> paths = getOperationalSubpaths(entry);
344 removeOperationalData(entry);
345 for (InstanceIdentifier potential : paths) {
346 removeOperationalData(potential);
350 public void deepRemoveConfigurationData(InstanceIdentifier entry) {
351 Iterable<InstanceIdentifier> paths = getConfigurationSubpaths(entry);
352 removeConfigurationData(entry);
353 for (InstanceIdentifier potential : paths) {
354 removeConfigurationData(potential);
358 public void putDeepConfigurationData(InstanceIdentifier entryKey, CompositeNode entryData) {
359 this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
362 public void putDeepOperationalData(InstanceIdentifier entryKey, CompositeNode entryData) {
363 this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
367 public Object getIdentifier() {
368 return this.identifier;
372 public TransactionStatus getStatus() {
377 public Future<RpcResult<TransactionStatus>> commit() {
378 throw new UnsupportedOperationException("Commit should not be invoked on this");
382 protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
383 CompositeNode modified) {
384 return mergeData(path, stored, modified, true);
388 protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
389 CompositeNode modified) {
390 return mergeData(path, stored, modified, false);
393 private void putData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
394 if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
395 switch (dataStoreIdentifier) {
396 case (CONFIGURATIONAL_DATA_STORE_MARKER):
397 this.putConfigurationData(entryKey, entryData);
399 case (OPERATIONAL_DATA_STORE_MARKER):
400 this.putOperationalData(entryKey, entryData);
404 LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
405 throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
410 private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData,
411 String dataStoreIdentifier) {
412 this.putData(entryKey, entryData, dataStoreIdentifier);
414 for (Node<?> child : entryData.getChildren()) {
415 InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType())
417 if (child instanceof CompositeNode) {
418 DataSchemaNode subSchema = schemaNodeFor(subEntryId);
419 CompositeNode compNode = (CompositeNode) child;
420 InstanceIdentifier instanceId = null;
422 if (subSchema instanceof ListSchemaNode) {
423 ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
424 Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
425 (CompositeNode) child);
426 if (mapOfSubValues != null) {
427 instanceId = InstanceIdentifier.builder(entryKey)
428 .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
430 } else if (subSchema instanceof ContainerSchemaNode) {
431 ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
432 instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
434 if (instanceId != null) {
435 this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
441 private Map<QName, Object> getValuesFromListSchema(ListSchemaNode listSchema, CompositeNode entryData) {
442 List<QName> keyDef = listSchema.getKeyDefinition();
443 if (keyDef != null && !keyDef.isEmpty()) {
444 Map<QName, Object> map = new HashMap<QName, Object>();
445 for (QName key : keyDef) {
446 List<Node<?>> data = entryData.get(key);
447 if (data != null && !data.isEmpty()) {
448 for (Node<?> nodeData : data) {
449 if (nodeData instanceof SimpleNode<?>) {
450 map.put(key, data.get(0).getValue());