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.io.Console;
13 import java.util.ArrayList;
14 import java.util.Comparator;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
19 import java.util.Map.Entry;
21 import java.util.concurrent.Future;
23 import javax.activation.UnsupportedDataTypeException;
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.InstanceIdentifier.InstanceIdentifierBuilder;
38 import org.opendaylight.yangtools.yang.data.api.Node;
39 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
40 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
41 import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
42 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
47 import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
48 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 import com.google.common.base.Optional;
54 import com.google.common.base.Predicate;
55 import com.google.common.collect.FluentIterable;
56 import com.google.common.collect.ImmutableSet;
58 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
60 SchemaServiceListener, //
61 SchemaContextListener, //
64 private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
66 private SchemaContext schema = null;
67 private boolean validationEnabled = false;
68 private final DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
71 public boolean containsConfigurationPath(InstanceIdentifier path) {
73 getDelegateReadLock().lock();
74 return getDelegate().containsConfigurationPath(path);
77 getDelegateReadLock().unlock();
82 public boolean containsOperationalPath(InstanceIdentifier path) {
84 getDelegateReadLock().lock();
85 return getDelegate().containsOperationalPath(path);
88 getDelegateReadLock().unlock();
93 public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
95 getDelegateReadLock().lock();
96 return getDelegate().getStoredConfigurationPaths();
99 getDelegateReadLock().unlock();
104 public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
106 getDelegateReadLock().lock();
107 return getDelegate().getStoredOperationalPaths();
110 getDelegateReadLock().unlock();
115 public CompositeNode readConfigurationData(InstanceIdentifier path) {
116 return reader.readConfigurationData(path);
120 public CompositeNode readOperationalData(InstanceIdentifier path) {
121 return reader.readOperationalData(path);
125 public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
126 DataModification<InstanceIdentifier, CompositeNode> modification) {
127 validateAgainstSchema(modification);
128 NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
129 cleanedUp.status = TransactionStatus.SUBMITED;
130 return retrieveDelegate().requestCommit(cleanedUp);
133 public boolean isValidationEnabled() {
134 return validationEnabled;
137 public void setValidationEnabled(boolean validationEnabled) {
138 this.validationEnabled = validationEnabled;
141 private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
142 if (!validationEnabled) {
146 if (schema == null) {
147 LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
153 protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
158 public void onGlobalContextUpdated(SchemaContext context) {
159 this.schema = context;
163 public void close() throws Exception {
167 protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
169 // long startTime = System.nanoTime();
171 DataSchemaNode node = schemaNodeFor(path);
172 return YangDataOperations.merge(node, stored, modified, config);
174 // System.out.println("Merge time: " + ((System.nanoTime() -
175 // startTime) / 1000.0d));
179 private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
180 checkState(schema != null, "YANG Schema is not available");
181 return YangSchemaUtils.getSchemaNode(schema, path);
184 private NormalizedDataModification prepareMergedTransaction(
185 DataModification<InstanceIdentifier, CompositeNode> original) {
186 NormalizedDataModification normalized = new NormalizedDataModification(original);
187 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
188 normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
190 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
191 normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
193 for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
194 normalized.deepRemoveConfigurationData(entry);
196 for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
197 normalized.deepRemoveOperationalData(entry);
202 private Iterable<InstanceIdentifier> getConfigurationSubpaths(InstanceIdentifier entry) {
203 // FIXME: This should be replaced by index
204 Iterable<InstanceIdentifier> paths = getStoredConfigurationPaths();
206 return getChildrenPaths(entry, paths);
210 public Iterable<InstanceIdentifier> getOperationalSubpaths(InstanceIdentifier entry) {
211 // FIXME: This should be indexed
212 Iterable<InstanceIdentifier> paths = getStoredOperationalPaths();
214 return getChildrenPaths(entry, paths);
217 private static final Iterable<InstanceIdentifier> getChildrenPaths(InstanceIdentifier entry,
218 Iterable<InstanceIdentifier> paths) {
219 ImmutableSet.Builder<InstanceIdentifier> children = ImmutableSet.builder();
220 for (InstanceIdentifier potential : paths) {
221 if (entry.contains(potential)) {
225 return children.build();
228 private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
230 public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
231 InstanceIdentifier o1Key = o1.getKey();
232 InstanceIdentifier o2Key = o2.getKey();
233 return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
237 private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
240 public CompositeNode readConfigurationData(final InstanceIdentifier path) {
241 getDelegateReadLock().lock();
243 if (path.getPath().isEmpty()) {
247 CompositeNode original = getDelegate().readConfigurationData(path);
248 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
249 if (original != null) {
250 childNodes.addAll(original.getChildren());
251 qname = original.getNodeType();
253 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
256 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
257 .filter(new Predicate<InstanceIdentifier>() {
259 public boolean apply(InstanceIdentifier input) {
260 if (path.contains(input)) {
261 int nesting = input.getPath().size() - path.getPath().size();
269 for (InstanceIdentifier instanceIdentifier : directChildren) {
270 childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
272 if (original == null && childNodes.isEmpty()) {
276 return new CompositeNodeTOImpl(qname, null, childNodes);
278 getDelegateReadLock().unlock();
283 public CompositeNode readOperationalData(final InstanceIdentifier path) {
284 getDelegateReadLock().lock();
286 if (path.getPath().isEmpty()) {
290 CompositeNode original = getDelegate().readOperationalData(path);
291 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
292 if (original != null) {
293 childNodes.addAll(original.getChildren());
294 qname = original.getNodeType();
296 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
299 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
300 .filter(new Predicate<InstanceIdentifier>() {
302 public boolean apply(InstanceIdentifier input) {
303 if (path.contains(input)) {
304 int nesting = input.getPath().size() - path.getPath().size();
313 for (InstanceIdentifier instanceIdentifier : directChildren) {
314 childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
316 if (original == null && childNodes.isEmpty()) {
320 return new CompositeNodeTOImpl(qname, null, childNodes);
322 getDelegateReadLock().unlock();
327 private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
329 private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
330 private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
331 private final Object identifier;
332 private TransactionStatus status;
334 public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
335 super(getDelegate());
336 identifier = original;
337 status = TransactionStatus.NEW;
342 * Ensures all subpaths are removed - this currently does slow lookup in
347 public void deepRemoveOperationalData(InstanceIdentifier entry) {
348 Iterable<InstanceIdentifier> paths = getOperationalSubpaths(entry);
349 removeOperationalData(entry);
350 for (InstanceIdentifier potential : paths) {
351 removeOperationalData(potential);
355 public void deepRemoveConfigurationData(InstanceIdentifier entry) {
356 Iterable<InstanceIdentifier> paths = getConfigurationSubpaths(entry);
357 removeConfigurationData(entry);
358 for (InstanceIdentifier potential : paths) {
359 removeConfigurationData(potential);
363 public void putDeepConfigurationData(InstanceIdentifier entryKey, CompositeNode entryData) {
364 this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
367 public void putDeepOperationalData(InstanceIdentifier entryKey, CompositeNode entryData) {
368 this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
372 public Object getIdentifier() {
373 return this.identifier;
377 public TransactionStatus getStatus() {
382 public Future<RpcResult<TransactionStatus>> commit() {
383 throw new UnsupportedOperationException("Commit should not be invoked on this");
387 protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
388 CompositeNode modified) {
389 return mergeData(path, stored, modified, true);
393 protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
394 CompositeNode modified) {
395 return mergeData(path, stored, modified, false);
398 private void putData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
399 if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
400 switch (dataStoreIdentifier) {
401 case (CONFIGURATIONAL_DATA_STORE_MARKER):
402 this.putConfigurationData(entryKey, entryData);
404 case (OPERATIONAL_DATA_STORE_MARKER):
405 this.putOperationalData(entryKey, entryData);
409 LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
410 throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
415 private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
416 this.putData(entryKey, entryData, dataStoreIdentifier);
418 for (Node<?> child : entryData.getChildren()) {
419 InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType()).toInstance();
420 if (child instanceof CompositeNode) {
421 DataSchemaNode subSchema = schemaNodeFor(subEntryId);
422 CompositeNode compNode = (CompositeNode) child;
423 InstanceIdentifier instanceId = null;
425 if (subSchema instanceof ListSchemaNode) {
426 ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
427 Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema, (CompositeNode) child);
428 if (mapOfSubValues != null) {
429 instanceId = InstanceIdentifier.builder(entryKey).nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
432 else if (subSchema instanceof ContainerSchemaNode) {
433 ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
434 instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
436 if (instanceId != null) {
437 this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
443 private Map<QName, Object> getValuesFromListSchema (ListSchemaNode listSchema, CompositeNode entryData) {
444 List<QName> keyDef = listSchema.getKeyDefinition();
445 if (keyDef != null && ! keyDef.isEmpty()) {
446 Map<QName, Object> map = new HashMap<QName, Object>();
447 for (QName key : keyDef) {
448 List<Node<?>> data = entryData.get(key);
449 if (data != null && ! data.isEmpty()) {
450 for (Node<?> nodeData : data) {
451 if (nodeData instanceof SimpleNode<?>) {
452 map.put(key, data.get(0).getValue());