Merge "Exception for URI /restconf/operations/module_name:rpc ended with slash"
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / impl / SchemaAwareDataStoreAdapter.java
index 4f4fadcc443a755809394998246fccfeb7845aa8..d8315568bee86ea07ee00b86feddcb0801faaa8e 100644 (file)
@@ -1,17 +1,21 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
 package org.opendaylight.controller.sal.dom.broker.impl;
 
-import java.awt.PageAttributes.OriginType;
+import static com.google.common.base.Preconditions.checkState;
+
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicLong;
 
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
@@ -19,40 +23,37 @@ import org.opendaylight.controller.md.sal.common.api.data.DataReader;
 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
 import org.opendaylight.controller.sal.core.api.data.DataStore;
-import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
+import org.opendaylight.controller.sal.dom.broker.util.YangDataOperations;
+import org.opendaylight.controller.sal.dom.broker.util.YangSchemaUtils;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.util.YangDataOperations;
-import org.opendaylight.yangtools.yang.util.YangSchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.FluentIterable;
-
-import static com.google.common.base.Preconditions.*;
-import org.opendaylight.yangtools.yang.util.YangDataOperations;
+import com.google.common.collect.ImmutableSet;
 
 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
         DataStore, //
-        SchemaServiceListener, //
+        SchemaContextListener, //
         AutoCloseable {
 
     private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
 
     private SchemaContext schema = null;
     private boolean validationEnabled = false;
-    private DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
+    private final DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
 
     @Override
     public boolean containsConfigurationPath(InstanceIdentifier path) {
@@ -131,7 +132,7 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataS
         }
 
         if (schema == null) {
-            LOG.info("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
+            LOG.warn("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
             return;
         }
     }
@@ -151,41 +152,74 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataS
         this.schema = null;
     }
 
-    protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified, boolean config) {
-        long startTime = System.nanoTime();
+    protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
+            boolean config) {
+        // long startTime = System.nanoTime();
         try {
-        DataSchemaNode node = schemaNodeFor(path);
-        return YangDataOperations.merge(node,stored,modified,config);
+            DataSchemaNode node = schemaNodeFor(path);
+            return YangDataOperations.merge(node, stored, modified, config);
         } finally {
-            //System.out.println("Merge time: " + ((System.nanoTime() - startTime) / 1000.0d));
+            // System.out.println("Merge time: " + ((System.nanoTime() -
+            // startTime) / 1000.0d));
         }
     }
-    
-    
+
     private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
-        checkState(schema != null,"YANG Schema is not available");
+        checkState(schema != null, "YANG Schema is not available");
         return YangSchemaUtils.getSchemaNode(schema, path);
     }
 
     private NormalizedDataModification prepareMergedTransaction(
             DataModification<InstanceIdentifier, CompositeNode> original) {
-        // NOOP for now
         NormalizedDataModification normalized = new NormalizedDataModification(original);
-        for (Entry<InstanceIdentifier,CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
-            normalized.putConfigurationData(entry.getKey(), entry.getValue());
+        LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
+                original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
+        LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
+                original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
+        LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
+                original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
+
+        for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
+            normalized.deepRemoveConfigurationData(entry);
         }
-        for (Entry<InstanceIdentifier,CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
-            normalized.putOperationalData(entry.getKey(), entry.getValue());
+        for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
+            normalized.deepRemoveOperationalData(entry);
         }
-        for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
-            normalized.removeConfigurationData(entry);
+        for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
+            normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
         }
-        for(InstanceIdentifier entry : original.getRemovedOperationalData()) {
-            normalized.removeOperationalData(entry);
+        for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
+            normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
         }
         return normalized;
     }
 
+    private Iterable<InstanceIdentifier> getConfigurationSubpaths(InstanceIdentifier entry) {
+        // FIXME: This should be replaced by index
+        Iterable<InstanceIdentifier> paths = getStoredConfigurationPaths();
+
+        return getChildrenPaths(entry, paths);
+
+    }
+
+    public Iterable<InstanceIdentifier> getOperationalSubpaths(InstanceIdentifier entry) {
+        // FIXME: This should be indexed
+        Iterable<InstanceIdentifier> paths = getStoredOperationalPaths();
+
+        return getChildrenPaths(entry, paths);
+    }
+
+    private static final Iterable<InstanceIdentifier> getChildrenPaths(InstanceIdentifier entry,
+            Iterable<InstanceIdentifier> paths) {
+        ImmutableSet.Builder<InstanceIdentifier> children = ImmutableSet.builder();
+        for (InstanceIdentifier potential : paths) {
+            if (entry.contains(potential)) {
+                children.add(entry);
+            }
+        }
+        return children.build();
+    }
+
     private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
         @Override
         public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
