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;
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;
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;
46 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements DataStore, SchemaContextListener, AutoCloseable {
48 private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
50 private SchemaContext schema = null;
51 private boolean validationEnabled = false;
52 private final DataReader<YangInstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
55 public boolean containsConfigurationPath(final YangInstanceIdentifier path) {
57 getDelegateReadLock().lock();
58 return getDelegate().containsConfigurationPath(path);
61 getDelegateReadLock().unlock();
66 public boolean containsOperationalPath(final YangInstanceIdentifier path) {
68 getDelegateReadLock().lock();
69 return getDelegate().containsOperationalPath(path);
72 getDelegateReadLock().unlock();
77 public Iterable<YangInstanceIdentifier> getStoredConfigurationPaths() {
79 getDelegateReadLock().lock();
80 return getDelegate().getStoredConfigurationPaths();
83 getDelegateReadLock().unlock();
88 public Iterable<YangInstanceIdentifier> getStoredOperationalPaths() {
90 getDelegateReadLock().lock();
91 return getDelegate().getStoredOperationalPaths();
94 getDelegateReadLock().unlock();
99 public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
100 return reader.readConfigurationData(path);
104 public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
105 return reader.readOperationalData(path);
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);
117 public boolean isValidationEnabled() {
118 return validationEnabled;
121 public void setValidationEnabled(final boolean validationEnabled) {
122 this.validationEnabled = validationEnabled;
125 private void validateAgainstSchema(final DataModification<YangInstanceIdentifier, CompositeNode> modification) {
126 if (!validationEnabled) {
130 if (schema == null) {
131 LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
137 protected void onDelegateChanged(final DataStore oldDelegate, final DataStore newDelegate) {
142 public void onGlobalContextUpdated(final SchemaContext context) {
143 this.schema = context;
147 public void close() throws Exception {
151 protected CompositeNode mergeData(final YangInstanceIdentifier path, final CompositeNode stored, final CompositeNode modified,
152 final boolean config) {
153 // long startTime = System.nanoTime();
155 DataSchemaNode node = schemaNodeFor(path);
156 return YangDataOperations.merge(node, stored, modified, config);
158 // System.out.println("Merge time: " + ((System.nanoTime() -
159 // startTime) / 1000.0d));
163 private DataSchemaNode schemaNodeFor(final YangInstanceIdentifier path) {
164 checkState(schema != null, "YANG Schema is not available");
165 return YangSchemaUtils.getSchemaNode(schema, path);
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());
178 for (YangInstanceIdentifier entry : original.getRemovedConfigurationData()) {
179 normalized.deepRemoveConfigurationData(entry);
181 for (YangInstanceIdentifier entry : original.getRemovedOperationalData()) {
182 normalized.deepRemoveOperationalData(entry);
184 for (Entry<YangInstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
185 normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
187 for (Entry<YangInstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
188 normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
193 private Iterable<YangInstanceIdentifier> getConfigurationSubpaths(final YangInstanceIdentifier entry) {
194 // FIXME: This should be replaced by index
195 Iterable<YangInstanceIdentifier> paths = getStoredConfigurationPaths();
197 return getChildrenPaths(entry, paths);
201 public Iterable<YangInstanceIdentifier> getOperationalSubpaths(final YangInstanceIdentifier entry) {
202 // FIXME: This should be indexed
203 Iterable<YangInstanceIdentifier> paths = getStoredOperationalPaths();
205 return getChildrenPaths(entry, paths);
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)) {
216 return children.build();
219 private final Comparator<Entry<YangInstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<YangInstanceIdentifier, CompositeNode>>() {
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()));
228 private class MergeFirstLevelReader implements DataReader<YangInstanceIdentifier, CompositeNode> {
231 public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
232 getDelegateReadLock().lock();
234 if (Iterables.isEmpty(path.getPathArguments())) {
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();
244 qname = path.getLastPathArgument().getNodeType();
247 FluentIterable<YangInstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
248 .filter(new Predicate<YangInstanceIdentifier>() {
250 public boolean apply(final YangInstanceIdentifier input) {
251 if (path.contains(input)) {
252 int nesting = Iterables.size(input.getPathArguments()) - Iterables.size(path.getPathArguments());
260 for (YangInstanceIdentifier instanceIdentifier : directChildren) {
261 childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
263 if (original == null && childNodes.isEmpty()) {
267 return new CompositeNodeTOImpl(qname, null, childNodes);
269 getDelegateReadLock().unlock();
274 public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
275 getDelegateReadLock().lock();
277 if (Iterables.isEmpty(path.getPathArguments())) {
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();
287 qname = path.getLastPathArgument().getNodeType();
290 FluentIterable<YangInstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
291 .filter(new Predicate<YangInstanceIdentifier>() {
293 public boolean apply(final YangInstanceIdentifier input) {
294 if (path.contains(input)) {
295 int nesting = Iterables.size(input.getPathArguments()) - Iterables.size(path.getPathArguments());
304 for (YangInstanceIdentifier instanceIdentifier : directChildren) {
305 childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
307 if (original == null && childNodes.isEmpty()) {
311 return new CompositeNodeTOImpl(qname, null, childNodes);
313 getDelegateReadLock().unlock();
318 private class NormalizedDataModification extends AbstractDataModification<YangInstanceIdentifier, CompositeNode> {
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;
325 public NormalizedDataModification(final DataModification<YangInstanceIdentifier, CompositeNode> original) {
326 super(getDelegate());
327 identifier = original;
328 status = TransactionStatus.NEW;
333 * Ensures all subpaths are removed - this currently does slow lookup in
338 public void deepRemoveOperationalData(final YangInstanceIdentifier entry) {
339 Iterable<YangInstanceIdentifier> paths = getOperationalSubpaths(entry);
340 removeOperationalData(entry);
341 for (YangInstanceIdentifier potential : paths) {
342 removeOperationalData(potential);
346 public void deepRemoveConfigurationData(final YangInstanceIdentifier entry) {
347 Iterable<YangInstanceIdentifier> paths = getConfigurationSubpaths(entry);
348 removeConfigurationData(entry);
349 for (YangInstanceIdentifier potential : paths) {
350 removeConfigurationData(potential);
354 public void putDeepConfigurationData(final YangInstanceIdentifier entryKey, final CompositeNode entryData) {
355 this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
358 public void putDeepOperationalData(final YangInstanceIdentifier entryKey, final CompositeNode entryData) {
359 this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
363 public Object getIdentifier() {
364 return this.identifier;
368 public TransactionStatus getStatus() {
373 public Future<RpcResult<TransactionStatus>> commit() {
374 throw new UnsupportedOperationException("Commit should not be invoked on this");
378 protected CompositeNode mergeConfigurationData(final YangInstanceIdentifier path, final CompositeNode stored,
379 final CompositeNode modified) {
380 return mergeData(path, stored, modified, true);
384 protected CompositeNode mergeOperationalData(final YangInstanceIdentifier path, final CompositeNode stored,
385 final CompositeNode modified) {
386 return mergeData(path, stored, modified, false);
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);
395 case (OPERATIONAL_DATA_STORE_MARKER):
396 this.putOperationalData(entryKey, entryData);
400 LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
401 throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
406 private void putCompositeNodeData(final YangInstanceIdentifier entryKey, final CompositeNode entryData,
407 final String dataStoreIdentifier) {
408 this.putData(entryKey, entryData, dataStoreIdentifier);
410 for (Node<?> child : entryData.getValue()) {
411 YangInstanceIdentifier subEntryId = YangInstanceIdentifier.builder(entryKey).node(child.getNodeType())
413 if (child instanceof CompositeNode) {
414 DataSchemaNode subSchema = schemaNodeFor(subEntryId);
415 CompositeNode compNode = (CompositeNode) child;
416 YangInstanceIdentifier instanceId = null;
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();
426 } else if (subSchema instanceof ContainerSchemaNode) {
427 ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
428 instanceId = YangInstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
430 if (instanceId != null) {
431 this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
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());