Bug 4957 No empty transaction for every connection fix
[openflowplugin.git] / applications / forwardingrules-manager / src / main / java / org / opendaylight / openflowplugin / applications / frm / impl / AbstractListeningCommiter.java
index a5f6dd80810fb0d9281ae1c84c7f94f7d8b3b0e0..3cc3bed6e79be1e387918a58f4e7ac9ac513d46b 100644 (file)
@@ -7,19 +7,19 @@
  */
 package org.opendaylight.openflowplugin.applications.frm.impl;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.openflowplugin.applications.frm.ForwardingRulesCommiter;
 import org.opendaylight.openflowplugin.applications.frm.ForwardingRulesManager;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
+import java.util.Collection;
 
 /**
  * AbstractChangeListner implemented basic {@link AsyncDataChangeEvent} processing for
@@ -30,6 +30,8 @@ import java.util.Set;
  */
 public abstract class AbstractListeningCommiter <T extends DataObject> implements ForwardingRulesCommiter<T> {
 
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractListeningCommiter.class);
+
     protected ForwardingRulesManager provider;
 
     protected final Class<T> clazz;
@@ -40,96 +42,82 @@ public abstract class AbstractListeningCommiter <T extends DataObject> implement
     }
 
     @Override
-    public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changeEvent) {
-        Preconditions.checkNotNull(changeEvent,"Async ChangeEvent can not be null!");
-
-        /* All DataObjects for create */
-        final Map<InstanceIdentifier<?>, DataObject> createdData = changeEvent.getCreatedData() != null
-                ? changeEvent.getCreatedData() : Collections.<InstanceIdentifier<?>, DataObject> emptyMap();
-        /* All DataObjects for remove */
-        final Set<InstanceIdentifier<?>> removeData = changeEvent.getRemovedPaths() != null
-                ? changeEvent.getRemovedPaths() : Collections.<InstanceIdentifier<?>> emptySet();
-        /* All DataObjects for updates */
-        final Map<InstanceIdentifier<?>, DataObject> updateData = changeEvent.getUpdatedData() != null
-                ? changeEvent.getUpdatedData() : Collections.<InstanceIdentifier<?>, DataObject> emptyMap();
-        /* All Original DataObjects */
-        final Map<InstanceIdentifier<?>, DataObject> originalData = changeEvent.getOriginalData() != null
-                ? changeEvent.getOriginalData() : Collections.<InstanceIdentifier<?>, DataObject> emptyMap();
-
-        this.createData(createdData);
-        this.updateData(updateData, originalData);
-        this.removeData(removeData, originalData);
-    }
-
-    /**
-     * Method return wildCardPath for Listener registration
-     * and for identify the correct KeyInstanceIdentifier from data;
-     */
-    protected abstract InstanceIdentifier<T> getWildCardPath();
-
-
-
-    @SuppressWarnings("unchecked")
-    private void createData(final Map<InstanceIdentifier<?>, DataObject> createdData) {
-        final Set<InstanceIdentifier<?>> keys = createdData.keySet() != null
-                ? createdData.keySet() : Collections.<InstanceIdentifier<?>> emptySet();
-        for (InstanceIdentifier<?> key : keys) {
-            if (clazz.equals(key.getTargetType())) {
-                final InstanceIdentifier<FlowCapableNode> nodeIdent =
-                        key.firstIdentifierOf(FlowCapableNode.class);
-                if (preConfigurationCheck(nodeIdent)) {
-                    InstanceIdentifier<T> createKeyIdent = key.firstIdentifierOf(clazz);
-                    final Optional<DataObject> value = Optional.of(createdData.get(key));
-                    if (value.isPresent()) {
-                        this.add(createKeyIdent, (T)value.get(), nodeIdent);
+    public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
+        Preconditions.checkNotNull(changes, "Changes may not be null!");
+
+        for (DataTreeModification<T> change : changes) {
+            final InstanceIdentifier<T> key = change.getRootPath().getRootIdentifier();
+            final DataObjectModification<T> mod = change.getRootNode();
+            final InstanceIdentifier<FlowCapableNode> nodeIdent =
+                    key.firstIdentifierOf(FlowCapableNode.class);
+
+            if (preConfigurationCheck(nodeIdent)) {
+                switch (mod.getModificationType()) {
+                case DELETE:
+                    remove(key, mod.getDataBefore(), nodeIdent);
+                    break;
+                case SUBTREE_MODIFIED:
+                    update(key, mod.getDataBefore(), mod.getDataAfter(), nodeIdent);
+                    break;
+                case WRITE:
+                    if (mod.getDataBefore() == null) {
+                        add(key, mod.getDataAfter(), nodeIdent);
+                    } else {
+                        update(key, mod.getDataBefore(), mod.getDataAfter(), nodeIdent);
                     }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
                 }
             }
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private void updateData(final Map<InstanceIdentifier<?>, DataObject> updateData,
-            final Map<InstanceIdentifier<?>, DataObject> originalData) {
-
-        final Set<InstanceIdentifier<?>> keys = updateData.keySet() != null
-                ? updateData.keySet() : Collections.<InstanceIdentifier<?>> emptySet();
-        for (InstanceIdentifier<?> key : keys) {
-            if (clazz.equals(key.getTargetType())) {
-                final InstanceIdentifier<FlowCapableNode> nodeIdent =
-                        key.firstIdentifierOf(FlowCapableNode.class);
-                if (preConfigurationCheck(nodeIdent)) {
-                    InstanceIdentifier<T> updateKeyIdent = key.firstIdentifierOf(clazz);
-                    final Optional<DataObject> value = Optional.of(updateData.get(key));
-                    final Optional<DataObject> original = Optional.of(originalData.get(key));
-                    if (value.isPresent() && original.isPresent()) {
-                        this.update(updateKeyIdent, (T)original.get(), (T)value.get(), nodeIdent);
+            else{
+                if (provider.getConfiguration().isStaleMarkingEnabled()) {
+                    LOG.info("Stale-Marking ENABLED and switch {} is NOT connected, storing stale entities",
+                            nodeIdent.toString());
+                    // Switch is NOT connected
+                    switch (mod.getModificationType()) {
+                        case DELETE:
+                            createStaleMarkEntity(key, mod.getDataBefore(), nodeIdent);
+                            break;
+                        case SUBTREE_MODIFIED:
+                            break;
+                        case WRITE:
+                            break;
+                        default:
+                            throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
                     }
                 }
             }
         }
     }
 
-    @SuppressWarnings("unchecked")
-    private void removeData(final Set<InstanceIdentifier<?>> removeData,
-            final Map<InstanceIdentifier<?>, DataObject> originalData) {
-
-        for (InstanceIdentifier<?> key : removeData) {
-            if (clazz.equals(key.getTargetType())) {
-                final InstanceIdentifier<FlowCapableNode> nodeIdent =
-                        key.firstIdentifierOf(FlowCapableNode.class);
-                if (preConfigurationCheck(nodeIdent)) {
-                    final InstanceIdentifier<T> ident = key.firstIdentifierOf(clazz);
-                    final DataObject removeValue = originalData.get(key);
-                    this.remove(ident, (T)removeValue, nodeIdent);
-                }
-            }
-        }
-    }
+    /**
+     * Method return wildCardPath for Listener registration
+     * and for identify the correct KeyInstanceIdentifier from data;
+     */
+    protected abstract InstanceIdentifier<T> getWildCardPath();
 
     private boolean preConfigurationCheck(final InstanceIdentifier<FlowCapableNode> nodeIdent) {
         Preconditions.checkNotNull(nodeIdent, "FlowCapableNode ident can not be null!");
-        return provider.isNodeActive(nodeIdent);
+        // In single node cluster, node should be in local cache before we get any flow/group/meter
+        // data change event from data store. So first check should pass.
+        // In case of 3-node cluster, when shard leader changes, clustering will send blob of data
+        // present in operational data store and config data store. So ideally local node cache
+        // should get populated. But to handle a scenario where flow request comes before the blob
+        // of config/operational data gets processes, it won't find node in local cache and it will
+        // skip the flow/group/meter operational. This requires an addition check, where it reads
+        // node from operational data store and if it's present it calls flowNodeConnected to explictly
+        // trigger the event of new node connected.
+
+        if (!provider.isNodeActive(nodeIdent)) {
+            if (provider.checkNodeInOperationalDataStore(nodeIdent)) {
+                provider.getFlowNodeReconciliation().flowNodeConnected(nodeIdent);
+                return true;
+            } else {
+                return false;
+            }
+        }
+        return true;
     }
 }