Refactor persister: require only capabilities referenced by the xml snapshot.
[controller.git] / opendaylight / netconf / config-persister-impl / src / main / java / org / opendaylight / controller / netconf / persist / impl / CapabilityStrippingConfigSnapshotHolder.java
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java
new file mode 100644 (file)
index 0000000..0440cbd
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ * @author Tomas Olvecky
+ *
+ * 11 2013
+ *
+ * Copyright (c) 2013 by Cisco Systems, Inc.
+ * All rights reserved.
+ */
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.opendaylight.controller.config.persist.api.Persister.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder {
+    private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class);
+
+    private final String configSnapshot;
+    private final StripCapabilitiesResult stripCapabilitiesResult;
+
+    public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities, Pattern ignoredMissingCapabilityRegex) {
+        final XmlElement configElement = XmlElement.fromDomElement(snapshot);
+        configSnapshot = XmlUtil.toString(configElement.getDomElement());
+        stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex);
+    }
+
+    private static class StripCapabilitiesResult {
+        private final SortedSet<String> requiredCapabilities, missingNamespaces;
+
+        private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> missingNamespaces) {
+            this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities);
+            this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces);
+        }
+    }
+
+
+    @VisibleForTesting
+    static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello,
+                                                     Pattern ignoredMissingCapabilityRegex) {
+        // collect all namespaces
+        Set<String> foundNamespacesInXML = getNamespaces(configElement);
+        logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML);
+        // required are referenced both in xml and hello
+        SortedSet<String> requiredCapabilities = new TreeSet<>();
+        // can be removed
+        Set<String> obsoleteCapabilities = new HashSet<>();
+        // are in xml but not in hello
+        SortedSet<String> missingNamespaces = new TreeSet<>(foundNamespacesInXML);
+        for (String capability : allCapabilitiesFromHello) {
+            String namespace = capability.replaceAll("\\?.*","");
+            if (foundNamespacesInXML.contains(namespace)) {
+                requiredCapabilities.add(capability);
+                checkState(missingNamespaces.remove(namespace));
+            } else {
+                obsoleteCapabilities.add(capability);
+            }
+        }
+
+        logger.trace("Required capabilities {}, \nObsolete capabilities {}",
+                requiredCapabilities, obsoleteCapabilities);
+
+        for(Iterator<String> iterator = missingNamespaces.iterator();iterator.hasNext(); ){
+            String capability = iterator.next();
+            if (ignoredMissingCapabilityRegex.matcher(capability).matches()){
+                logger.trace("Ignoring missing capability {}", capability);
+                iterator.remove();
+            }
+        }
+        if (missingNamespaces.size() > 0) {
+            logger.warn("Some capabilities are missing: {}", missingNamespaces);
+        }
+        return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces);
+    }
+
+    static Set<String> getNamespaces(XmlElement element){
+        Set<String> result = new HashSet<>();
+        for (Entry<String,Attr> attribute : element.getAttributes().entrySet()) {
+            if  (attribute.getKey().startsWith("xmlns")){
+                result.add(attribute.getValue().getValue());
+            }
+        }
+        //element.getAttributes()
+        for(XmlElement child: element.getChildElements()) {
+            result.addAll(getNamespaces(child));
+        }
+        return result;
+    }
+
+    @Override
+    public SortedSet<String> getCapabilities() {
+        return stripCapabilitiesResult.requiredCapabilities;
+    }
+
+    @VisibleForTesting
+    Set<String> getMissingNamespaces(){
+        return stripCapabilitiesResult.missingNamespaces;
+    }
+
+    @Override
+    public String getConfigSnapshot() {
+        return configSnapshot;
+    }
+}