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 com.google.common.base.Predicate;
13 import com.google.common.collect.FluentIterable;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.collect.Iterables;
17 import java.util.ArrayList;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.Map.Entry;
23 import java.util.concurrent.Future;
25 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
26 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
27 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
28 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
29 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
30 import org.opendaylight.controller.sal.core.api.data.DataStore;
31 import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
32 import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.common.RpcResult;
35 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
36 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.Node;
38 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
39 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
40 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
50 SchemaContextListener, //
53 private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
55 private SchemaContext schema = null;
56 private boolean validationEnabled = false;
57 private final DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
60 public boolean containsConfigurationPath(final InstanceIdentifier path) {
62 getDelegateReadLock().lock();
63 return getDelegate().containsConfigurationPath(path);
66 getDelegateReadLock().unlock();
71 public boolean containsOperationalPath(final InstanceIdentifier path) {
73 getDelegateReadLock().lock();
74 return getDelegate().containsOperationalPath(path);
77 getDelegateReadLock().unlock();
82 public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
84 getDelegateReadLock().lock();
85 return getDelegate().getStoredConfigurationPaths();
88 getDelegateReadLock().unlock();
93 public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
95 getDelegateReadLock().lock();
96 return getDelegate().getStoredOperationalPaths();
99 getDelegateReadLock().unlock();
104 public CompositeNode readConfigurationData(final InstanceIdentifier path) {
105 return reader.readConfigurationData(path);
109 public CompositeNode readOperationalData(final InstanceIdentifier path) {
110 return reader.readOperationalData(path);
114 public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
115 final DataModification<InstanceIdentifier, CompositeNode> modification) {
116 validateAgainstSchema(modification);
117 NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
118 cleanedUp.status = TransactionStatus.SUBMITED;
119 return retrieveDelegate().requestCommit(cleanedUp);
122 public boolean isValidationEnabled() {
123 return validationEnabled;
126 public void setValidationEnabled(final boolean validationEnabled) {
127 this.validationEnabled = validationEnabled;
130 private void validateAgainstSchema(final DataModification<InstanceIdentifier, CompositeNode> modification) {
131 if (!validationEnabled) {
135 if (schema == null) {
136 LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
142 protected void onDelegateChanged(final DataStore oldDelegate, final DataStore newDelegate) {
147 public void onGlobalContextUpdated(final SchemaContext context) {
148 this.schema = context;
152 public void close() throws Exception {
156 protected CompositeNode mergeData(final InstanceIdentifier path, final CompositeNode stored, final CompositeNode modified,
157 final boolean config) {
158 // long startTime = System.nanoTime();
160 DataSchemaNode node = schemaNodeFor(path);
161 return YangDataOperations.merge(node, stored, modified, config);
163 // System.out.println("Merge time: " + ((System.nanoTime() -
164 // startTime) / 1000.0d));
168 private DataSchemaNode schemaNodeFor(final InstanceIdentifier path) {
169 checkState(schema != null, "YANG Schema is not available");
170 return YangSchemaUtils.getSchemaNode(schema, path);
173 private NormalizedDataModification prepareMergedTransaction(
174 final DataModification<InstanceIdentifier, CompositeNode> original) {
175 NormalizedDataModification normalized = new NormalizedDataModification(original);
176 LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
177 original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
178 LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
179 original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
180 LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
181 original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
183 for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
184 normalized.deepRemoveConfigurationData(entry);
186 for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
187 normalized.deepRemoveOperationalData(entry);
189 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
190 normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
192 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
193 normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
198 private Iterable<InstanceIdentifier> getConfigurationSubpaths(final InstanceIdentifier entry) {
199 // FIXME: This should be replaced by index
200 Iterable<InstanceIdentifier> paths = getStoredConfigurationPaths();
202 return getChildrenPaths(entry, paths);
206 public Iterable<InstanceIdentifier> getOperationalSubpaths(final InstanceIdentifier entry) {
207 // FIXME: This should be indexed
208 Iterable<InstanceIdentifier> paths = getStoredOperationalPaths();
210 return getChildrenPaths(entry, paths);
213 private static final Iterable<InstanceIdentifier> getChildrenPaths(final InstanceIdentifier entry,
214 final Iterable<InstanceIdentifier> paths) {
215 ImmutableSet.Builder<InstanceIdentifier> children = ImmutableSet.builder();
216 for (InstanceIdentifier potential : paths) {
217 if (entry.contains(potential)) {
221 return children.build();
224 private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
226 public int compare(final Entry<InstanceIdentifier, CompositeNode> o1, final Entry<InstanceIdentifier, CompositeNode> o2) {
227 InstanceIdentifier o1Key = o1.getKey();
228 InstanceIdentifier o2Key = o2.getKey();
229 return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
233 private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
236 public CompositeNode readConfigurationData(final InstanceIdentifier path) {
237 getDelegateReadLock().lock();
239 if (Iterables.isEmpty(path.getPathArguments())) {
243 CompositeNode original = getDelegate().readConfigurationData(path);
244 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
245 if (original != null) {
246 childNodes.addAll(original.getValue());
247 qname = original.getNodeType();
249 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
252 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
253 .filter(new Predicate<InstanceIdentifier>() {
255 public boolean apply(final InstanceIdentifier input) {
256 if (path.contains(input)) {
257 int nesting = input.getPath().size() - path.getPath().size();
265 for (InstanceIdentifier instanceIdentifier : directChildren) {
266 childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
268 if (original == null && childNodes.isEmpty()) {
272 return new CompositeNodeTOImpl(qname, null, childNodes);
274 getDelegateReadLock().unlock();
279 public CompositeNode readOperationalData(final InstanceIdentifier path) {
280 getDelegateReadLock().lock();
282 if (Iterables.isEmpty(path.getPathArguments())) {
286 CompositeNode original = getDelegate().readOperationalData(path);
287 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
288 if (original != null) {
289 childNodes.addAll(original.getValue());
290 qname = original.getNodeType();
292 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
295 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
296 .filter(new Predicate<InstanceIdentifier>() {
298 public boolean apply(final InstanceIdentifier input) {
299 if (path.contains(input)) {
300 int nesting = input.getPath().size() - path.getPath().size();
309 for (InstanceIdentifier instanceIdentifier : directChildren) {
310 childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
312 if (original == null && childNodes.isEmpty()) {
316 return new CompositeNodeTOImpl(qname, null, childNodes);
318 getDelegateReadLock().unlock();
323 private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
325 private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
326 private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
327 private final Object identifier;
328 private TransactionStatus status;
330 public NormalizedDataModification(final DataModification<InstanceIdentifier, CompositeNode> original) {
331 super(getDelegate());
332 identifier = original;
333 status = TransactionStatus.NEW;
338 * Ensures all subpaths are removed - this currently does slow lookup in
343 public void deepRemoveOperationalData(final InstanceIdentifier entry) {
344 Iterable<InstanceIdentifier> paths = getOperationalSubpaths(entry);
345 removeOperationalData(entry);
346 for (InstanceIdentifier potential : paths) {
347 removeOperationalData(potential);
351 public void deepRemoveConfigurationData(final InstanceIdentifier entry) {
352 Iterable<InstanceIdentifier> paths = getConfigurationSubpaths(entry);
353 removeConfigurationData(entry);
354 for (InstanceIdentifier potential : paths) {
355 removeConfigurationData(potential);
359 public void putDeepConfigurationData(final InstanceIdentifier entryKey, final CompositeNode entryData) {
360 this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
363 public void putDeepOperationalData(final InstanceIdentifier entryKey, final CompositeNode entryData) {
364 this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
368 public Object getIdentifier() {
369 return this.identifier;
373 public TransactionStatus getStatus() {
378 public Future<RpcResult<TransactionStatus>> commit() {
379 throw new UnsupportedOperationException("Commit should not be invoked on this");
383 protected CompositeNode mergeConfigurationData(final InstanceIdentifier path, final CompositeNode stored,
384 final CompositeNode modified) {
385 return mergeData(path, stored, modified, true);
389 protected CompositeNode mergeOperationalData(final InstanceIdentifier path, final CompositeNode stored,
390 final CompositeNode modified) {
391 return mergeData(path, stored, modified, false);
394 private void putData(final InstanceIdentifier entryKey, final CompositeNode entryData, final String dataStoreIdentifier) {
395 if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
396 switch (dataStoreIdentifier) {
397 case (CONFIGURATIONAL_DATA_STORE_MARKER):
398 this.putConfigurationData(entryKey, entryData);
400 case (OPERATIONAL_DATA_STORE_MARKER):
401 this.putOperationalData(entryKey, entryData);
405 LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
406 throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
411 private void putCompositeNodeData(final InstanceIdentifier entryKey, final CompositeNode entryData,
412 final String dataStoreIdentifier) {
413 this.putData(entryKey, entryData, dataStoreIdentifier);
415 for (Node<?> child : entryData.getValue()) {
416 InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType())
418 if (child instanceof CompositeNode) {
419 DataSchemaNode subSchema = schemaNodeFor(subEntryId);
420 CompositeNode compNode = (CompositeNode) child;
421 InstanceIdentifier instanceId = null;
423 if (subSchema instanceof ListSchemaNode) {
424 ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
425 Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
426 (CompositeNode) child);
427 if (mapOfSubValues != null) {
428 instanceId = InstanceIdentifier.builder(entryKey)
429 .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
431 } else if (subSchema instanceof ContainerSchemaNode) {
432 ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
433 instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
435 if (instanceId != null) {
436 this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
442 private Map<QName, Object> getValuesFromListSchema(final ListSchemaNode listSchema, final CompositeNode entryData) {
443 List<QName> keyDef = listSchema.getKeyDefinition();
444 if (keyDef != null && !keyDef.isEmpty()) {
445 Map<QName, Object> map = new HashMap<QName, Object>();
446 for (QName key : keyDef) {
447 List<Node<?>> data = entryData.get(key);
448 if (data != null && !data.isEmpty()) {
449 for (Node<?> nodeData : data) {
450 if (nodeData instanceof SimpleNode<?>) {
451 map.put(key, data.get(0).getValue());