@@ -284,10 +318,12 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataS
             }
         }
     }
-    
+
     private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
 
-        private Object identifier;
+        private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
+        private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
+        private final Object identifier;
         private TransactionStatus status;
 
         public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
@@ -295,12 +331,43 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataS
             identifier = original;
             status = TransactionStatus.NEW;
         }
-        
+
+        /**
+         *
+         * Ensures all subpaths are removed - this currently does slow lookup in
+         * all keys.
+         *
+         * @param entry
+         */
+        public void deepRemoveOperationalData(InstanceIdentifier entry) {
+            Iterable<InstanceIdentifier> paths = getOperationalSubpaths(entry);
+            removeOperationalData(entry);
+            for (InstanceIdentifier potential : paths) {
+                removeOperationalData(potential);
+            }
+        }
+
+        public void deepRemoveConfigurationData(InstanceIdentifier entry) {
+            Iterable<InstanceIdentifier> paths = getConfigurationSubpaths(entry);
+            removeConfigurationData(entry);
+            for (InstanceIdentifier potential : paths) {
+                removeConfigurationData(potential);
+            }
+        }
+
+        public void putDeepConfigurationData(InstanceIdentifier entryKey, CompositeNode entryData) {
+            this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
+        }
+
+        public void putDeepOperationalData(InstanceIdentifier entryKey, CompositeNode entryData) {
+            this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
+        }
+
         @Override
         public Object getIdentifier() {
             return this.identifier;
         }
-        
+
         @Override
         public TransactionStatus getStatus() {
             return status;
@@ -310,18 +377,84 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataS
         public Future<RpcResult<TransactionStatus>> commit() {
             throw new UnsupportedOperationException("Commit should not be invoked on this");
         }
-        
+
         @Override
-        protected CompositeNode mergeConfigurationData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) {
-            return mergeData(path,stored, modified,true);
+        protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
+                CompositeNode modified) {
+            return mergeData(path, stored, modified, true);
         }
-        
+
         @Override
-        protected CompositeNode mergeOperationalData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) {
-            // TODO Auto-generated method stub
-            return mergeData(path,stored,modified,false);
+        protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
+                CompositeNode modified) {
+            return mergeData(path, stored, modified, false);
         }
-        
-    }
 
+        private void putData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
+            if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
+                switch (dataStoreIdentifier) {
+                case (CONFIGURATIONAL_DATA_STORE_MARKER):
+                    this.putConfigurationData(entryKey, entryData);
+                    break;
+                case (OPERATIONAL_DATA_STORE_MARKER):
+                    this.putOperationalData(entryKey, entryData);
+                    break;
+
+                default:
+                    LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
+                    throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
+                }
+            }
+        }
+
+        private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData,
+                String dataStoreIdentifier) {
+            this.putData(entryKey, entryData, dataStoreIdentifier);
+
+            for (Node<?> child : entryData.getChildren()) {
+                InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType())
+                        .toInstance();
+                if (child instanceof CompositeNode) {
+                    DataSchemaNode subSchema = schemaNodeFor(subEntryId);
+                    CompositeNode compNode = (CompositeNode) child;
+                    InstanceIdentifier instanceId = null;
+
+                    if (subSchema instanceof ListSchemaNode) {
+                        ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
+                        Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
+                                (CompositeNode) child);
+                        if (mapOfSubValues != null) {
+                            instanceId = InstanceIdentifier.builder(entryKey)
+                                    .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
+                        }
+                    } else if (subSchema instanceof ContainerSchemaNode) {
+                        ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
+                        instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
+                    }
+                    if (instanceId != null) {
+                        this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
+                    }
+                }
+            }
+        }
+
+        private Map<QName, Object> getValuesFromListSchema(ListSchemaNode listSchema, CompositeNode entryData) {
+            List<QName> keyDef = listSchema.getKeyDefinition();
+            if (keyDef != null && !keyDef.isEmpty()) {
+                Map<QName, Object> map = new HashMap<QName, Object>();
+                for (QName key : keyDef) {
+                    List<Node<?>> data = entryData.get(key);
+                    if (data != null && !data.isEmpty()) {
+                        for (Node<?> nodeData : data) {
+                            if (nodeData instanceof SimpleNode<?>) {
+                                map.put(key, data.get(0).getValue());
+                            }
+                        }
+                    }
+                }
+                return map;
+            }
+            return null;
+        }
+    }
 }