Merge "Bug 6110: Fixed bugs in statistics manager due to race condition." into stable...
[openflowplugin.git] / applications / statistics-manager / src / main / java / org / opendaylight / openflowplugin / applications / statistics / manager / impl / StatAbstractListenCommit.java
index 66f5660c7d162a0e90d92cfbed988945ab59f419..740f82378ced82b5b905e24d8a0d3d4b1fc0c196 100644 (file)
@@ -10,18 +10,17 @@ package org.opendaylight.openflowplugin.applications.statistics.manager.impl;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
 import org.opendaylight.openflowplugin.applications.statistics.manager.StatListeningCommiter;
+import org.opendaylight.openflowplugin.applications.statistics.manager.StatNodeRegistration;
 import org.opendaylight.openflowplugin.applications.statistics.manager.StatisticsManager;
+import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -30,62 +29,91 @@ import org.opendaylight.yangtools.yang.binding.NotificationListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * statistics-manager
  * org.opendaylight.openflowplugin.applications.statistics.manager.impl
  *
  * StatAbstractListeneningCommiter
- * Class is abstract implementation for all Configuration/DataStore DataChange
+ * Class is abstract implementation for all Configuration/DataStore DataTreeModification
  * listenable DataObjects like flows, groups, meters. It is a holder for common
- * functionality needed by construction/destruction class and for DataChange
+ * functionality needed by construction/destruction class and for DataTreeModification
  * event processing.
- *
  */
 public abstract class StatAbstractListenCommit<T extends DataObject, N extends NotificationListener>
                                             extends StatAbstractNotifyCommit<N> implements StatListeningCommiter<T,N> {
 
     private static final Logger LOG = LoggerFactory.getLogger(StatAbstractListenCommit.class);
 
-    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private ListenerRegistration<StatAbstractListenCommit<T, N>> listenerRegistration;
 
     protected final Map<InstanceIdentifier<Node>, Map<InstanceIdentifier<T>, Integer>> mapNodesForDelete = new ConcurrentHashMap<>();
     protected final Map<InstanceIdentifier<Node>, Integer> mapNodeFeautureRepeater = new ConcurrentHashMap<>();
+    protected final Map<InstanceIdentifier<Node>, ArrayList<T>> removedDataBetweenStatsCycle = new
+            ConcurrentHashMap<>();
 
     private final Class<T> clazz;
 
-    private final DataBroker dataBroker;
+    protected final DataBroker dataBroker;
+
+    protected final StatNodeRegistration nodeRegistrationManager;
 
     private ReadOnlyTransaction currentReadTx;
     private volatile boolean currentReadTxStale;
 
+    private static final int STARTUP_LOOP_TICK = 500;
+    private static final int STARTUP_LOOP_MAX_RETRIES = 8;
+
+    private final DataTreeIdentifier<T> treeId =
+            new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, getWildCardedRegistrationPath());
+
     /* Constructor has to make a registration */
     public StatAbstractListenCommit(final StatisticsManager manager, final DataBroker db,
-            final NotificationProviderService nps, final Class<T> clazz) {
-        super(manager,nps);
+            final NotificationProviderService nps, final Class<T> clazz, final StatNodeRegistration nodeRegistrationManager) {
+        super(manager,nps, nodeRegistrationManager);
         this.clazz = Preconditions.checkNotNull(clazz, "Referenced Class can not be null");
         Preconditions.checkArgument(db != null, "DataBroker can not be null!");
-        listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
-                getWildCardedRegistrationPath(), this, DataChangeScope.BASE);
         this.dataBroker = db;
+        this.nodeRegistrationManager = nodeRegistrationManager;
+
+        SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
+        try {
+            listenerRegistration =  looper.loopUntilNoException(new Callable<ListenerRegistration<StatAbstractListenCommit<T, N>>>() {
+                @Override
+                public ListenerRegistration<StatAbstractListenCommit<T, N>> call() throws Exception {
+                    return db.registerDataTreeChangeListener(treeId,StatAbstractListenCommit.this);
+                }
+            });
+        } catch (final Exception ex) {
+            LOG.debug(" StatAbstractListenCommit DataTreeChangeListener registration failed {}", ex.getMessage());
+            throw new IllegalStateException("Notification supplier startup fail! System needs restart.", ex);
+        }
     }
 
     /**
      * Method returns WildCarded Path which is used for registration as a listening path changes in
-     * {@link org.opendaylight.controller.md.sal.binding.api.DataChangeListener}
+     * {@link org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener}
      * @return
      */
     protected abstract InstanceIdentifier<T> getWildCardedRegistrationPath();
 
-    @Override
-    public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changeEvent) {
-        Preconditions.checkNotNull(changeEvent,"Async ChangeEvent can not be null!");
+    protected abstract void processDataChange(Collection<DataTreeModification<T>> changes);
 
+    @Override
+    public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
+        Preconditions.checkNotNull(changes, "Changes must not be null!");
         /*
          * If we have opened read transaction for configuration data store, we need to mark it as stale.
          *
          * Latest read transaction will be allocated on another read using readLatestConfiguration
          */
         currentReadTxStale = true;
+        processDataChange(changes);
     }
 
     @SuppressWarnings("unchecked")
@@ -107,6 +135,7 @@ public abstract class StatAbstractListenCommit<T extends DataObject, N extends N
     @Override
     public void cleanForDisconnect(final InstanceIdentifier<Node> nodeIdent) {
         mapNodesForDelete.remove(nodeIdent);
+        removedDataBetweenStatsCycle.remove(nodeIdent);
     }
 
     @Override
@@ -115,7 +144,7 @@ public abstract class StatAbstractListenCommit<T extends DataObject, N extends N
             try {
                 listenerRegistration.close();
             } catch (final Exception e) {
-                LOG.error("Error by stop {} DataChange StatListeningCommiter.", clazz.getSimpleName(), e);
+                LOG.error("Error by stop {} DataTreeChangeListener StatListeningCommiter.", clazz.getSimpleName(), e);
             }
             listenerRegistration = null;
         }
@@ -160,5 +189,6 @@ public abstract class StatAbstractListenCommit<T extends DataObject, N extends N
 
         return Optional.absent();
     }
+
 }