Bug 3557 - Li -ofp when device reconnects, it has to wait for previous transaction... 98/22098/1
authorMartin Bobak <mbobak@cisco.com>
Wed, 3 Jun 2015 06:52:49 +0000 (08:52 +0200)
committermichal rehak <mirehak@cisco.com>
Mon, 8 Jun 2015 12:58:32 +0000 (12:58 +0000)
TransactionChain should be checked for its lifecycle status when device reconnects

Change-Id: I272da2eae4d81802c0dc3e70308989daa6dd8162
Signed-off-by: Martin Bobak <mbobak@cisco.com>
(cherry picked from commit 8fd4238633295a741f65e2e82a883dfb401bb5b3)

openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceStateImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceTransactionChainManagerProvider.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/ReadyForNewTransactionChainHandler.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/TransactionChainManager.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/DeviceStateUtil.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/TransactionChainManagerTest.java

index e2802fa86711f43491253de2a558a49881b02b4a..9b06829957177129aceca6491f4c64741614f65a 100644 (file)
@@ -96,7 +96,7 @@ public class DeviceContextImpl implements DeviceContext {
     private final DataBroker dataBroker;
     private final HashedWheelTimer hashedWheelTimer;
     private final Map<SwitchConnectionDistinguisher, ConnectionContext> auxiliaryConnectionContexts;
-    private final TransactionChainManager txChainManager;
+    private final TransactionChainManager transactionChainManager;
     private final DeviceFlowRegistry deviceFlowRegistry;
     private final DeviceGroupRegistry deviceGroupRegistry;
     private final DeviceMeterRegistry deviceMeterRegistry;
@@ -121,13 +121,14 @@ public class DeviceContextImpl implements DeviceContext {
                       @Nonnull final HashedWheelTimer hashedWheelTimer,
                       @Nonnull final MessageSpy _messageSpy,
                       @Nonnull final OutboundQueueProvider outboundQueueProvider,
-                      @Nonnull final TranslatorLibrary translatorLibrary) {
+                      @Nonnull final TranslatorLibrary translatorLibrary,
+                      @Nonnull final TransactionChainManager transactionChainManager) {
         this.primaryConnectionContext = Preconditions.checkNotNull(primaryConnectionContext);
         this.deviceState = Preconditions.checkNotNull(deviceState);
         this.dataBroker = Preconditions.checkNotNull(dataBroker);
         this.hashedWheelTimer = Preconditions.checkNotNull(hashedWheelTimer);
         this.outboundQueueProvider = Preconditions.checkNotNull(outboundQueueProvider);
-        txChainManager = new TransactionChainManager(dataBroker, deviceState);
+        this.transactionChainManager = Preconditions.checkNotNull(transactionChainManager);
         auxiliaryConnectionContexts = new HashMap<>();
         deviceFlowRegistry = new DeviceFlowRegistryImpl();
         deviceGroupRegistry = new DeviceGroupRegistryImpl();
@@ -150,7 +151,7 @@ public class DeviceContextImpl implements DeviceContext {
      * and we are able to set a scheduler for an automatic transaction submitting by time (0,5sec).
      */
     void initialSubmitTransaction() {
-        txChainManager.initialSubmitWriteTransaction();
+        transactionChainManager.initialSubmitWriteTransaction();
     }
 
     @Override
@@ -191,17 +192,17 @@ public class DeviceContextImpl implements DeviceContext {
     @Override
     public <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
                                                           final InstanceIdentifier<T> path, final T data) {
-        txChainManager.writeToTransaction(store, path, data);
+        transactionChainManager.writeToTransaction(store, path, data);
     }
 
     @Override
     public <T extends DataObject> void addDeleteToTxChain(final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
-        txChainManager.addDeleteOperationTotTxChain(store, path);
+        transactionChainManager.addDeleteOperationTotTxChain(store, path);
     }
 
     @Override
     public boolean submitTransaction() {
-        return txChainManager.submitWriteTransaction();
+        return transactionChainManager.submitWriteTransaction();
     }
 
     @Override
@@ -347,7 +348,7 @@ public class DeviceContextImpl implements DeviceContext {
             deviceContextClosedHandler.onDeviceContextClosed(this);
         }
 
-        txChainManager.close();
+        transactionChainManager.close();
     }
 
     @Override
index 4c776c5d41e020543f2dc94df72e4279f58196f5..281ba53df91a7b4ad318188bcd2dd0d1521eb9c1 100644 (file)
@@ -64,6 +64,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev13
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsDataBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeatures;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
@@ -101,7 +102,7 @@ import org.slf4j.LoggerFactory;
 /**
  *
  */
-public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
+public class DeviceManagerImpl implements DeviceManager, AutoCloseable, ReadyForNewTransactionChainHandler {
 
     private static final Logger LOG = LoggerFactory.getLogger(DeviceManagerImpl.class);
 
@@ -122,6 +123,7 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
     private final long barrierNanos = TimeUnit.MILLISECONDS.toNanos(500);
     private final int maxQueueDepth = 25600;
     private final boolean switchFeaturesMandatory;
+    private final DeviceTransactionChainManagerProvider deviceTransactionChainManagerProvider;
 
     public DeviceManagerImpl(@Nonnull final DataBroker dataBroker,
                              @Nonnull final MessageIntelligenceAgency messageIntelligenceAgency,
@@ -143,6 +145,7 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
 
         this.messageIntelligenceAgency = messageIntelligenceAgency;
         this.switchFeaturesMandatory = switchFeaturesMandatory;
+        deviceTransactionChainManagerProvider = new DeviceTransactionChainManagerProvider();
     }
 
 
@@ -173,6 +176,12 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
     public void deviceConnected(@CheckForNull final ConnectionContext connectionContext) {
         Preconditions.checkArgument(connectionContext != null);
 
+        TransactionChainManager transactionChainManager = deviceTransactionChainManagerProvider.provideTransactionChainManagerOrWaitForNotification(connectionContext, dataBroker, this);
+        initializeDeviceContextSafely(connectionContext, transactionChainManager);
+    }
+
+    private void initializeDeviceContext(final ConnectionContext connectionContext, final TransactionChainManager transactionChainManager) {
+
         // Cache this for clarity
         final ConnectionAdapter connectionAdapter = connectionContext.getConnectionAdapter();
 
@@ -187,10 +196,11 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
                 connectionAdapter.registerOutboundQueueHandler(outboundQueueProvider, maxQueueDepth, barrierNanos);
         connectionContext.setOutboundQueueHandleRegistration(outboundQueueHandlerRegistration);
 
-        final DeviceState deviceState = new DeviceStateImpl(connectionContext.getFeatures(), connectionContext.getNodeId());
+        final NodeId nodeId = connectionContext.getNodeId();
+        final DeviceState deviceState = new DeviceStateImpl(connectionContext.getFeatures(), nodeId);
 
         final DeviceContext deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker,
-                hashedWheelTimer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary);
+                hashedWheelTimer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, transactionChainManager);
         deviceContext.setNotificationService(notificationService);
         deviceContext.setNotificationPublishService(notificationPublishService);
         final NodeBuilder nodeBuilder = new NodeBuilder().setId(deviceState.getNodeId()).setNodeConnector(Collections.<NodeConnector>emptyList());
@@ -589,4 +599,16 @@ public class DeviceManagerImpl implements DeviceManager, AutoCloseable {
         spyPool = new ScheduledThreadPoolExecutor(1);
         spyPool.scheduleAtFixedRate(messageIntelligenceAgency, spyRate, spyRate, TimeUnit.SECONDS);
     }
+
+    @Override
+    public void onReadyForNewTransactionChain(final ConnectionContext connectionContext) {
+        TransactionChainManager transactionChainManager = deviceTransactionChainManagerProvider.provideTransactionChainManagerOrWaitForNotification(connectionContext, dataBroker, this);
+        initializeDeviceContextSafely(connectionContext, transactionChainManager);
+    }
+
+    private void initializeDeviceContextSafely(final ConnectionContext connectionContext, final TransactionChainManager transactionChainManager) {
+        if (null != transactionChainManager) {
+            initializeDeviceContext(connectionContext, transactionChainManager);
+        }
+    }
 }
index bc9dafd75491d2cf9b41b891f2ff3e9bdcfbc28f..28803566206472d8a058b730e75c7b4ad1115444 100644 (file)
@@ -12,14 +12,13 @@ import com.google.common.base.Preconditions;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutputBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 /**
@@ -52,8 +51,7 @@ class DeviceStateImpl implements DeviceState {
         Preconditions.checkArgument(featuresReply != null);
         featuresOutput = new GetFeaturesOutputBuilder(featuresReply).build();
         this.nodeId = Preconditions.checkNotNull(nodeId);
-        // FIXME: use builder, as we will be using this identifier often
-        nodeII = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId));
+        nodeII = DeviceStateUtil.createNodeInstanceIdentifier(nodeId);
         version = featuresReply.getVersion();
     }
 
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceTransactionChainManagerProvider.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceTransactionChainManagerProvider.java
new file mode 100644 (file)
index 0000000..3e6df96
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 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.openflowplugin.impl.device;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by Martin Bobak &lt;mbobak@cisco.com&gt; on 2.6.2015.
+ */
+public class DeviceTransactionChainManagerProvider {
+
+
+    private static final Logger LOG = LoggerFactory.getLogger(DeviceTransactionChainManagerProvider.class);
+    private static final Map<NodeId, TransactionChainManager> txChManagers = new HashMap<>();
+
+    public TransactionChainManager provideTransactionChainManagerOrWaitForNotification(final ConnectionContext connectionContext,
+                                                                                       final DataBroker dataBroker,
+                                                                                       final ReadyForNewTransactionChainHandler readyForNewTransactionChainHandler) {
+        final NodeId nodeId = connectionContext.getNodeId();
+        synchronized (this) {
+            TransactionChainManager transactionChainManager = txChManagers.get(nodeId);
+            if (null == transactionChainManager) {
+                LOG.info("Creating new transaction chain for device {}", nodeId.toString());
+                Registration registration = new Registration() {
+                    @Override
+                    public void close() throws Exception {
+                        txChManagers.remove(nodeId);
+                    }
+                };
+                transactionChainManager = new TransactionChainManager(dataBroker, connectionContext, registration);
+                txChManagers.put(nodeId, transactionChainManager);
+                return transactionChainManager;
+            } else {
+                LOG.info("Device {} waits for previous connection's transaction chain to be closed.", nodeId.toString());
+                try {
+                    if (!transactionChainManager.attemptToRegisterHandler(readyForNewTransactionChainHandler)) {
+                        LOG.info("There already exists one handler for connection described as {}. Will try again.", nodeId);
+                        readyForNewTransactionChainHandler.onReadyForNewTransactionChain(connectionContext);
+                    }
+                } catch (Exception e) {
+                    LOG.info("Transaction closed handler registration for node {} failed because we most probably hit previous transaction chain  manager's close process. Will try again.", nodeId);
+                    readyForNewTransactionChainHandler.onReadyForNewTransactionChain(connectionContext);
+                }
+            }
+        }
+        return null;
+    }
+
+
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/ReadyForNewTransactionChainHandler.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/ReadyForNewTransactionChainHandler.java
new file mode 100644 (file)
index 0000000..58b22b5
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.openflowplugin.impl.device;
+
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+
+/**
+ * Created by Martin Bobak &lt;mbobak@cisco.com&gt; on 2.6.2015.
+ */
+public interface ReadyForNewTransactionChainHandler {
+
+    void onReadyForNewTransactionChain(ConnectionContext connectionContext);
+}
index 0870fabb038d2d124831be87ee1fad6d5bfa1571..22988af60a522f2730a9621c8e06db60ce81318a 100644 (file)
@@ -9,9 +9,10 @@
 package org.opendaylight.openflowplugin.impl.device;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import java.util.concurrent.ExecutionException;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
 import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -20,10 +21,15 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
-import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,37 +52,30 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
 
     private final Object txLock = new Object();
 
-    private final DeviceState deviceState;
     private final DataBroker dataBroker;
     private WriteTransaction wTx;
     private BindingTransactionChain txChainFactory;
     private boolean submitIsEnabled;
-
-    TransactionChainManager(@Nonnull final DataBroker dataBroker, @Nonnull final DeviceState deviceState) {
+    private TransactionChainManagerStatus transactionChainManagerStatus;
+    private ReadyForNewTransactionChainHandler readyForNewTransactionChainHandler;
+    private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
+    private final ConnectionContext connectionContext;
+    private Registration managerRegistration;
+
+    TransactionChainManager(@Nonnull final DataBroker dataBroker,
+                            @Nonnull final ConnectionContext connectionContext,
+                            @Nonnull final Registration managerRegistration) {
         this.dataBroker = Preconditions.checkNotNull(dataBroker);
-        this.deviceState = Preconditions.checkNotNull(deviceState);
-        checkExistingNode();
-        txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
+        this.nodeII = Preconditions.checkNotNull(DeviceStateUtil.createNodeInstanceIdentifier(connectionContext.getNodeId()));
+        this.connectionContext = Preconditions.checkNotNull(connectionContext);
+        this.managerRegistration = Preconditions.checkNotNull(managerRegistration);
+        createTxChain(dataBroker);
         LOG.debug("created txChainManager");
     }
 
-    /**
-     * Creating new TransactionChainManager means we have new Node (HandShake process was successful), but
-     * the node existence in OPERATIONAL DataStore indicates some not finished NODE disconnection or some
-     * unexpected problem with DataStore.
-     * We should not continue with a PostHandShake NODE information collecting in this state.
-     */
-    private void checkExistingNode() {
-        Optional<Node> node = Optional.<Node> absent();
-        try {
-            node = dataBroker.newReadOnlyTransaction()
-                    .read(LogicalDatastoreType.OPERATIONAL, deviceState.getNodeInstanceIdentifier()).get();
-        }
-        catch (InterruptedException | ExecutionException e) {
-            LOG.warn("Not able to read node {} in Operation DataStore", deviceState.getNodeId());
-            throw new IllegalStateException(e);
-        }
-        Preconditions.checkArgument((!node.isPresent()), "Node {} is exist, can not add same now!", deviceState.getNodeId());
+    private void createTxChain(final DataBroker dataBroker) {
+        txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
+        this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
     }
 
     void initialSubmitWriteTransaction() {
@@ -84,15 +83,24 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
         submitWriteTransaction();
     }
 
+    public boolean attemptToRegisterHandler(final ReadyForNewTransactionChainHandler readyForNewTransactionChainHandler) {
+        if (null == this.readyForNewTransactionChainHandler) {
+            synchronized (this) {
+                Preconditions.checkState(null != this.managerRegistration);
+                this.readyForNewTransactionChainHandler = readyForNewTransactionChainHandler;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     boolean submitWriteTransaction() {
         if (!submitIsEnabled) {
             LOG.trace("transaction not committed - submit block issued");
             return false;
         }
-        if ( ! deviceState.isValid()) {
-            LOG.info("DeviceState is not valid will not submit transaction");
-            return false;
-        }
+
         if (wTx == null) {
             LOG.trace("nothing to commit - submit returns true");
             return true;
@@ -134,14 +142,14 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
 
     private void recreateTxChain() {
         txChainFactory.close();
-        txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
+        createTxChain(dataBroker);
         synchronized (txLock) {
             wTx = null;
         }
     }
 
     private WriteTransaction getTransactionSafely() {
-        if (wTx == null) {
+        if (wTx == null && !TransactionChainManagerStatus.SHUTTING_DOWN.equals(transactionChainManagerStatus)) {
             synchronized (txLock) {
                 if (wTx == null) {
                     wTx = txChainFactory.newWriteOnlyTransaction();
@@ -158,13 +166,45 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
 
     @Override
     public void close() {
-        LOG.debug("Removing node {} from operational DS.", deviceState.getNodeId());
+        LOG.debug("Removing node {} from operational DS.", nodeII);
         synchronized (txLock) {
             final WriteTransaction writeTx = getTransactionSafely();
-            writeTx.delete(LogicalDatastoreType.OPERATIONAL, deviceState.getNodeInstanceIdentifier());
-            writeTx.submit();
+            this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
+            writeTx.delete(LogicalDatastoreType.OPERATIONAL, nodeII);
+            CheckedFuture<Void, TransactionCommitFailedException> submitsFuture = writeTx.submit();
+            Futures.addCallback(submitsFuture, new FutureCallback<Void>() {
+                @Override
+                public void onSuccess(final Void aVoid) {
+                    notifyReadyForNewTransactionChainAndCloseFactory();
+                }
+
+                @Override
+                public void onFailure(final Throwable throwable) {
+                    LOG.info("Attempt to close transaction chain factory failed.", throwable);
+                    notifyReadyForNewTransactionChainAndCloseFactory();
+                }
+            });
             wTx = null;
-            txChainFactory.close();
         }
     }
+
+    private void notifyReadyForNewTransactionChainAndCloseFactory() {
+        synchronized (this) {
+            if (null != readyForNewTransactionChainHandler) {
+                readyForNewTransactionChainHandler.onReadyForNewTransactionChain(connectionContext);
+            }
+            try {
+                managerRegistration.close();
+            } catch (Exception e) {
+                LOG.warn("Failed to close transaction chain manager's registration.", e);
+            }
+            managerRegistration = null;
+        }
+        txChainFactory.close();
+    }
+
+    private enum TransactionChainManagerStatus {
+        WORKING, SHUTTING_DOWN;
+    }
+
 }
index 958476049d22c76cfce2dd866017a873348d4c3c..a0380df0f7b772d9b21b2a870a2d4c812c679211 100644 (file)
@@ -9,8 +9,14 @@
 package org.opendaylight.openflowplugin.impl.util;
 
 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Capabilities;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.CapabilitiesV10;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 /**
  * Created by Martin Bobak &lt;mbobak@cisco.com&gt; on 20.4.2015.
@@ -36,4 +42,7 @@ public final class DeviceStateUtil {
         deviceState.setQueueStatisticsAvailable(capabilitiesV10.isOFPCQUEUESTATS());
     }
 
+    public static KeyedInstanceIdentifier<Node, NodeKey> createNodeInstanceIdentifier(NodeId nodeId){
+        return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId));
+    }
 }
index 3f5b5c3d3479b642ab2882cf5087088c6db31136..538176c0f21b4f035dab42ef90ae29a2964c2e40 100644 (file)
@@ -36,6 +36,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetAsyncReply;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
@@ -78,17 +79,19 @@ public class DeviceContextImplTest {
     KeyedInstanceIdentifier<Node, NodeKey> nodeKeyIdent;
     @Mock
     TranslatorLibrary translatorLibrary;
+    @Mock
+    Registration registration;
 
     private final AtomicLong atomicLong = new AtomicLong(0);
 
     @Before
     public void setUp() {
-        final CheckedFuture<Optional<Node>, ReadFailedException> noExistNodeFuture = Futures.immediateCheckedFuture(Optional.<Node> absent());
+        final CheckedFuture<Optional<Node>, ReadFailedException> noExistNodeFuture = Futures.immediateCheckedFuture(Optional.<Node>absent());
         Mockito.when(rTx.read(LogicalDatastoreType.OPERATIONAL, nodeKeyIdent)).thenReturn(noExistNodeFuture);
         Mockito.when(dataBroker.newReadOnlyTransaction()).thenReturn(rTx);
         Mockito.when(dataBroker.createTransactionChain(Mockito.any(TransactionChainManager.class))).thenReturn(txChainFactory);
         Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeKeyIdent);
-        txChainManager = new TransactionChainManager(dataBroker, deviceState);
+        txChainManager = new TransactionChainManager(dataBroker, connectionContext, registration);
         final SettableFuture<RpcResult<GetAsyncReply>> settableFuture = SettableFuture.create();
         final SettableFuture<RpcResult<MultipartReply>> settableFutureMultiReply = SettableFuture.create();
         Mockito.when(requestContext.getFuture()).thenReturn(settableFuture);
@@ -114,30 +117,25 @@ public class DeviceContextImplTest {
         Mockito.when(dataBroker.newReadOnlyTransaction()).thenReturn(rTx);
         Mockito.when(connectionContext.getOutboundQueueProvider()).thenReturn(outboundQueueProvider);
         Mockito.when(connectionContext.getConnectionAdapter()).thenReturn(connectionAdapter);
-        deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary);
+        deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager);
 
         xid = new Xid(atomicLong.incrementAndGet());
         xidMulti = new Xid(atomicLong.incrementAndGet());
     }
 
-    @Test(expected = NullPointerException.class)
-    public void testDeviceContextImplConstructorNullConnectionContext() throws Exception {
-        new DeviceContextImpl(null, deviceState, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close();
-    }
-
     @Test(expected = NullPointerException.class)
     public void testDeviceContextImplConstructorNullDataBroker() throws Exception {
-        new DeviceContextImpl(connectionContext, deviceState, null, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close();
+        new DeviceContextImpl(connectionContext, deviceState, null, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager).close();
     }
 
     @Test(expected = NullPointerException.class)
     public void testDeviceContextImplConstructorNullDeviceState() throws Exception {
-        new DeviceContextImpl(connectionContext, null, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close();
+        new DeviceContextImpl(connectionContext, null, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager).close();
     }
 
     @Test(expected = NullPointerException.class)
     public void testDeviceContextImplConstructorNullTimer() throws Exception {
-        new DeviceContextImpl(null, deviceState, dataBroker, null, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close();
+        new DeviceContextImpl(null, deviceState, dataBroker, null, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager).close();
     }
 
     @Test
index 20a48418ab7150b5967686dadc49397351548dd0..81180fa562da8261418eb7d086f311885e8effc0 100644 (file)
@@ -31,12 +31,13 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
@@ -49,7 +50,7 @@ public class TransactionChainManagerTest {
     @Mock
     private DataBroker dataBroker;
     @Mock
-    private DeviceState deviceState;
+    private ConnectionContext connectionContext;
     @Mock
     private BindingTransactionChain txChain;
     @Mock
@@ -58,6 +59,9 @@ public class TransactionChainManagerTest {
     private TransactionChain<?, ?> transactionChain;
     @Mock
     HashedWheelTimer timer;
+    @Mock
+    Registration registration;
+
     @Mock
     private KeyedInstanceIdentifier<Node, NodeKey> nodeKeyIdent;
 
@@ -68,14 +72,12 @@ public class TransactionChainManagerTest {
     @Before
     public void setUp() throws Exception {
         final ReadOnlyTransaction readOnlyTx = Mockito.mock(ReadOnlyTransaction.class);
-        final CheckedFuture<Optional<Node>, ReadFailedException> noExistNodeFuture = Futures.immediateCheckedFuture(Optional.<Node> absent());
+        final CheckedFuture<Optional<Node>, ReadFailedException> noExistNodeFuture = Futures.immediateCheckedFuture(Optional.<Node>absent());
         Mockito.when(readOnlyTx.read(LogicalDatastoreType.OPERATIONAL, nodeKeyIdent)).thenReturn(noExistNodeFuture);
         Mockito.when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTx);
         Mockito.when(dataBroker.createTransactionChain(Matchers.any(TransactionChainListener.class)))
                 .thenReturn(txChain);
-        Mockito.when(deviceState.isValid()).thenReturn(Boolean.TRUE);
-        Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeKeyIdent);
-        txChainManager = new TransactionChainManager(dataBroker, deviceState);
+        txChainManager = new TransactionChainManager(dataBroker, connectionContext, registration);
         Mockito.when(txChain.newWriteOnlyTransaction()).thenReturn(writeTx);
 
         nodeId = new NodeId("h2g2:42");
@@ -112,6 +114,7 @@ public class TransactionChainManagerTest {
 
     /**
      * test of {@link TransactionChainManager#enableSubmit()}: no submit - counter is not active
+     *
      * @throws Exception
      */
     @Test
@@ -126,6 +129,7 @@ public class TransactionChainManagerTest {
 
     /**
      * test of {@link TransactionChainManager#enableSubmit()}: submit - after counter activated
+     *
      * @throws Exception
      */
     @Test