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.Map.Entry;
15 import java.util.concurrent.Future;
17 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
18 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
19 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
20 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
21 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
22 import org.opendaylight.controller.sal.core.api.data.DataStore;
23 import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
24 import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.RpcResult;
27 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.Node;
30 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33 import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import com.google.common.base.Predicate;
38 import com.google.common.collect.FluentIterable;
39 import com.google.common.collect.ImmutableSet;
41 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
43 SchemaServiceListener, //
46 private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
48 private SchemaContext schema = null;
49 private boolean validationEnabled = false;
50 private final DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
53 public boolean containsConfigurationPath(InstanceIdentifier path) {
55 getDelegateReadLock().lock();
56 return getDelegate().containsConfigurationPath(path);
59 getDelegateReadLock().unlock();
64 public boolean containsOperationalPath(InstanceIdentifier path) {
66 getDelegateReadLock().lock();
67 return getDelegate().containsOperationalPath(path);
70 getDelegateReadLock().unlock();
75 public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
77 getDelegateReadLock().lock();
78 return getDelegate().getStoredConfigurationPaths();
81 getDelegateReadLock().unlock();
86 public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
88 getDelegateReadLock().lock();
89 return getDelegate().getStoredOperationalPaths();
92 getDelegateReadLock().unlock();
97 public CompositeNode readConfigurationData(InstanceIdentifier path) {
98 return reader.readConfigurationData(path);
102 public CompositeNode readOperationalData(InstanceIdentifier path) {
103 return reader.readOperationalData(path);
107 public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
108 DataModification<InstanceIdentifier, CompositeNode> modification) {
109 validateAgainstSchema(modification);
110 NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
111 cleanedUp.status = TransactionStatus.SUBMITED;
112 return retrieveDelegate().requestCommit(cleanedUp);
115 public boolean isValidationEnabled() {
116 return validationEnabled;
119 public void setValidationEnabled(boolean validationEnabled) {
120 this.validationEnabled = validationEnabled;
123 private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
124 if (!validationEnabled) {
128 if (schema == null) {
129 LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
135 protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
140 public void onGlobalContextUpdated(SchemaContext context) {
141 this.schema = context;
145 public void close() throws Exception {
149 protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
151 long startTime = System.nanoTime();
153 DataSchemaNode node = schemaNodeFor(path);
154 return YangDataOperations.merge(node, stored, modified, config);
156 // System.out.println("Merge time: " + ((System.nanoTime() -
157 // startTime) / 1000.0d));
161 private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
162 checkState(schema != null, "YANG Schema is not available");
163 return YangSchemaUtils.getSchemaNode(schema, path);
166 private NormalizedDataModification prepareMergedTransaction(
167 DataModification<InstanceIdentifier, CompositeNode> original) {
168 NormalizedDataModification normalized = new NormalizedDataModification(original);
169 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
170 normalized.putConfigurationData(entry.getKey(), entry.getValue());
172 for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
173 normalized.putOperationalData(entry.getKey(), entry.getValue());
175 for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
176 normalized.deepRemoveConfigurationData(entry);
178 for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
179 normalized.deepRemoveOperationalData(entry);
184 private Iterable<InstanceIdentifier> getConfigurationSubpaths(InstanceIdentifier entry) {
185 // FIXME: This should be replaced by index
186 Iterable<InstanceIdentifier> paths = getStoredConfigurationPaths();
188 return getChildrenPaths(entry, paths);
192 public Iterable<InstanceIdentifier> getOperationalSubpaths(InstanceIdentifier entry) {
193 // FIXME: This should be indexed
194 Iterable<InstanceIdentifier> paths = getStoredOperationalPaths();
196 return getChildrenPaths(entry, paths);
199 private static final Iterable<InstanceIdentifier> getChildrenPaths(InstanceIdentifier entry,
200 Iterable<InstanceIdentifier> paths) {
201 ImmutableSet.Builder<InstanceIdentifier> children = ImmutableSet.builder();
202 for (InstanceIdentifier potential : paths) {
203 if (entry.contains(potential)) {
207 return children.build();
210 private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
212 public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
213 InstanceIdentifier o1Key = o1.getKey();
214 InstanceIdentifier o2Key = o2.getKey();
215 return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
219 private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
222 public CompositeNode readConfigurationData(final InstanceIdentifier path) {
223 getDelegateReadLock().lock();
225 if (path.getPath().isEmpty()) {
229 CompositeNode original = getDelegate().readConfigurationData(path);
230 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
231 if (original != null) {
232 childNodes.addAll(original.getChildren());
233 qname = original.getNodeType();
235 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
238 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
239 .filter(new Predicate<InstanceIdentifier>() {
241 public boolean apply(InstanceIdentifier input) {
242 if (path.contains(input)) {
243 int nesting = input.getPath().size() - path.getPath().size();
251 for (InstanceIdentifier instanceIdentifier : directChildren) {
252 childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
254 if (original == null && childNodes.isEmpty()) {
258 return new CompositeNodeTOImpl(qname, null, childNodes);
260 getDelegateReadLock().unlock();
265 public CompositeNode readOperationalData(final InstanceIdentifier path) {
266 getDelegateReadLock().lock();
268 if (path.getPath().isEmpty()) {
272 CompositeNode original = getDelegate().readOperationalData(path);
273 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
274 if (original != null) {
275 childNodes.addAll(original.getChildren());
276 qname = original.getNodeType();
278 qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
281 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
282 .filter(new Predicate<InstanceIdentifier>() {
284 public boolean apply(InstanceIdentifier input) {
285 if (path.contains(input)) {
286 int nesting = input.getPath().size() - path.getPath().size();
295 for (InstanceIdentifier instanceIdentifier : directChildren) {
296 childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
298 if (original == null && childNodes.isEmpty()) {
302 return new CompositeNodeTOImpl(qname, null, childNodes);
304 getDelegateReadLock().unlock();
309 private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
311 private final Object identifier;
312 private TransactionStatus status;
314 public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
315 super(getDelegate());
316 identifier = original;
317 status = TransactionStatus.NEW;
322 * Ensures all subpaths are removed - this currently does slow lookup in
327 public void deepRemoveOperationalData(InstanceIdentifier entry) {
328 Iterable<InstanceIdentifier> paths = getOperationalSubpaths(entry);
329 removeOperationalData(entry);
330 for (InstanceIdentifier potential : paths) {
331 removeOperationalData(potential);
335 public void deepRemoveConfigurationData(InstanceIdentifier entry) {
336 Iterable<InstanceIdentifier> paths = getConfigurationSubpaths(entry);
337 removeConfigurationData(entry);
338 for (InstanceIdentifier potential : paths) {
339 removeConfigurationData(potential);
344 public Object getIdentifier() {
345 return this.identifier;
349 public TransactionStatus getStatus() {
354 public Future<RpcResult<TransactionStatus>> commit() {
355 throw new UnsupportedOperationException("Commit should not be invoked on this");
359 protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
360 CompositeNode modified) {
361 return mergeData(path, stored, modified, true);
365 protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
366 CompositeNode modified) {
367 return mergeData(path, stored, modified, false);