Merge "Fix NoSuchElementException thrown when transaction is empty"
authorTom Pantelis <tpanteli@brocade.com>
Fri, 24 Apr 2015 15:57:37 +0000 (15:57 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 24 Apr 2015 15:57:37 +0000 (15:57 +0000)
115 files changed:
features/mdsal/src/main/resources/features.xml
opendaylight/adsal/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SnapshotManager.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingNotificationAdapterModule.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/NotificationBrokerImplModule.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AbstractNotificationListenerRegistration.java [moved from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AbstractNotificationListenerRegistration.java with 96% similarity]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/AggregatedNotificationListenerRegistration.java [moved from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/AggregatedNotificationListenerRegistration.java with 97% similarity]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/HydrogenNotificationBrokerImpl.java [moved from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.java with 83% similarity]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/ListenerMapGeneration.java [moved from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/ListenerMapGeneration.java with 98% similarity]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationInvoker.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationListenerRegistration.java [moved from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationListenerRegistration.java with 95% similarity]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotifyTask.java [moved from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotifyTask.java with 98% similarity]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationListenerAdapter.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMNotificationServiceAdapter.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RpcIsNotRoutedException.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeGenerator.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/AbstractRuntimeCodeGenerator.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/DefaultRuntimeCodeGenerator.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcMetadata.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvoker.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvokerPrototype.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataBrokerImpl.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataTransactionImpl.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProxyContext.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/BindingAwareDataReaderRouter.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/NotificationInvokerFactory.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/RpcProviderRegistryTest.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/codegen/impl/DefaultRuntimeCodeGeneratorTest.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolderTest.java [deleted file]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractDataTreeCandidateNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ChainedCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ChainedTransactionProxy.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeCandidatePayload.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerProxy.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DeletedDataTreeCandidateNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ModifiedDataTreeCandidateNode.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ReadWriteShardDataTreeTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardCommitCoordinator.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTree.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeCohort.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeTransactionChain.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeTransactionParent.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinator.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardSnapshotCohort.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ForwardedReadyTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/PrimaryShardInfo.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/RegisterDataTreeChangeListenerReply.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractTransactionProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeCandidatePayloadTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerProxyTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerRegistrationActorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/compat/PreLithiumShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/ModificationPayloadTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/TestModel.java
opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataTreeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractDOMStoreTransaction.java
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadTransaction.java [moved from opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadTransaction.java with 80% similarity]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedReadWriteTransaction.java [moved from opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java with 78% similarity]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java [moved from opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java with 81% similarity]
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java [new file with mode: 0644]
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfBaseOps.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NodeContainerProxy.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToRpcRequestTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang [deleted file]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonNormalizedNodeBodyReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/input/to/cnsn/test/RestPutListDataTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreService.java
opendaylight/netconf/mdsal-netconf-connector/pom.xml
opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SubtreeFilter.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java

index f8bbfeca49ad69d34d72fbf459a64d71e4892c22..a992d2f95e19b497f81ae90641a269214095221c 100644 (file)
     <feature name='odl-netconf-mdsal' version='${project.version}' description="OpenDaylight :: Netconf :: Mdsal">
         <feature version='${config.version}'>odl-config-all</feature>
         <feature version='${netconf.version}'>odl-netconf-all</feature>
-        <!-- TODO remove dependency on sal netconf connector (odl-yangtools-models, sal-netconf-connector, model-inventory-->
-        <feature version='${yangtools.version}'>odl-yangtools-models</feature>
-        <bundle>mvn:org.opendaylight.controller/sal-netconf-connector/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller.model/model-inventory/${project.version}</bundle>
         <bundle>mvn:org.opendaylight.controller/netconf-ssh/${netconf.version}</bundle>
         <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
         <bundle>mvn:org.opendaylight.controller/mdsal-netconf-connector/${netconf.version}</bundle>
index a361285db5c60d42b65d0866a731d7a247d4c250..2c5727070c697a1a62424b2665beacc8095f7ab9 100644 (file)
@@ -8,8 +8,18 @@
 
 package org.opendaylight.controller.topologymanager.internal;
 
-import org.junit.Assert;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.controller.sal.core.Bandwidth;
@@ -40,17 +50,6 @@ import org.opendaylight.controller.switchmanager.Switch;
 import org.opendaylight.controller.switchmanager.SwitchConfig;
 import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-
 public class TopologyManagerImplTest {
     private TopologyManagerImpl topoManagerImpl;
 
@@ -783,15 +782,26 @@ public class TopologyManagerImplTest {
         }
 
         Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000));
-        // Give TopologyManger time to update its edges DB.
-        Thread.sleep(1000);
-        Assert.assertEquals(1, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(1);
         Assert.assertNotNull(topoManagerImpl.getEdges().get(edge));
     }
 
+    private void verifyEdgesSize(int expSize) throws InterruptedException {
+        int timeout = 5000;
+        for(int i = 0; i < timeout / 50; i++) {
+            if(topoManagerImpl.getEdges().size() == expSize) {
+                return;
+            }
+
+            Thread.sleep(50);
+        }
+
+        Assert.fail(String.format("Expected edges size %d. Actual was %d",
+                topoManagerImpl.getEdges().size(), expSize));
+    }
+
     @Test
-    public void testNotifyNodeConnector() throws ConstructionException,
-           InterruptedException {
+    public void testNotifyNodeConnector() throws Exception {
         TestSwitchManager swMgr = new TestSwitchManager();
         topoManagerImpl.setSwitchManager(swMgr);
         topoManagerImpl.nonClusterObjectCreate();
@@ -803,14 +813,14 @@ public class TopologyManagerImplTest {
         Map<String, Property> propMap = new HashMap<>();
         swMgr.addNodeConnectors(nc1);
         topoManagerImpl.notifyNodeConnector(nc1, UpdateType.ADDED, propMap);
-        Assert.assertEquals(0, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(0);
 
         topoManagerImpl.notifyNodeConnector(nc1, UpdateType.CHANGED, propMap);
-        Assert.assertEquals(0, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(0);
 
         swMgr.clear();
         topoManagerImpl.notifyNodeConnector(nc1, UpdateType.REMOVED, propMap);
-        Assert.assertEquals(0, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(0);
 
         // Test NodeConnector notification in the case that there is a related
         // edge update just before the notification.
@@ -830,9 +840,7 @@ public class TopologyManagerImplTest {
         swMgr.addNodeConnectors(nc2);
         topoManagerImpl.notifyNodeConnector(nc2, UpdateType.CHANGED, propMap);
         Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000));
-        // Give TopologyManger time to update its edges DB.
-        Thread.sleep(1000);
-        Assert.assertEquals(2, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(2);
 
         teu1 = new TopoEdgeUpdate(edge1, props, UpdateType.REMOVED);
         teu2 = new TopoEdgeUpdate(edge2, props, UpdateType.REMOVED);
@@ -841,9 +849,7 @@ public class TopologyManagerImplTest {
         topoedgeupdateList.add(teu2);
         topoManagerImpl.edgeUpdate(topoedgeupdateList);
         Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000));
-        // Give TopologyManger time to update its edges DB.
-        Thread.sleep(1000);
-        Assert.assertEquals(0, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(0);
         topoManagerImpl.notifyNodeConnector(nc1, UpdateType.REMOVED, propMap);
         topoManagerImpl.notifyNodeConnector(nc2, UpdateType.REMOVED, propMap);
 
@@ -870,14 +876,10 @@ public class TopologyManagerImplTest {
         swMgr.addNodeConnectors(nc2);
         topoManagerImpl.notifyNodeConnector(nc2, UpdateType.CHANGED, propMap);
         Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000));
-        // Give TopologyManger time to update its edges DB.
-        Thread.sleep(1000);
-        Assert.assertEquals(0, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(0);
         topoManagerImpl.notifyNodeConnector(nc1, UpdateType.REMOVED, propMap);
         topoManagerImpl.notifyNodeConnector(nc2, UpdateType.REMOVED, propMap);
         Assert.assertTrue(topoManagerImpl.flushUpdateQueue(5000));
-        // Give TopologyManger time to update its edges DB.
-        Thread.sleep(1000);
-        Assert.assertEquals(0, topoManagerImpl.getEdges().size());
+        verifyEdgesSize(0);
     }
 }
index 847954816ccd33f1c916ead7641b57200e2303e2..9a916625c9331413685d6263bfe053930b6795bf 100644 (file)
@@ -201,14 +201,16 @@ public class SnapshotManager implements SnapshotState {
 
             LOG.debug("lastSequenceNumber prior to capture: {}", lastSequenceNumber);
 
+            SnapshotManager.this.currentState = CREATING;
+
             try {
                 createSnapshotProcedure.apply(null);
             } catch (Exception e) {
+                SnapshotManager.this.currentState = IDLE;
                 LOG.error("Error creating snapshot", e);
                 return false;
             }
 
-            SnapshotManager.this.currentState = CREATING;
             return true;
         }
 
index 903cb27c92f52d8a6215ffd32c450f5c4039be28..971153bc7bf80dcb7858e538619b268a36b5ab28 100644 (file)
@@ -9,18 +9,17 @@ package org.opendaylight.controller.config.yang.md.sal.binding.impl;
 
 import org.opendaylight.controller.config.api.DependencyResolver;
 import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
 import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
 import org.opendaylight.controller.sal.core.api.Broker;
 
 public class BindingNotificationAdapterModule extends AbstractBindingNotificationAdapterModule  {
-    public BindingNotificationAdapterModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver) {
+    public BindingNotificationAdapterModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public BindingNotificationAdapterModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.md.sal.binding.impl.BindingNotificationAdapterModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public BindingNotificationAdapterModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.md.sal.binding.impl.BindingNotificationAdapterModule oldModule, final java.lang.AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
@@ -34,7 +33,7 @@ public class BindingNotificationAdapterModule extends AbstractBindingNotificatio
         final BindingToNormalizedNodeCodec codec = getBindingMappingServiceDependency();
         final Broker.ProviderSession session = getDomAsyncBrokerDependency().registerProvider(new DummyDOMProvider());
         final DOMNotificationService notifService = session.getService(DOMNotificationService.class);
-        return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), notifService, SingletonHolder.INVOKER_FACTORY);
+        return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), notifService);
     }
 
 }
index 415c9783da4891bf151afed1eb4b946a0531f1cb..58d5a855658879ad242a4dc24c3d465bd82eec52 100644 (file)
@@ -7,9 +7,12 @@
  */
 package org.opendaylight.controller.config.yang.md.sal.binding.impl;
 
-import com.google.common.util.concurrent.ListeningExecutorService;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenNotificationBrokerImpl;
+
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumNotificationProviderServiceAdapter;
 import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
-import org.opendaylight.controller.sal.binding.impl.NotificationBrokerImpl;
 
 /**
 *
@@ -17,14 +20,14 @@ import org.opendaylight.controller.sal.binding.impl.NotificationBrokerImpl;
 public final class NotificationBrokerImplModule extends
         org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractNotificationBrokerImplModule {
 
-    public NotificationBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
-            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+    public NotificationBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public NotificationBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
-            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
-            NotificationBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public NotificationBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            final NotificationBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
@@ -37,14 +40,20 @@ public final class NotificationBrokerImplModule extends
     @Override
     public java.lang.AutoCloseable createInstance() {
 
+        final NotificationPublishService notificationPublishService = getNotificationPublishAdapterDependency();
+        final NotificationService notificationService = getNotificationAdapterDependency();
+
+        if(notificationPublishService != null & notificationService != null) {
+            return new HeliumNotificationProviderServiceAdapter(notificationPublishService, notificationService);
+        }
+
         /*
          *  FIXME: Switch to new broker (which has different threading model)
          *  once this change is communicated with downstream users or
          *  we will have adapter implementation which will honor Helium
          *  threading model for notifications.
          */
-        ListeningExecutorService listeningExecutor = SingletonHolder.getDefaultNotificationExecutor();
-        NotificationBrokerImpl broker = new NotificationBrokerImpl(listeningExecutor);
-        return broker;
+
+        return new HydrogenNotificationBrokerImpl(SingletonHolder.getDefaultNotificationExecutor());
     }
 }
@@ -5,7 +5,7 @@
  * 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.binding.impl;
+package org.opendaylight.controller.md.sal.binding.compat;
 
 import org.opendaylight.controller.sal.binding.api.NotificationListener;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
@@ -5,13 +5,12 @@
  * 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.binding.impl;
+package org.opendaylight.controller.md.sal.binding.compat;
 
+import com.google.common.base.Preconditions;
 import org.opendaylight.controller.sal.binding.api.NotificationListener;
 import org.opendaylight.yangtools.yang.binding.Notification;
 
-import com.google.common.base.Preconditions;
-
 /**
  * An aggregated listener registration. This is a result of registering an invoker which can handle multiple
  * interfaces at the same time. In order to support correct delivery, we need to maintain per-type registrations
@@ -5,18 +5,17 @@
  * 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.binding.impl;
+package org.opendaylight.controller.md.sal.binding.compat;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicReference;
-
 import javax.annotation.concurrent.GuardedBy;
-
 import org.opendaylight.controller.sal.binding.api.NotificationListener;
 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.util.ListenerRegistry;
@@ -24,19 +23,16 @@ import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-
-public class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable {
-    private static final Logger LOG = LoggerFactory.getLogger(NotificationBrokerImpl.class);
+@Deprecated
+public class HydrogenNotificationBrokerImpl implements NotificationProviderService, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(HydrogenNotificationBrokerImpl.class);
 
     private final ListenerRegistry<NotificationInterestListener> interestListeners =
             ListenerRegistry.create();
     private final AtomicReference<ListenerMapGeneration> listeners = new AtomicReference<>(new ListenerMapGeneration());
     private final ExecutorService executor;
 
-    public NotificationBrokerImpl(final ExecutorService executor) {
+    public HydrogenNotificationBrokerImpl(final ExecutorService executor) {
         this.executor = Preconditions.checkNotNull(executor);
     }
 
@@ -47,7 +43,7 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
 
     @Override
     public void publish(final Notification notification, final ExecutorService service) {
-        for (NotificationListenerRegistration<?> r : listeners.get().listenersFor(notification)) {
+        for (final NotificationListenerRegistration<?> r : listeners.get().listenersFor(notification)) {
             service.submit(new NotifyTask(r, notification));
         }
     }
@@ -61,7 +57,7 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
         synchronized (this) {
             final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
                     mutableListeners();
-            for (NotificationListenerRegistration<?> reg : registrations) {
+            for (final NotificationListenerRegistration<?> reg : registrations) {
                 newListeners.put(reg.getType(), reg);
             }
 
@@ -69,7 +65,7 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
         }
 
         // Notifications are dispatched out of lock...
-        for (NotificationListenerRegistration<?> reg : registrations) {
+        for (final NotificationListenerRegistration<?> reg : registrations) {
             announceNotificationSubscription(reg.getType());
         }
     }
@@ -78,7 +74,7 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
         final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
                 mutableListeners();
 
-        for (NotificationListenerRegistration<?> reg : registrations) {
+        for (final NotificationListenerRegistration<?> reg : registrations) {
             newListeners.remove(reg.getType(), reg);
         }
 
@@ -89,7 +85,7 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
         for (final ListenerRegistration<NotificationInterestListener> listener : interestListeners) {
             try {
                 listener.getInstance().onNotificationSubscribtion(notification);
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 LOG.warn("Listener {} reported unexpected error on notification {}",
                         listener.getInstance(), notification, e);
             }
@@ -121,14 +117,14 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
 
     @Override
     public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(final org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
-        final NotificationInvoker invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener);
+        final NotificationInvoker invoker = NotificationInvoker.invokerFor(listener);
         final Set<Class<? extends Notification>> types = invoker.getSupportedNotifications();
         final NotificationListenerRegistration<?>[] regs = new NotificationListenerRegistration<?>[types.size()];
 
         // Populate the registrations...
         int i = 0;
-        for (Class<? extends Notification> type : types) {
-            regs[i] = new AggregatedNotificationListenerRegistration<Notification, Object>(type, invoker.getInvocationProxy(), regs) {
+        for (final Class<? extends Notification> type : types) {
+            regs[i] = new AggregatedNotificationListenerRegistration<Notification, Object>(type, invoker, regs) {
                 @Override
                 protected void removeRegistration() {
                     // Nothing to do, will be cleaned up by parent (below)
@@ -145,7 +141,7 @@ public class NotificationBrokerImpl implements NotificationProviderService, Auto
             @Override
             protected void removeRegistration() {
                 removeRegistrations(regs);
-                for (ListenerRegistration<?> reg : regs) {
+                for (final ListenerRegistration<?> reg : regs) {
                     reg.close();
                 }
             }
@@ -5,7 +5,7 @@
  * 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.binding.impl;
+package org.opendaylight.controller.md.sal.binding.compat;
 
 import java.util.Arrays;
 import java.util.Collection;
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationInvoker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/compat/NotificationInvoker.java
new file mode 100644 (file)
index 0000000..08a1477
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.controller.md.sal.binding.compat;
+
+import com.google.common.collect.ImmutableMap;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.NotificationListenerInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+
+final class NotificationInvoker implements org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> {
+
+    private final NotificationListener delegate;
+    private final Map<Class<? extends Notification>,InvokerContext> invokers;
+
+
+    private NotificationInvoker(final NotificationListener listener) {
+        delegate = listener;
+        final Map<Class<? extends Notification>, InvokerContext> builder = new HashMap<>();
+        for(final Class<?> iface : listener.getClass().getInterfaces()) {
+            if(NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
+                @SuppressWarnings("unchecked")
+                final Class<? extends NotificationListener> listenerType = (Class<? extends NotificationListener>) iface;
+                final NotificationListenerInvoker invoker = NotificationListenerInvoker.from(listenerType);
+                for(final Class<? extends Notification> type : getNotificationTypes(listenerType)) {
+                    builder.put(type, new InvokerContext(BindingReflections.findQName(type) , invoker));
+                }
+            }
+        }
+        invokers = ImmutableMap.copyOf(builder);
+    }
+
+    public static NotificationInvoker invokerFor(final NotificationListener listener) {
+        return new NotificationInvoker(listener);
+    }
+
+    public Set<Class<? extends Notification>> getSupportedNotifications() {
+        return invokers.keySet();
+    }
+
+    @Override
+    public void onNotification(final Notification notification) {
+        getContext(notification.getImplementedInterface()).invoke(notification);
+    };
+
+    private InvokerContext getContext(final Class<?> type) {
+        return invokers.get(type);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Set<Class<? extends Notification>> getNotificationTypes(final Class<? extends org.opendaylight.yangtools.yang.binding.NotificationListener> type) {
+        // TODO: Investigate possibility and performance impact if we cache this or expose
+        // it from NotificationListenerInvoker
+        final Set<Class<? extends Notification>> ret = new HashSet<>();
+        for(final Method method : type.getMethods()) {
+            if(BindingReflections.isNotificationCallback(method)) {
+                final Class<? extends Notification> notification = (Class<? extends Notification>) method.getParameterTypes()[0];
+                ret.add(notification);
+            }
+        }
+        return ret;
+    }
+
+    private class InvokerContext {
+
+        private final QName name;
+        private final NotificationListenerInvoker invoker;
+
+        private InvokerContext(final QName name, final NotificationListenerInvoker invoker) {
+            this.name = name;
+            this.invoker = invoker;
+        }
+
+        public void invoke(final Notification notification) {
+            invoker.invokeNotification(delegate, name, notification);
+        }
+
+    }
+
+}
@@ -5,7 +5,7 @@
  * 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.binding.impl;
+package org.opendaylight.controller.md.sal.binding.compat;
 
 import org.opendaylight.controller.sal.binding.api.NotificationListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -5,7 +5,7 @@
  * 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.binding.impl;
+package org.opendaylight.controller.md.sal.binding.compat;
 
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
index 668030e1a6f00572c7862d5a93f019f2d78eeed6..86822274efb2d6dfa9965e9e7b33863bec141164 100644 (file)
@@ -58,7 +58,7 @@ class BindingDOMNotificationListenerAdapter implements DOMNotificationListener {
         return invokers.keySet();
     }
 
-    private static Map<SchemaPath, NotificationListenerInvoker> createInvokerMapFor(final Class<? extends NotificationListener> implClz) {
+    public static Map<SchemaPath, NotificationListenerInvoker> createInvokerMapFor(final Class<? extends NotificationListener> implClz) {
         final Map<SchemaPath, NotificationListenerInvoker> builder = new HashMap<>();
         for(final Class<?> iface : implClz.getInterfaces()) {
             if(NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
index cdf03fa5527d76c911cccb30cc6b40b586f08b6f..2a31d34d016a3e86d116b9c5e9c1f57fb775bd1f 100644 (file)
@@ -14,8 +14,6 @@ import org.opendaylight.controller.md.sal.binding.api.NotificationService;
 import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
 import org.opendaylight.controller.md.sal.dom.api.DOMService;
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -34,7 +32,7 @@ public class BindingDOMNotificationServiceAdapter implements NotificationService
     private final BindingNormalizedNodeSerializer codec;
     private final DOMNotificationService domNotifService;
 
-    public BindingDOMNotificationServiceAdapter(final BindingNormalizedNodeSerializer codec, final DOMNotificationService domNotifService, final NotificationInvokerFactory notificationInvokerFactory) {
+    public BindingDOMNotificationServiceAdapter(final BindingNormalizedNodeSerializer codec, final DOMNotificationService domNotifService) {
         this.codec = codec;
         this.domNotifService = domNotifService;
     }
@@ -72,8 +70,7 @@ public class BindingDOMNotificationServiceAdapter implements NotificationService
         protected NotificationService createInstance(final BindingToNormalizedNodeCodec codec,
                 final ClassToInstanceMap<DOMService> delegates) {
             final DOMNotificationService domNotification = delegates.getInstance(DOMNotificationService.class);
-            final NotificationInvokerFactory invokerFactory = SingletonHolder.INVOKER_FACTORY;
-            return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), domNotification, invokerFactory);
+            return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), domNotification);
         }
 
         @Override
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RpcIsNotRoutedException.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RpcIsNotRoutedException.java
deleted file mode 100644 (file)
index 5317324..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.codegen;
-
-import com.google.common.base.Preconditions;
-
-/**
- * Exception is raised when supplied Bidning Aware
- * RPCService class is not routed and was used in context
- * where routed RPCs should only be used.
- *
- */
-public class RpcIsNotRoutedException extends IllegalStateException {
-
-    private static final long serialVersionUID = 1L;
-
-    public RpcIsNotRoutedException(final String message, final Throwable cause) {
-        super(Preconditions.checkNotNull(message), cause);
-    }
-
-    public RpcIsNotRoutedException(final String message) {
-        super(Preconditions.checkNotNull(message));
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeGenerator.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeGenerator.java
deleted file mode 100644 (file)
index c7c5f10..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.codegen;
-
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-
-public interface RuntimeCodeGenerator {
-
-    /**
-     * Returns an instance of provided RpcService type which delegates all calls
-     * to the delegate.
-     *
-     * <p>
-     * Returned instance:
-     * <ul>
-     * <li>implements provided subclass of RpcService type and
-     * {@link org.opendaylight.controller.sal.binding.spi.DelegateProxy} interface.
-     * <li>
-     * <p>
-     * delegates all invocations of methods, which are defined in RpcService
-     * subtype to delegate which is defined by
-     * {@link org.opendaylight.controller.sal.binding.spi.DelegateProxy#setDelegate(Object)}.
-     * <p>
-     * If delegate is not defined (<code>getDelegate() == null</code>)
-     * implementation throws {@link IllegalStateException}
-     * <li>{@link org.opendaylight.controller.sal.binding.spi.DelegateProxy#getDelegate()} - returns the delegate to which
-     * all calls are delegated.
-     * <li>{@link org.opendaylight.controller.sal.binding.spi.DelegateProxy#setDelegate(Object)} - sets the delegate for
-     * particular instance
-     *
-     * </ul>
-     *
-     * @param serviceType
-     *            - Subclass of RpcService for which direct proxy is to be
-     *            generated.
-     * @return Instance of RpcService of provided serviceType which implements
-     *         and {@link org.opendaylight.controller.sal.binding.spi.DelegateProxy}
-     * @throws IllegalArgumentException
-     *
-     */
-    <T extends RpcService> T getDirectProxyFor(Class<T> serviceType) throws IllegalArgumentException;
-
-    /**
-     * Returns an instance of provided RpcService type which routes all calls to
-     * other instances selected on particular input field.
-     *
-     * <p>
-     * Returned instance:
-     * <ul>
-     * <li>Implements:
-     * <ul>
-     * <li>{@link org.opendaylight.controller.sal.binding.spi.DelegateProxy}
-     * <li>{@link RpcRouter}
-     * </ul>
-     * <li>
-     * routes all invocations of methods, which are defined in RpcService
-     * subtype based on method arguments and routing information defined in the
-     * RpcRoutingTables for this instance
-     * {@link RpcRouter#getRoutingTable(Class)}.
-     * <ul>
-     * <li>
-     * Implementation uses
-     * {@link RpcRouter#getService(Class, org.opendaylight.yangtools.yang.binding.InstanceIdentifier)} method to
-     * retrieve particular instance to which call will be routed.
-     * <li>
-     * Instance of {@link org.opendaylight.yangtools.yang.binding.InstanceIdentifier} is determined by first argument of
-     * method and is retrieved via method which is annotated with
-     * {@link org.opendaylight.yangtools.yang.binding.annotations.RoutingContext}.
-     * Class representing Routing Context Identifier is retrieved by a
-     * {@link org.opendaylight.yangtools.yang.binding.annotations.RoutingContext}.
-     * <li>If first argument is not defined / {@link org.opendaylight.yangtools.yang.binding.annotations.RoutingContext} annotation
-     * is not present on any field invocation will be delegated to default
-     * service {@link RpcRouter#getDefaultService()}.
-     * </ul>
-     *
-     * @param serviceType
-     *            - Subclass of RpcService for which Router is to be generated.
-     * @return Instance of RpcService of provided serviceType which implements
-     *         also {@link RpcRouter}<T> and {@link org.opendaylight.controller.sal.binding.spi.DelegateProxy}
-     * @throws RpcIsNotRoutedException
-     */
-    <T extends RpcService> RpcRouter<T> getRouterFor(Class<T> serviceType,String name) throws IllegalArgumentException, RpcIsNotRoutedException;
-
-    NotificationInvokerFactory getInvokerFactory();
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.java
deleted file mode 100644 (file)
index ae90a77..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.codegen;
-
-import java.lang.reflect.Field;
-import java.util.Map;
-
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-
-public final class RuntimeCodeHelper {
-    private RuntimeCodeHelper() {
-        throw new UnsupportedOperationException("Utility class should never be instantiated");
-    }
-
-    private static Field getField(final Class<?> cls, final String name) {
-        try {
-            return cls.getField(name);
-        } catch (NoSuchFieldException e) {
-            throw new IllegalArgumentException(
-                    String.format("Class %s is missing field %s", cls, name), e);
-        } catch (SecurityException e) {
-            throw new IllegalStateException(String.format("Failed to examine class %s", cls), e);
-        }
-    }
-
-    private static Field getDelegateField(final Class<?> cls) {
-        return getField(cls, RuntimeCodeSpecification.DELEGATE_FIELD);
-    }
-
-    private static Object getFieldValue(final Field field, final Object obj) {
-        try {
-            return field.get(obj);
-        } catch (IllegalAccessException e) {
-            throw new IllegalStateException(String.format("Failed to get field %s of object %s", field, obj), e);
-        }
-    }
-
-    private static void setFieldValue(final Field field, final Object obj, final Object value) {
-        try {
-            field.set(obj, value);
-        } catch (IllegalAccessException e) {
-            throw new IllegalStateException(String.format("Failed to set field %s to %s", field, value), e);
-        }
-    }
-
-    /**
-     * Helper method to return delegate from ManagedDirectedProxy with use of reflection.
-     *
-     * Note: This method uses reflection, but access to delegate field should be
-     * avoided and called only if necessary.
-     */
-    @SuppressWarnings("unchecked")
-    public static <T extends RpcService> T getDelegate(final RpcService proxy) {
-        return (T)getFieldValue(getDelegateField(proxy.getClass()), proxy);
-    }
-
-    /**
-     * Helper method to set delegate to ManagedDirectedProxy with use of reflection.
-     *
-     * Note: This method uses reflection, but setting delegate field should not occur too much
-     * to introduce any significant performance hits.
-     */
-    public static void setDelegate(final Object proxy, final Object delegate) {
-        final Field field = getDelegateField(proxy.getClass());
-
-        if (delegate != null) {
-            final Class<?> ft = field.getType();
-            if (!ft.isAssignableFrom(delegate.getClass())) {
-                throw new IllegalArgumentException(
-                        String.format("Field %s type %s is not compatible with delegate type %s",
-                                field, ft, delegate.getClass()));
-            }
-        }
-
-        setFieldValue(field, proxy, delegate);
-    }
-
-    @SuppressWarnings("unchecked")
-    public static Map<InstanceIdentifier<? extends Object>,? extends RpcService> getRoutingTable(final RpcService target, final Class<? extends BaseIdentity> tableClass) {
-        final Field field = getField(target.getClass(), RuntimeCodeSpecification.getRoutingTableField(tableClass));
-        return (Map<InstanceIdentifier<? extends Object>,? extends RpcService>) getFieldValue(field, target);
-    }
-
-    public static void setRoutingTable(final RpcService target, final Class<? extends BaseIdentity> tableClass, final Map<InstanceIdentifier<? extends Object>,? extends RpcService> routingTable) {
-        final Field field = getField(target.getClass(), RuntimeCodeSpecification.getRoutingTableField(tableClass));
-        setFieldValue(field, target, routingTable);
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.java
deleted file mode 100644 (file)
index 22b6f6f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (c) 2013 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.binding.codegen;
-
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-
-public final class RuntimeCodeSpecification {
-    public final static String DIRECT_PROXY_SUFFIX = "DirectProxy";
-    public final static String INVOKER_SUFFIX = "ListenerInvoker";
-    public final static String ROUTER_SUFFIX = "Router";
-
-    public final static String DELEGATE_FIELD = "_delegate";
-    public final static String ROUTING_TABLE_FIELD_PREFIX = "_routes_";
-
-    private RuntimeCodeSpecification() {
-        throw new UnsupportedOperationException("Utility class");
-    }
-
-    /**
-     * Returns a name for generated interface
-     */
-    private static String getGeneratedName(final Class<? extends Object> cls, final String suffix) {
-        return cls.getName() + "$$Broker$" + suffix;
-    }
-
-    public static String getInvokerName(final Class<? extends NotificationListener> listener) {
-        return getGeneratedName(listener, RuntimeCodeSpecification.INVOKER_SUFFIX);
-    }
-
-    /**
-     * Returns a name for DirectProxy implementation
-     */
-    public static String getDirectProxyName(final Class<? extends RpcService> base) {
-        return getGeneratedName(base, RuntimeCodeSpecification.DIRECT_PROXY_SUFFIX);
-    }
-
-    /**
-     * Returns a name for Router implementation
-     */
-    public static String getRouterName(final Class<? extends RpcService> base) {
-        return getGeneratedName(base, RuntimeCodeSpecification.ROUTER_SUFFIX);
-    }
-
-    /**
-     * Returns a field name for specified routing context
-     */
-    public static String getRoutingTableField(final Class<? extends BaseIdentity> routingContext) {
-        return "_routes_" + routingContext.getSimpleName();
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/AbstractRuntimeCodeGenerator.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/AbstractRuntimeCodeGenerator.java
deleted file mode 100644 (file)
index 6c02001..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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.binding.codegen.impl;
-
-import com.google.common.base.Supplier;
-import com.google.common.collect.Iterables;
-import java.util.Map;
-import java.util.WeakHashMap;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.CtMethod;
-import javassist.NotFoundException;
-import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
-import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
-import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
-import org.opendaylight.yangtools.util.ClassLoaderUtils;
-import org.opendaylight.yangtools.yang.binding.BindingMapping;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
-
-abstract class AbstractRuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
-    @GuardedBy("this")
-    private final Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses = new WeakHashMap<>();
-    private final CtClass brokerNotificationListener;
-    protected final JavassistUtils utils;
-
-    protected AbstractRuntimeCodeGenerator(final ClassPool pool) {
-        utils = JavassistUtils.forClassPool(pool);
-
-        /*
-         * Make sure Javassist ClassPool sees the classloader of RpcService
-         */
-        utils.ensureClassLoader(RpcService.class);
-
-        brokerNotificationListener = utils.asCtClass(org.opendaylight.controller.sal.binding.api.NotificationListener.class);
-    }
-
-    protected final CtClass getBrokerNotificationListener() {
-        return brokerNotificationListener;
-    }
-
-    protected abstract RuntimeGeneratedInvokerPrototype generateListenerInvoker(Class<? extends NotificationListener> cls);
-    protected abstract <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType);
-    protected abstract <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, RpcServiceMetadata metadata);
-
-    private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException, RpcIsNotRoutedException {
-        final RpcServiceMetadata metadata = new RpcServiceMetadata();
-
-        for (CtMethod method : iface.getMethods()) {
-            if (isRpcMethodWithInput(iface, method)) {
-                final RpcMetadata routingPair = getRpcMetadata(method);
-                if (routingPair != null) {
-                    metadata.addContext(routingPair.getContext());
-                    metadata.addRpcMethod(method.getName(), routingPair);
-
-                    /*
-                     * Force-load the RPC class representing the "input" of this RPC.
-                     *
-                     * FIXME: this is pre-existing side-effect of the original code, which
-                     *        kept a reference to the loaded class, but it did not use it.
-                     *
-                     *        There was no explanation as to why forcing this load was
-                     *        necessary. As far as I can tell now is that it forces the
-                     *        resolution of method arguments, which would (according to
-                     *        my reading of JLS) occur only when the method is invoked via
-                     *        binding-aware class action, not when coming from
-                     *        binding-independent world. Whether that makes sense or not,
-                     *        remains to be investigated.
-                     */
-                    Thread.currentThread().getContextClassLoader().loadClass(routingPair.getInputType().getName());
-                } else {
-                    throw new RpcIsNotRoutedException(String.format("RPC %s from %s is not routed", method.getName(), iface.getName()));
-                }
-            }
-        }
-
-        return metadata;
-    }
-
-
-    private boolean isRpcMethodWithInput(final CtClass iface, final CtMethod method) throws NotFoundException {
-        if(iface.equals(method.getDeclaringClass())
-                && method.getParameterTypes().length == 1) {
-            final CtClass onlyArg = method.getParameterTypes()[0];
-            if(onlyArg.isInterface() && onlyArg.getName().endsWith(BindingMapping.RPC_INPUT_SUFFIX)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private RpcMetadata getRpcMetadata(final CtMethod method) throws NotFoundException {
-        final CtClass inputClass = method.getParameterTypes()[0];
-        return rpcMethodMetadata(inputClass, inputClass, method.getName());
-    }
-
-    private RpcMetadata rpcMethodMetadata(final CtClass dataClass, final CtClass inputClass, final String rpcMethod) throws NotFoundException {
-        for (CtMethod method : dataClass.getMethods()) {
-            if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
-                for (Object annotation : method.getAvailableAnnotations()) {
-                    if (annotation instanceof RoutingContext) {
-                        boolean encapsulated = !method.getReturnType().equals(utils.asCtClass(InstanceIdentifier.class));
-                        return new RpcMetadata(rpcMethod, ((RoutingContext)annotation).value(), method, encapsulated, inputClass);
-                    }
-                }
-            }
-        }
-
-        for (CtClass iface : dataClass.getInterfaces()) {
-            final RpcMetadata ret = rpcMethodMetadata(iface, inputClass, rpcMethod);
-            if(ret != null) {
-                return ret;
-            }
-        }
-        return null;
-    }
-
-    private synchronized RuntimeGeneratedInvokerPrototype resolveInvokerClass(final Class<? extends NotificationListener> cls) {
-        RuntimeGeneratedInvokerPrototype invoker = invokerClasses.get(cls);
-        if (invoker != null) {
-            return invoker;
-        }
-
-        synchronized (utils) {
-            invoker = ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Supplier<RuntimeGeneratedInvokerPrototype>() {
-                @Override
-                public RuntimeGeneratedInvokerPrototype get() {
-                    return generateListenerInvoker(cls);
-                }
-            });
-        }
-
-        invokerClasses.put(cls, invoker);
-        return invoker;
-    }
-
-    @Override
-    public final NotificationInvokerFactory getInvokerFactory() {
-        return this;
-    }
-
-    @Override
-    public final <T extends RpcService> T getDirectProxyFor(final Class<T> serviceType) {
-        synchronized (utils) {
-            return ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), directProxySupplier(serviceType));
-        }
-    }
-
-    @Override
-    public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) throws RpcIsNotRoutedException {
-        final RpcServiceMetadata metadata = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), new Supplier<RpcServiceMetadata>() {
-            @Override
-            public RpcServiceMetadata get() {
-                try {
-                    return getRpcMetadata(utils.asCtClass(serviceType));
-                } catch (ClassNotFoundException | NotFoundException e) {
-                    throw new IllegalStateException(String.format("Failed to load metadata for class %s", serviceType), e);
-                }
-            }
-        });
-
-        if (Iterables.isEmpty(metadata.getContexts())) {
-            throw new RpcIsNotRoutedException("Service doesn't have routing context associated.");
-        }
-
-        synchronized (utils) {
-            final T instance = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), routerSupplier(serviceType, metadata));
-            return new RpcRouterCodegenInstance<T>(name, serviceType, instance, metadata.getContexts());
-        }
-    }
-
-    @Override
-    public NotificationInvoker invokerFor(final NotificationListener instance) {
-        final Class<? extends NotificationListener> cls = instance.getClass();
-        final RuntimeGeneratedInvokerPrototype prototype = resolveInvokerClass(cls);
-
-        try {
-            return RuntimeGeneratedInvoker.create(instance, prototype);
-        } catch (InstantiationException | IllegalAccessException e) {
-            throw new IllegalStateException(String.format("Failed to create invoker for %s", instance), e);
-        }
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/DefaultRuntimeCodeGenerator.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/DefaultRuntimeCodeGenerator.java
deleted file mode 100644 (file)
index dfa164b..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.codegen.impl;
-
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-import java.lang.reflect.Method;
-import java.util.Map;
-import javassist.CannotCompileException;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.CtMethod;
-import javassist.NotFoundException;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification;
-import org.opendaylight.yangtools.sal.binding.generator.util.ClassGenerator;
-import org.opendaylight.yangtools.sal.binding.generator.util.MethodGenerator;
-import org.opendaylight.yangtools.util.ClassLoaderUtils;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
-import org.opendaylight.yangtools.yang.binding.RpcImplementation;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
-
-final class DefaultRuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
-
-    DefaultRuntimeCodeGenerator(final ClassPool pool) {
-        super(pool);
-    }
-
-    @Override
-    protected <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType) {
-        return new Supplier<T>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public T get() {
-                final String proxyName = RuntimeCodeSpecification.getDirectProxyName(serviceType);
-
-                final Class<?> potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName);
-                if (potentialClass != null) {
-                    try {
-                        return (T)potentialClass.newInstance();
-                    } catch (InstantiationException | IllegalAccessException e) {
-                        throw new IllegalStateException("Failed to instantiate class " + potentialClass.getName(), e);
-                    }
-                }
-
-                final CtClass supertype = utils.asCtClass(serviceType);
-                final String directProxyName = RuntimeCodeSpecification.getDirectProxyName(serviceType);
-
-                final CtClass createdCls;
-                try {
-                    createdCls = utils.createClass(directProxyName, supertype, new ClassGenerator() {
-                        @Override
-                        public void process(final CtClass cls) throws CannotCompileException {
-                            utils.field(cls, RuntimeCodeSpecification.DELEGATE_FIELD, serviceType);
-                            utils.implementsType(cls, utils.asCtClass(RpcImplementation.class));
-                            utils.implementMethodsFrom(cls, supertype, new MethodGenerator() {
-                                @Override
-                                public void process(final CtMethod method) throws CannotCompileException {
-                                    final StringBuilder sb = new StringBuilder("\n");
-                                    sb.append("{\n");
-                                    sb.append("    if (").append(RuntimeCodeSpecification.DELEGATE_FIELD).append(" == null) {\n");
-                                    sb.append("        throw new java.lang.IllegalStateException(\"No default provider is available\");\n");
-                                    sb.append("    }\n");
-                                    sb.append("    return ($r) ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append('.').append(method.getName()).append("($$);\n");
-                                    sb.append("}\n");
-                                    method.setBody(sb.toString());
-                                }
-                            });
-
-                            // FIXME: copy this one...
-                            utils.implementMethodsFrom(cls, utils.asCtClass(RpcImplementation.class), new MethodGenerator() {
-                                @Override
-                                public void process(final CtMethod method) throws CannotCompileException {
-                                    final StringBuilder sb = new StringBuilder("\n");
-                                    sb.append("{\n");
-                                    sb.append("    throw new java.lang.IllegalStateException(\"No provider is processing supplied message\");\n");
-                                    sb.append("    return ($r) null;\n");
-                                    sb.append("}\n");
-                                    method.setBody(sb.toString());
-                                }
-                            });
-                        }
-                    });
-                } catch (CannotCompileException e) {
-                    throw new IllegalStateException("Failed to create class " + directProxyName, e);
-                }
-
-                final Class<?> c;
-                try {
-                    c = createdCls.toClass(serviceType.getClassLoader(), serviceType.getProtectionDomain());
-                } catch (CannotCompileException e) {
-                    throw new IllegalStateException(String.format("Failed to create class %s", createdCls), e);
-                }
-
-                try {
-                    return (T) c.newInstance();
-                } catch (InstantiationException | IllegalAccessException e) {
-                    throw new IllegalStateException(String.format("Failed to instantiated class %s", c), e);
-                }
-            }
-        };
-    }
-
-    @Override
-    protected <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, final RpcServiceMetadata metadata) {
-        return new Supplier<T>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public T get() {
-                final CtClass supertype = utils.asCtClass(serviceType);
-                final String routerName = RuntimeCodeSpecification.getRouterName(serviceType);
-                final Class<?> potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName);
-                if (potentialClass != null) {
-                    try {
-                        return (T)potentialClass.newInstance();
-                    } catch (InstantiationException | IllegalAccessException e) {
-                        throw new IllegalStateException("Failed to instantiate class", e);
-                    }
-                }
-
-                final CtClass targetCls;
-                try {
-                    targetCls = utils.createClass(routerName, supertype, new ClassGenerator() {
-                        @Override
-                        public void process(final CtClass cls) throws CannotCompileException {
-                            utils.field(cls, RuntimeCodeSpecification.DELEGATE_FIELD, serviceType);
-                            //utils.field(cls, REMOTE_INVOKER_FIELD,iface);
-                            utils.implementsType(cls, utils.asCtClass(RpcImplementation.class));
-
-                            for (final Class<? extends BaseIdentity> ctx : metadata.getContexts()) {
-                                utils.field(cls, RuntimeCodeSpecification.getRoutingTableField(ctx), Map.class);
-                            }
-
-                            utils.implementMethodsFrom(cls, supertype, new MethodGenerator() {
-                                @Override
-                                public void process(final CtMethod method) throws CannotCompileException {
-                                    final int ptl;
-                                    try {
-                                        ptl = method.getParameterTypes().length;
-                                    } catch (NotFoundException e) {
-                                        throw new CannotCompileException(e);
-                                    }
-                                    final StringBuilder sb = new StringBuilder();
-
-                                    switch (ptl) {
-                                    case 0:
-                                        sb.append("return ($r) ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append('.').append(method.getName()).append("($$);");
-                                        break;
-                                    case 1:
-                                        final RpcMetadata rpcMeta = metadata.getRpcMethod(method.getName());
-                                        final String rtGetter = rpcMeta.getInputRouteGetter().getName();
-                                        final String stName = supertype.getName();
-
-                                        sb.append('\n');
-                                        sb.append("{\n");
-                                        sb.append("    if ($1 == null) {\n");
-                                        sb.append("        throw new IllegalArgumentException(\"RPC input must not be null and must contain a value for field ").append(rtGetter).append("\");\n");
-                                        sb.append("    }\n");
-                                        sb.append("    if ($1.").append(rtGetter).append("() == null) {\n");
-                                        sb.append("        throw new IllegalArgumentException(\"Field ").append(rtGetter).append(" must not be null\");\n");
-                                        sb.append("    }\n");
-
-                                        sb.append("    final org.opendaylight.yangtools.yang.binding.InstanceIdentifier identifier = $1.").append(rtGetter).append("()");
-                                        if (rpcMeta.isRouteEncapsulated()) {
-                                            sb.append(".getValue()");
-                                        }
-                                        sb.append(";\n");
-
-                                        sb.append("    ").append(supertype.getName()).append(" instance = (").append(stName).append(") ").append(RuntimeCodeSpecification.getRoutingTableField(rpcMeta.getContext())).append(".get(identifier);\n");
-                                        sb.append("    if (instance == null) {\n");
-                                        sb.append("        instance = ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append(";\n");
-                                        sb.append("    }\n");
-
-                                        sb.append("    if (instance == null) {\n");
-                                        sb.append("        throw new java.lang.IllegalStateException(\"No routable provider is processing routed message for \" + String.valueOf(identifier));\n");
-                                        sb.append("    }\n");
-                                        sb.append("    return ($r) instance.").append(method.getName()).append("($$);\n");
-                                        sb.append('}');
-                                        break;
-                                    default:
-                                        throw new CannotCompileException(String.format("Unsupported parameters length %s", ptl));
-                                    }
-
-                                    method.setBody(sb.toString());
-                                }
-                            });
-
-                            // FIXME: move this into a template class
-                            utils.implementMethodsFrom(cls, utils.asCtClass(RpcImplementation.class), new MethodGenerator() {
-                                @Override
-                                public void process(final CtMethod method) throws CannotCompileException {
-                                    final StringBuilder sb = new StringBuilder("\n");
-                                    sb.append("{\n");
-                                    sb.append("    throw new java.lang.IllegalStateException(\"No provider is processing supplied message\");\n");
-                                    sb.append("    return ($r) null;\n");
-                                    sb.append("}\n");
-
-                                    method.setBody(sb.toString());
-                                }
-                            });
-                        }
-                    });
-                } catch (CannotCompileException e) {
-                    throw new IllegalStateException("Failed to create class " + routerName, e);
-                }
-
-                final Class<?> c;
-                try {
-                    c = targetCls.toClass(serviceType.getClassLoader(), serviceType.getProtectionDomain());
-                } catch (CannotCompileException e) {
-                    throw new IllegalStateException(String.format("Failed to compile class %s", targetCls), e);
-                }
-
-                try {
-                    return (T)c.newInstance();
-                } catch (InstantiationException | IllegalAccessException e) {
-                    throw new IllegalStateException(String.format("Failed to instantiate class %s", c), e);
-                }
-            }
-        };
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    protected RuntimeGeneratedInvokerPrototype generateListenerInvoker(final Class<? extends NotificationListener> listenerType) {
-        final String invokerName = RuntimeCodeSpecification.getInvokerName(listenerType);
-        final CtClass targetCls;
-
-        // Builder for a set of supported types. Filled while the target class is being generated
-        final Builder<Class<? extends Notification>> b = ImmutableSet.builder();
-
-        try {
-            targetCls = utils.createClass(invokerName, getBrokerNotificationListener(), new ClassGenerator() {
-                @Override
-                public void process(final CtClass cls) throws CannotCompileException {
-                    utils.field(cls, RuntimeCodeSpecification.DELEGATE_FIELD, listenerType);
-                    utils.implementMethodsFrom(cls, getBrokerNotificationListener(), new MethodGenerator() {
-                        @Override
-                        public void process(final CtMethod method) throws CannotCompileException {
-                            final StringBuilder sb = new StringBuilder("\n");
-
-                            sb.append("{\n");
-
-                            for (Method m : listenerType.getMethods()) {
-                                if (BindingReflections.isNotificationCallback(m)) {
-                                    final Class<?> argType = m.getParameterTypes()[0];
-
-                                    // populates builder above
-                                    b.add((Class<? extends Notification>) argType);
-
-                                    sb.append("    if ($1 instanceof ").append(argType.getName()).append(") {\n");
-                                    sb.append("        ").append(RuntimeCodeSpecification.DELEGATE_FIELD).append('.').append(m.getName()).append("((").append(argType.getName()).append(") $1);\n");
-                                    sb.append("        return null;\n");
-                                    sb.append("    } else ");
-                                }
-                            }
-
-                            sb.append("    return null;\n");
-                            sb.append("}\n");
-                            method.setBody(sb.toString());
-                        }
-                    });
-                }
-            });
-        } catch (CannotCompileException e) {
-            throw new IllegalStateException("Failed to create class " + invokerName, e);
-        }
-
-        final Class<?> finalClass;
-        try {
-            finalClass = targetCls.toClass(listenerType.getClassLoader(), listenerType.getProtectionDomain());
-        } catch (CannotCompileException e) {
-            throw new IllegalStateException(String.format("Failed to compile class %s", targetCls), e);
-        }
-
-        return new RuntimeGeneratedInvokerPrototype(b.build(), (Class<? extends org.opendaylight.controller.sal.binding.api.NotificationListener<?>>) finalClass);
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcMetadata.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcMetadata.java
deleted file mode 100644 (file)
index 6f9f85f..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Copyright (c) 2013 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.binding.codegen.impl;
-
-import javassist.CtClass;
-import javassist.CtMethod;
-
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-
-final class RpcMetadata {
-    private final Class<? extends BaseIdentity> context;
-    private final CtMethod inputRouteGetter;
-    private final Boolean routeEncapsulated;
-    private final CtClass inputType;
-    private final String methodName;
-
-    public Class<? extends BaseIdentity> getContext() {
-        return context;
-    }
-
-    public CtMethod getInputRouteGetter() {
-        return inputRouteGetter;
-    }
-
-    public CtClass getInputType() {
-        return inputType;
-    }
-
-    public boolean isRouteEncapsulated() {
-        return routeEncapsulated;
-    }
-
-    public RpcMetadata(final String methodName, final Class<? extends BaseIdentity> context, final CtMethod inputRouteGetter, final boolean routeEncapsulated, final CtClass inputType) {
-        this.inputRouteGetter = Preconditions.checkNotNull(inputRouteGetter);
-        this.methodName = Preconditions.checkNotNull(methodName);
-        this.inputType = Preconditions.checkNotNull(inputType);
-        this.context = Preconditions.checkNotNull(context);
-        this.routeEncapsulated = routeEncapsulated;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + methodName.hashCode();
-        result = prime * result + context.hashCode();
-        result = prime * result + inputRouteGetter.hashCode();
-        result = prime * result + routeEncapsulated.hashCode();
-        result = prime * result +  inputType.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof RpcMetadata)) {
-            return false;
-        }
-        final RpcMetadata other = (RpcMetadata) obj;
-        if (!methodName.equals(other.methodName)) {
-            return false;
-        }
-        if (!context.equals(other.context)) {
-            return false;
-        }
-        if (!inputRouteGetter.equals(other.inputRouteGetter)) {
-            return false;
-        }
-        if (!routeEncapsulated.equals(other.routeEncapsulated)) {
-            return false;
-        }
-        return inputType.equals(other.inputType);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this)
-                .add("context", context)
-                .add("inputRouteGetter", inputRouteGetter)
-                .add("inputType", inputType)
-                .add("methodName", methodName)
-                .add("routeEncapsulated", routeEncapsulated)
-                .toString();
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java
deleted file mode 100644 (file)
index d69aeed..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.codegen.impl;
-
-import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.setRoutingTable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRoutingTable;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.util.ListenerRegistry;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RpcRouterCodegenInstance<T extends RpcService> implements //
-RpcRouter<T>, RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
-
-    private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class);
-
-    private final Class<T> serviceType;
-
-    private final T invocationProxy;
-
-    private final Set<Class<? extends BaseIdentity>> contexts;
-
-    private final ListenerRegistry<RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> listeners;
-
-    private final Map<Class<? extends BaseIdentity>, RpcRoutingTableImpl<? extends BaseIdentity, T>> routingTables;
-
-    @SuppressWarnings("unchecked")
-    public RpcRouterCodegenInstance(final String name,final Class<T> type, final T routerImpl, final Iterable<Class<? extends BaseIdentity>> contexts) {
-        this.listeners = ListenerRegistry.create();
-        this.serviceType = type;
-        this.invocationProxy = routerImpl;
-        this.contexts = ImmutableSet.copyOf(contexts);
-        Map<Class<? extends BaseIdentity>, RpcRoutingTableImpl<? extends BaseIdentity, T>> mutableRoutingTables = new HashMap<>();
-        for (Class<? extends BaseIdentity> ctx : contexts) {
-            RpcRoutingTableImpl<? extends BaseIdentity, T> table = new RpcRoutingTableImpl<>(name,ctx,type);
-
-            @SuppressWarnings("rawtypes")
-            Map invokerView = table.getRoutes();
-
-            setRoutingTable(invocationProxy, ctx, invokerView);
-            mutableRoutingTables.put(ctx, table);
-            table.registerRouteChangeListener(this);
-        }
-        this.routingTables = ImmutableMap.copyOf(mutableRoutingTables);
-    }
-
-    @Override
-    public Class<T> getServiceType() {
-        return serviceType;
-    }
-
-    @Override
-    public T getInvocationProxy() {
-        return invocationProxy;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <C extends BaseIdentity> RpcRoutingTable<C, T> getRoutingTable(final Class<C> routeContext) {
-        return (RpcRoutingTable<C, T>) routingTables.get(routeContext);
-    }
-
-    @Override
-    public T getDefaultService() {
-        return RuntimeCodeHelper.getDelegate(invocationProxy);
-    }
-
-    @Override
-    public Set<Class<? extends BaseIdentity>> getContexts() {
-        return contexts;
-    }
-
-    @Override
-    public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
-            final L listener) {
-        return listeners.registerWithType(listener);
-    }
-
-    @Override
-    public void onRouteChange(final RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
-        for (ListenerRegistration<RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> listener : listeners) {
-            try {
-                listener.getInstance().onRouteChange(change);
-            } catch (Exception e) {
-                LOG.error("Error occured during invoker listener {}", listener.getInstance(), e);
-            }
-        }
-    }
-
-    @Override
-    public T getService(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
-        return routingTables.get(context).getRoute(path);
-    }
-
-    @Override
-    public RoutedRpcRegistration<T> addRoutedRpcImplementation(final T service) {
-        return new RoutedRpcRegistrationImpl(service);
-    }
-
-    public void removeDefaultImplementation(final T instance) {
-        RpcService current = RuntimeCodeHelper.getDelegate(invocationProxy);
-        if(instance == current) {
-            RuntimeCodeHelper.setDelegate(invocationProxy, null);
-        }
-    }
-
-    @Override
-    public RpcRegistration<T> registerDefaultService(final T service) {
-        RuntimeCodeHelper.setDelegate(invocationProxy, service);
-        return new DefaultRpcImplementationRegistration(service);
-    }
-
-    private final class RoutedRpcRegistrationImpl extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
-        /*
-         * FIXME: retaining this collection is not completely efficient. We really should be storing
-         *        a reference to this registration, as a particular listener may be registered multiple
-         *        times -- and then this goes kaboom in various aspects.
-         */
-        @GuardedBy("this")
-        private final Collection<Class<? extends BaseIdentity>> contexts = new ArrayList<>(1);
-
-        public RoutedRpcRegistrationImpl(final T instance) {
-            super(instance);
-        }
-
-        @Override
-        public Class<T> getServiceType() {
-            return serviceType;
-        }
-
-        @Override
-        public synchronized void registerPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
-            if (isClosed()) {
-                LOG.debug("Closed registration of {} ignoring new path {}", getInstance(), path);
-                return;
-            }
-
-            routingTables.get(context).updateRoute(path, getInstance());
-            contexts.add(context);
-        }
-
-        @Override
-        public synchronized void unregisterPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
-            if (isClosed()) {
-                LOG.debug("Closed unregistration of {} ignoring new path {}", getInstance(), path);
-                return;
-            }
-
-            routingTables.get(context).removeRoute(path, getInstance());
-            contexts.remove(context);
-        }
-
-        @Deprecated
-        @Override
-        public void registerInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
-            registerPath(context, instance);
-        }
-
-        @Deprecated
-        @Override
-        public void unregisterInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
-            unregisterPath(context, instance);
-        }
-
-        @Override
-        protected synchronized void removeRegistration() {
-            for (Class<? extends BaseIdentity> ctx : contexts) {
-                routingTables.get(ctx).removeAllReferences(getInstance());
-            }
-            contexts.clear();
-        }
-    }
-
-    private final class DefaultRpcImplementationRegistration extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
-
-
-        protected DefaultRpcImplementationRegistration(final T instance) {
-            super(instance);
-        }
-
-        @Override
-        protected void removeRegistration() {
-            removeDefaultImplementation(this.getInstance());
-        }
-
-        @Override
-        public Class<T> getServiceType() {
-            return serviceType;
-        }
-    }
-
-
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.java
deleted file mode 100644 (file)
index 78fa88b..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.binding.codegen.impl;
-
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
-import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRoutingTable;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.concepts.Mutable;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-final class RpcRoutingTableImpl<C extends BaseIdentity, S extends RpcService> implements
-        Mutable, //
-        RpcRoutingTable<C, S>, //
-        RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(RpcRoutingTableImpl.class);
-    private final String routerName;
-    private final Class<S> serviceType;
-
-    private final Class<C> contextType;
-    private final ConcurrentMap<InstanceIdentifier<?>, S> routes;
-    private final Map<InstanceIdentifier<?>, S> unmodifiableRoutes;
-
-    private RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> listener;
-    private S defaultRoute;
-
-    public RpcRoutingTableImpl(final String routerName,final Class<C> contextType, final Class<S> serviceType) {
-        super();
-        this.routerName = routerName;
-        this.serviceType = serviceType;
-        this.contextType = contextType;
-        this.routes = new ConcurrentHashMap<>();
-        this.unmodifiableRoutes = Collections.unmodifiableMap(routes);
-    }
-
-    @Override
-    public void setDefaultRoute(final S target) {
-        defaultRoute = target;
-    }
-
-    @Override
-    public S getDefaultRoute() {
-        return defaultRoute;
-    }
-
-    @Override
-    public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
-            final L listener) {
-        return new SingletonListenerRegistration<L>(listener);
-    }
-
-    @Override
-    public Class<C> getIdentifier() {
-        return contextType;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public void updateRoute(final InstanceIdentifier<?> path, final S service) {
-        S previous = this.routes.put(path, service);
-
-        LOGGER.debug("Route {} updated to {} in routing table {}",path,service,this);
-        @SuppressWarnings("rawtypes")
-        RouteChangeListener listenerCapture = listener;
-        if (previous == null && listenerCapture != null) {
-            listenerCapture.onRouteChange(RoutingUtils.announcementChange(contextType, path));
-        }
-    }
-
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public void removeRoute(final InstanceIdentifier<?> path) {
-        S previous = this.routes.remove(path);
-        LOGGER.debug("Route {} to {} removed in routing table {}",path,previous,this);
-        @SuppressWarnings("rawtypes")
-        RouteChangeListener listenerCapture = listener;
-        if (previous != null && listenerCapture != null) {
-            listenerCapture.onRouteChange(RoutingUtils.removalChange(contextType, path));
-        }
-    }
-
-    void removeRoute(final InstanceIdentifier<?> path, final S service) {
-        @SuppressWarnings("rawtypes")
-        RouteChangeListener listenerCapture = listener;
-        if (routes.remove(path, service) && listenerCapture != null) {
-            LOGGER.debug("Route {} to {} removed in routing table {}",path,service,this);
-            listenerCapture.onRouteChange(RoutingUtils.removalChange(contextType, path));
-        }
-    }
-
-    @Override
-    public S getRoute(final InstanceIdentifier<?> nodeInstance) {
-        S route = routes.get(nodeInstance);
-        if (route != null) {
-            return route;
-        }
-        return getDefaultRoute();
-    }
-
-    @Override
-    public Map<InstanceIdentifier<?>, S> getRoutes() {
-        return unmodifiableRoutes;
-    }
-
-    void removeAllReferences(final S service) {
-        // FIXME: replace this via properly-synchronized BiMap (or something)
-        final Iterator<S> it = routes.values().iterator();
-        while (it.hasNext()) {
-            final S s = it.next();
-            if (service.equals(s)) {
-                it.remove();
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "RpcRoutingTableImpl [router=" + routerName + ", service=" + serviceType.getSimpleName() + ", context="
-                + contextType.getSimpleName() + "]";
-    }
-
-    private class SingletonListenerRegistration<L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> extends
-            AbstractObjectRegistration<L>
-            implements ListenerRegistration<L> {
-
-        public SingletonListenerRegistration(final L instance) {
-            super(instance);
-            listener = instance;
-        }
-
-        @Override
-        protected void removeRegistration() {
-            listener = null;
-        }
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcServiceMetadata.java
deleted file mode 100644 (file)
index 430b7a7..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) 2013 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.binding.codegen.impl;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-
-import com.google.common.collect.Iterables;
-
-final class RpcServiceMetadata {
-    private final Set<Class<? extends BaseIdentity>> contexts = new HashSet<>();
-    private final Map<String, RpcMetadata> rpcMethods = new HashMap<>();
-    private final Iterable<Class<? extends BaseIdentity>> roContexts = Iterables.unmodifiableIterable(contexts);
-
-    public Iterable<Class<? extends BaseIdentity>> getContexts() {
-        return roContexts;
-    }
-
-    public RpcMetadata getRpcMethod(final String name) {
-        return rpcMethods.get(name);
-    }
-
-    public void addContext(final Class<? extends BaseIdentity> context) {
-        contexts.add(context);
-    }
-
-    public void addRpcMethod(final String name, final RpcMetadata routingPair) {
-        rpcMethods.put(name, routingPair);
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvoker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvoker.java
deleted file mode 100644 (file)
index 9302e49..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * Copyright (c) 2013 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.binding.codegen.impl;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import java.util.Set;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
-
-final class RuntimeGeneratedInvoker implements NotificationInvoker {
-    private final org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> invocationProxy;
-    private final RuntimeGeneratedInvokerPrototype prototype;
-    private final NotificationListener delegate;
-
-    @SuppressWarnings("unchecked")
-    private RuntimeGeneratedInvoker(final NotificationListener delegate, final RuntimeGeneratedInvokerPrototype prototype, final org.opendaylight.controller.sal.binding.api.NotificationListener<?> proxy) {
-        this.invocationProxy = (org.opendaylight.controller.sal.binding.api.NotificationListener<Notification>) proxy;
-        this.delegate = Preconditions.checkNotNull(delegate);
-        this.prototype = prototype;
-    }
-
-    public static RuntimeGeneratedInvoker create(final NotificationListener delegate, final RuntimeGeneratedInvokerPrototype prototype) throws InstantiationException, IllegalAccessException {
-        final org.opendaylight.controller.sal.binding.api.NotificationListener<?> proxy = Preconditions.checkNotNull(prototype.getProtoClass().newInstance());
-        RuntimeCodeHelper.setDelegate(proxy, delegate);
-        return new RuntimeGeneratedInvoker(delegate, prototype, proxy);
-    }
-
-    @Override
-    public NotificationListener getDelegate() {
-        return delegate;
-    }
-
-    @Override
-    public org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> getInvocationProxy() {
-        return invocationProxy;
-    }
-
-    @Override
-    public Set<Class<? extends Notification>> getSupportedNotifications() {
-        return prototype.getSupportedNotifications();
-    }
-
-    @Override
-    public void close() {
-        // Nothing to do
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + delegate.hashCode();
-        result = prime * result + invocationProxy.hashCode();
-        result = prime * result + prototype.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof RuntimeGeneratedInvoker)) {
-            return false;
-        }
-        final RuntimeGeneratedInvoker other = (RuntimeGeneratedInvoker) obj;
-        if (!delegate.equals(other.delegate)) {
-            return false;
-        }
-        if (!invocationProxy.equals(other.invocationProxy)) {
-            return false;
-        }
-        return prototype.equals(other.prototype);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this).toString();
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvokerPrototype.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeGeneratedInvokerPrototype.java
deleted file mode 100644 (file)
index 3172684..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (c) 2013 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.binding.codegen.impl;
-
-import java.util.Set;
-
-import org.opendaylight.controller.sal.binding.api.NotificationListener;
-import org.opendaylight.yangtools.yang.binding.Notification;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-
-final class RuntimeGeneratedInvokerPrototype {
-    private final Set<Class<? extends Notification>> supportedNotifications;
-    private final Class<? extends NotificationListener<?>> protoClass;
-
-    public RuntimeGeneratedInvokerPrototype(final Set<Class<? extends Notification>> supportedNotifications, final Class<? extends NotificationListener<?>> protoClass) {
-        this.supportedNotifications = Preconditions.checkNotNull(supportedNotifications);
-        this.protoClass = Preconditions.checkNotNull(protoClass);
-    }
-
-    public Set<Class<? extends Notification>> getSupportedNotifications() {
-        return supportedNotifications;
-    }
-
-    public Class<? extends NotificationListener<?>> getProtoClass() {
-        return protoClass;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + supportedNotifications.hashCode();
-        result = prime * result + protoClass.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof RuntimeGeneratedInvokerPrototype)) {
-            return false;
-        }
-        final RuntimeGeneratedInvokerPrototype other = (RuntimeGeneratedInvokerPrototype) obj;
-        if (!protoClass.equals(other.protoClass)) {
-            return false;
-        }
-        return supportedNotifications.equals(other.supportedNotifications);
-    }
-
-    @Override
-    public String toString() {
-        return Objects.toStringHelper(this)
-                .add("protoClass", protoClass)
-                .add("supportedNotifications", supportedNotifications)
-                .toString();
-    }
-}
index 4664b58d2a7d442e0ab20c59b32cedc7c1943696..6bcd74d213526b83c94b43087443e50ba9385da6 100644 (file)
@@ -22,8 +22,6 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import javassist.ClassPool;
 import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
 import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,10 +31,6 @@ public class SingletonHolder {
 
     public static final ClassPool CLASS_POOL = ClassPool.getDefault();
     public static final JavassistUtils JAVASSIST = JavassistUtils.forClassPool(CLASS_POOL);
-    public static final org.opendaylight.controller.sal.binding.codegen.impl.DefaultRuntimeCodeGenerator RPC_GENERATOR_IMPL = new org.opendaylight.controller.sal.binding.codegen.impl.DefaultRuntimeCodeGenerator(
-            CLASS_POOL);
-    public static final RuntimeCodeGenerator RPC_GENERATOR = RPC_GENERATOR_IMPL;
-    public static final NotificationInvokerFactory INVOKER_FACTORY = RPC_GENERATOR_IMPL.getInvokerFactory();
 
     public static final int CORE_NOTIFICATION_THREADS = 4;
     public static final int MAX_NOTIFICATION_THREADS = 32;
@@ -58,12 +52,12 @@ public class SingletonHolder {
 
         if (NOTIFICATION_EXECUTOR == null) {
             int queueSize = MAX_NOTIFICATION_QUEUE_SIZE;
-            String queueValue = System.getProperty(NOTIFICATION_QUEUE_SIZE_PROPERTY);
+            final String queueValue = System.getProperty(NOTIFICATION_QUEUE_SIZE_PROPERTY);
             if (StringUtils.isNotBlank(queueValue)) {
                 try {
                     queueSize = Integer.parseInt(queueValue);
                     logger.trace("Queue size was set to {}", queueSize);
-                } catch (NumberFormatException e) {
+                } catch (final NumberFormatException e) {
                     logger.warn("Cannot parse {} as set by {}, using default {}", queueValue,
                             NOTIFICATION_QUEUE_SIZE_PROPERTY, queueSize);
                 }
@@ -101,7 +95,7 @@ public class SingletonHolder {
                 public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
                     try {
                         executor.getQueue().put(r);
-                    } catch (InterruptedException e) {
+                    } catch (final InterruptedException e) {
                         throw new RejectedExecutionException("Interrupted while waiting on the queue", e);
                     }
                 }
@@ -120,7 +114,7 @@ public class SingletonHolder {
     @Deprecated
     public static synchronized ListeningExecutorService getDefaultCommitExecutor() {
         if (COMMIT_EXECUTOR == null) {
-            ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-commit-%d").build();
+            final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-commit-%d").build();
             /*
              * FIXME: this used to be newCacheThreadPool(), but MD-SAL does not have transaction
              *        ordering guarantees, which means that using a concurrent threadpool results
@@ -128,7 +122,7 @@ public class SingletonHolder {
              *        in inconsistent data being present. Once proper primitives are introduced,
              *        concurrency can be reintroduced.
              */
-            ExecutorService executor = Executors.newSingleThreadExecutor(factory);
+            final ExecutorService executor = Executors.newSingleThreadExecutor(factory);
             COMMIT_EXECUTOR = MoreExecutors.listeningDecorator(executor);
         }
 
@@ -137,7 +131,7 @@ public class SingletonHolder {
 
     public static ExecutorService getDefaultChangeEventExecutor() {
         if (CHANGE_EVENT_EXECUTOR == null) {
-            ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-change-%d").build();
+            final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-change-%d").build();
             /*
              * FIXME: this used to be newCacheThreadPool(), but MD-SAL does not have transaction
              *        ordering guarantees, which means that using a concurrent threadpool results
@@ -145,7 +139,7 @@ public class SingletonHolder {
              *        in inconsistent data being present. Once proper primitives are introduced,
              *        concurrency can be reintroduced.
              */
-            ExecutorService executor = Executors.newSingleThreadExecutor(factory);
+            final ExecutorService executor = Executors.newSingleThreadExecutor(factory);
             CHANGE_EVENT_EXECUTOR  = MoreExecutors.listeningDecorator(executor);
         }
 
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataBrokerImpl.java
deleted file mode 100644 (file)
index 4ed0ce9..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.binding.impl;
-
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
-import org.opendaylight.controller.md.sal.common.api.data.DataReader;
-import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter;
-import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataBroker;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-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.util.DataObjectReadingUtil;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.common.collect.Maps;
-
-@Deprecated
-public class DataBrokerImpl extends
-        AbstractDataBroker<InstanceIdentifier<? extends DataObject>, DataObject, DataChangeListener> //
-        implements DataProviderService, AutoCloseable {
-
-    private final static class ContainsWildcarded implements Predicate<InstanceIdentifier<? extends DataObject>> {
-
-        private final InstanceIdentifier<? extends DataObject> key;
-
-        public ContainsWildcarded(final InstanceIdentifier<? extends DataObject> key) {
-            this.key = key;
-        }
-
-        @Override
-        public boolean apply(final InstanceIdentifier<? extends DataObject> input) {
-            return key.containsWildcarded(input);
-        }
-    }
-
-    private final static class IsContainedWildcarded implements Predicate<InstanceIdentifier<? extends DataObject>> {
-
-        private final InstanceIdentifier<? extends DataObject> key;
-
-        public IsContainedWildcarded(final InstanceIdentifier<? extends DataObject> key) {
-            this.key = key;
-        }
-
-        @Override
-        public boolean apply(final InstanceIdentifier<? extends DataObject> input) {
-            return input.containsWildcarded(key);
-        }
-    }
-
-    private final AtomicLong nextTransaction = new AtomicLong();
-    private final AtomicLong createdTransactionsCount = new AtomicLong();
-    private final DelegatingDataReadRouter router = new DelegatingDataReadRouter();
-    private DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> rootCommitHandler;
-
-    public DataBrokerImpl() {
-        setDataReadRouter(router);
-    }
-
-    public void setDataReadDelegate(final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
-        router.setDelegate(delegate);
-    }
-
-    public AtomicLong getCreatedTransactionsCount() {
-        return createdTransactionsCount;
-    }
-
-    @Override
-    public DataTransactionImpl beginTransaction() {
-        String transactionId = "BA-" + nextTransaction.getAndIncrement();
-        createdTransactionsCount.getAndIncrement();
-        return new DataTransactionImpl(transactionId, this);
-    }
-
-    @Override
-    public void close() {
-
-    }
-
-    @Override
-    protected Predicate<InstanceIdentifier<? extends DataObject>> createContainsPredicate(
-            final InstanceIdentifier<? extends DataObject> key) {
-        return new ContainsWildcarded(key);
-    }
-
-    @Override
-    protected Predicate<InstanceIdentifier<? extends DataObject>> createIsContainedPredicate(
-            final InstanceIdentifier<? extends DataObject> key) {
-        return new IsContainedWildcarded(key);
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    protected Map<InstanceIdentifier<? extends DataObject>, DataObject> deepGetBySubpath(
-            final Map<InstanceIdentifier<? extends DataObject>, DataObject> dataSet,
-            final InstanceIdentifier<? extends DataObject> path) {
-        Builder<InstanceIdentifier<? extends DataObject>, DataObject> builder = ImmutableMap.builder();
-        Map<InstanceIdentifier<? extends DataObject>, DataObject> potential = Maps.filterKeys(dataSet,
-                createIsContainedPredicate(path));
-        for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : potential.entrySet()) {
-            try {
-                builder.putAll(DataObjectReadingUtil.readData(entry.getValue(), (InstanceIdentifier) entry.getKey(),
-                        path));
-            } catch (Exception e) {
-                // FIXME : Log exception;
-            }
-        }
-        return builder.build();
-
-    }
-
-    public class DelegatingDataReadRouter extends
-            AbstractDataReadRouter<InstanceIdentifier<? extends DataObject>, DataObject> {
-
-        private DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate;
-
-        @Override
-        public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
-            return delegate.readConfigurationData(path);
-        }
-
-        public void setDelegate(final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
-            this.delegate = delegate;
-        }
-
-        @Override
-        public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
-            return delegate.readOperationalData(path);
-        }
-
-        @Override
-        protected DataObject merge(final InstanceIdentifier<? extends DataObject> path, final Iterable<DataObject> data) {
-            throw new UnsupportedOperationException("Not supported");
-        }
-
-        @Override
-        public Registration registerConfigurationReader(
-                final InstanceIdentifier<? extends DataObject> path,
-                final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
-            throw new UnsupportedOperationException("Not supported");
-        }
-
-        @Override
-        public Registration registerOperationalReader(
-                final InstanceIdentifier<? extends DataObject> path,
-                final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
-            throw new UnsupportedOperationException("Not supported");
-        }
-    }
-
-    @Override
-    protected ImmutableList<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> affectedCommitHandlers(
-            final Set<InstanceIdentifier<? extends DataObject>> paths) {
-        ImmutableList.Builder<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> handlersBuilder = ImmutableList.builder();
-        return handlersBuilder //
-                .add(rootCommitHandler) //
-                .addAll(super.affectedCommitHandlers(paths)) //
-                .build();
-    }
-
-    public void setRootCommitHandler(final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
-        rootCommitHandler = commitHandler;
-    }
-
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataTransactionImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/DataTransactionImpl.java
deleted file mode 100644 (file)
index 4020232..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.binding.impl;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.util.ListenerRegistry;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-@Deprecated
-public class DataTransactionImpl extends AbstractDataTransaction<InstanceIdentifier<? extends DataObject>, DataObject>
-    implements DataModificationTransaction {
-    private final ListenerRegistry<DataTransactionListener> listeners = new ListenerRegistry<DataTransactionListener>();
-
-
-
-    public DataTransactionImpl(Object identifier,DataBrokerImpl dataBroker) {
-        super(identifier,dataBroker);
-    }
-
-    @Override
-    public ListenerRegistration<DataTransactionListener> registerListener(DataTransactionListener listener) {
-        return listeners.register(listener);
-    }
-
-    @Override
-    protected void onStatusChange(TransactionStatus status) {
-        for (ListenerRegistration<DataTransactionListener> listenerRegistration : listeners) {
-            listenerRegistration.getInstance().onStatusUpdated(this, status);
-        }
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java
deleted file mode 100644 (file)
index 0949d3d..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.impl;
-
-import static com.google.common.base.Preconditions.checkState;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.util.concurrent.UncheckedExecutionException;
-import java.util.EventListener;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
-import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
-import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
-import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper;
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.util.ListenerRegistry;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RpcProviderRegistryImpl implements RpcProviderRegistry, RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
-
-    private RuntimeCodeGenerator rpcFactory = SingletonHolder.RPC_GENERATOR_IMPL;
-
-    // cache of proxy objects where each value in the map corresponds to a specific RpcService
-    private final LoadingCache<Class<? extends RpcService>, RpcService> publicProxies = CacheBuilder.newBuilder().weakKeys().
-            build(new CacheLoader<Class<? extends RpcService>, RpcService>() {
-                @Override
-                public RpcService load(final Class<? extends RpcService> type) {
-                    final RpcService proxy = rpcFactory.getDirectProxyFor(type);
-                    LOG.debug("Created {} as public proxy for {} in {}", proxy, type.getSimpleName(), this);
-                    return proxy;
-                }
-            });
-
-    private final Cache<Class<? extends RpcService>, RpcRouter<?>> rpcRouters = CacheBuilder.newBuilder().weakKeys()
-            .build();
-
-    private final ListenerRegistry<RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> routeChangeListeners = ListenerRegistry
-            .create();
-    private final ListenerRegistry<RouterInstantiationListener> routerInstantiationListener = ListenerRegistry.create();
-
-    private final static Logger LOG = LoggerFactory.getLogger(RpcProviderRegistryImpl.class);
-
-    private final String name;
-
-    private final ListenerRegistry<GlobalRpcRegistrationListener> globalRpcListeners = ListenerRegistry.create();
-
-    public String getName() {
-        return name;
-    }
-
-    public RpcProviderRegistryImpl(final String name) {
-        super();
-        this.name = name;
-    }
-
-    @Override
-    public final <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type,
-            final T implementation) throws IllegalStateException {
-        return getRpcRouter(type).addRoutedRpcImplementation(implementation);
-    }
-
-    @Override
-    public final <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation) {
-
-        // FIXME: This should be well documented - addRpcImplementation for
-        // routed RPCs
-        try {
-            // Note: If RPC is really global, expected count of registrations
-            // of this method is really low.
-            RpcRouter<T> potentialRouter = getRpcRouter(type);
-            checkState(potentialRouter.getDefaultService() == null,
-                        "Default service for routed RPC already registered.");
-            return potentialRouter.registerDefaultService(implementation);
-        } catch (RpcIsNotRoutedException e) {
-            // NOOP - we could safely continue, since RPC is not routed
-            // so we fallback to global routing.
-            LOG.debug("RPC is not routed. Using global registration.",e);
-        }
-        T publicProxy = getRpcService(type);
-        RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy);
-        checkState(currentDelegate == null, "Rpc service is already registered");
-        LOG.debug("Registering {} as global implementation of {} in {}", implementation, type.getSimpleName(), this);
-        RuntimeCodeHelper.setDelegate(publicProxy, implementation);
-        notifyGlobalRpcAdded(type);
-        return new RpcProxyRegistration<T>(type, implementation, this);
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public final <T extends RpcService> T getRpcService(final Class<T> type) {
-        return (T) publicProxies.getUnchecked(type);
-    }
-
-
-    public <T extends RpcService> RpcRouter<T> getRpcRouter(final Class<T> type) {
-        try {
-            final AtomicBoolean created = new AtomicBoolean(false);
-            @SuppressWarnings( "unchecked")
-            // LoadingCache is unsuitable for RpcRouter since we need to distinguish
-            // first creation of RPC Router, so that is why
-            // we are using normal cache with load API and shared AtomicBoolean
-            // for this call, which will be set to true if router was created.
-            RpcRouter<T> router = (RpcRouter<T>) rpcRouters.get(type,new Callable<RpcRouter<?>>() {
-
-                @Override
-                public org.opendaylight.controller.sal.binding.api.rpc.RpcRouter<?> call()  {
-                    RpcRouter<?> router = rpcFactory.getRouterFor(type, name);
-                    router.registerRouteChangeListener(new RouteChangeForwarder<T>(type));
-                    LOG.debug("Registering router {} as global implementation of {} in {}", router, type.getSimpleName(), this);
-                    RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy());
-                    created.set(true);
-                    return router;
-                }
-            });
-            if(created.get()) {
-                notifyListenersRoutedCreated(router);
-            }
-            return router;
-        } catch (ExecutionException | UncheckedExecutionException e) {
-            // We rethrow Runtime Exceptions which were wrapped by
-            // Execution Exceptions
-            // otherwise we throw IllegalStateException with original
-            Throwables.propagateIfPossible(e.getCause());
-            throw new IllegalStateException("Could not load RPC Router for "+type.getName(),e);
-        }
-    }
-
-    private void notifyGlobalRpcAdded(final Class<? extends RpcService> type) {
-        for(ListenerRegistration<GlobalRpcRegistrationListener> listener : globalRpcListeners) {
-            try {
-                listener.getInstance().onGlobalRpcRegistered(type);
-            } catch (Exception e) {
-                LOG.error("Unhandled exception during invoking listener {}", e);
-            }
-        }
-
-    }
-
-    private void notifyListenersRoutedCreated(final RpcRouter<?> router) {
-
-        for (ListenerRegistration<RouterInstantiationListener> listener : routerInstantiationListener) {
-            try {
-                listener.getInstance().onRpcRouterCreated(router);
-            } catch (Exception e) {
-                LOG.error("Unhandled exception during invoking listener {}", e);
-            }
-        }
-
-    }
-
-    public ListenerRegistration<RouterInstantiationListener> registerRouterInstantiationListener(
-            final RouterInstantiationListener listener) {
-        ListenerRegistration<RouterInstantiationListener> reg = routerInstantiationListener.register(listener);
-        try {
-            for (RpcRouter<?> router : rpcRouters.asMap().values()) {
-                listener.onRpcRouterCreated(router);
-            }
-        } catch (Exception e) {
-            LOG.error("Unhandled exception during invoking listener {}", e);
-        }
-        return reg;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
-            final L listener) {
-        return (ListenerRegistration<L>) routeChangeListeners.register(listener);
-    }
-
-    public RuntimeCodeGenerator getRpcFactory() {
-        return rpcFactory;
-    }
-
-    public void setRpcFactory(final RuntimeCodeGenerator rpcFactory) {
-        this.rpcFactory = rpcFactory;
-    }
-
-    public interface RouterInstantiationListener extends EventListener {
-        void onRpcRouterCreated(RpcRouter<?> router);
-    }
-
-    public ListenerRegistration<GlobalRpcRegistrationListener> registerGlobalRpcRegistrationListener(final GlobalRpcRegistrationListener listener) {
-        return globalRpcListeners.register(listener);
-    }
-
-    public interface GlobalRpcRegistrationListener extends EventListener {
-        void onGlobalRpcRegistered(Class<? extends RpcService> cls);
-        void onGlobalRpcUnregistered(Class<? extends RpcService> cls);
-
-    }
-
-    private final class RouteChangeForwarder<T extends RpcService> implements RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
-        private final Class<T> type;
-
-        RouteChangeForwarder(final Class<T> type) {
-            this.type = type;
-        }
-
-        @Override
-        public void onRouteChange(final RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
-            Map<RpcContextIdentifier, Set<InstanceIdentifier<?>>> announcements = new HashMap<>();
-            for (Entry<Class<? extends BaseIdentity>, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements()
-                    .entrySet()) {
-                RpcContextIdentifier key = RpcContextIdentifier.contextFor(type, entry.getKey());
-                announcements.put(key, entry.getValue());
-            }
-            Map<RpcContextIdentifier, Set<InstanceIdentifier<?>>> removals = new HashMap<>();
-            for (Entry<Class<? extends BaseIdentity>, Set<InstanceIdentifier<?>>> entry : change.getRemovals()
-                    .entrySet()) {
-                RpcContextIdentifier key = RpcContextIdentifier.contextFor(type, entry.getKey());
-                removals.put(key, entry.getValue());
-            }
-            RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> toPublish = RoutingUtils
-                    .<RpcContextIdentifier, InstanceIdentifier<?>> change(announcements, removals);
-            for (ListenerRegistration<RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> listener : routeChangeListeners) {
-                try {
-                    listener.getInstance().onRouteChange(toPublish);
-                } catch (Exception e) {
-                    LOG.error("Unhandled exception during invoking listener",listener.getInstance(),e);
-                }
-            }
-        }
-    }
-
-    private static final class RpcProxyRegistration<T extends RpcService> extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
-        private final RpcProviderRegistryImpl registry;
-        private final Class<T> serviceType;
-
-        RpcProxyRegistration(final Class<T> type, final T service, final RpcProviderRegistryImpl registry) {
-            super(service);
-            this.registry =  Preconditions.checkNotNull(registry);
-            this.serviceType = type;
-        }
-
-        @Override
-        public Class<T> getServiceType() {
-            return serviceType;
-        }
-
-        @Override
-        protected void removeRegistration() {
-            T publicProxy = registry.getRpcService(serviceType);
-            RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy);
-            if (currentDelegate == getInstance()) {
-                RuntimeCodeHelper.setDelegate(publicProxy, null);
-            }
-        }
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProxyContext.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProxyContext.java
deleted file mode 100644 (file)
index 3b6a253..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2013 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.binding.impl;
-
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.osgi.framework.ServiceRegistration;
-
-@SuppressWarnings("all")
-public class RpcProxyContext {
-  public RpcProxyContext(final Class<? extends RpcService> proxyClass) {
-    this.proxyClass = proxyClass;
-  }
-
-  protected final Class<? extends RpcService> proxyClass;
-
-  protected RpcService _proxy;
-
-  public RpcService getProxy() {
-    return this._proxy;
-  }
-
-  public void setProxy(final RpcService proxy) {
-    this._proxy = proxy;
-  }
-
-  protected ServiceRegistration<? extends RpcService> _registration;
-
-  public ServiceRegistration<? extends RpcService> getRegistration() {
-    return this._registration;
-  }
-
-  public void setRegistration(final ServiceRegistration<? extends RpcService> registration) {
-    this._registration = registration;
-  }
-}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/BindingAwareDataReaderRouter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/BindingAwareDataReaderRouter.java
deleted file mode 100644 (file)
index 97f74ff..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.binding.impl.util;
-
-import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class BindingAwareDataReaderRouter extends AbstractDataReadRouter<InstanceIdentifier<? extends DataObject>,DataObject> {
-    @Override
-    protected DataObject merge(final InstanceIdentifier<? extends DataObject> path, final Iterable<DataObject> data) {
-        return data.iterator().next();
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/NotificationInvokerFactory.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/NotificationInvokerFactory.java
deleted file mode 100644 (file)
index 28e552e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.binding.spi;
-
-import java.util.Set;
-
-import org.opendaylight.controller.sal.binding.api.NotificationListener;
-import org.opendaylight.yangtools.yang.binding.Notification;
-
-public interface NotificationInvokerFactory {
-
-    NotificationInvoker invokerFor(org.opendaylight.yangtools.yang.binding.NotificationListener instance);
-
-    public interface NotificationInvoker {
-
-        Set<Class<? extends Notification>> getSupportedNotifications();
-
-        NotificationListener<Notification> getInvocationProxy();
-
-        public abstract void close();
-
-        org.opendaylight.yangtools.yang.binding.NotificationListener getDelegate();
-
-    }
-}
index ee130fdeeb87502a8a15780264e0dbfd0d255280..866cb844d189711458785614edaca761f2f3f850 100644 (file)
@@ -217,6 +217,29 @@ module opendaylight-sal-binding-broker-impl {
         }
     }
 
+    augment "/config:modules/config:module/config:configuration" {
+        case binding-notification-broker {
+            when "/config:modules/config:module/config:type = 'binding-notification-broker'";
+            container notification-adapter {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity binding-new-notification-service;
+                    }
+                }
+            }
+
+            container notification-publish-adapter {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity binding-new-notification-publish-service;
+                    }
+                }
+            }
+        }
+    }
+
     augment "/config:modules/config:module/config:state" {
         case binding-notification-broker {
             when "/config:modules/config:module/config:type = 'binding-notification-broker'";
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/RpcProviderRegistryTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/RpcProviderRegistryTest.java
deleted file mode 100644 (file)
index de5f3ca..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-package org.opendaylight.controller.md.sal.binding.impl.test;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
-import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
-import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
-
-import com.google.common.base.Throwables;
-import java.util.Arrays;
-import javassist.ClassPool;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.controller.md.sal.binding.compat.HeliumRpcProviderRegistry;
-import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
-import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcServiceAdapter;
-import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
-import org.opendaylight.controller.md.sal.binding.test.AbstractSchemaAwareTest;
-import org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
-import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
-import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
-import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.OpendaylightTestRoutedRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.TestContext;
-import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
-import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
-import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
-import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
-import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
-import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-
-public class RpcProviderRegistryTest  extends AbstractSchemaAwareTest {
-
-    private static InstanceIdentifier<TopLevelList> FOO_PATH = path(TOP_FOO_KEY);
-    private static InstanceIdentifier<TopLevelList> BAR_PATH = path(TOP_BAR_KEY);
-    private static RpcContextIdentifier ROUTING_CONTEXT = RpcContextIdentifier.contextFor(OpendaylightTestRoutedRpcService.class, TestContext.class);
-
-    private RpcProviderRegistry rpcRegistry;
-
-
-    @Override
-    protected Iterable<YangModuleInfo> getModuleInfos() {
-        try {
-            return Arrays.asList(
-                    BindingReflections.getModuleInfo(TopLevelList.class),
-                    BindingReflections.getModuleInfo(OpendaylightTestRoutedRpcService.class),
-                    BindingReflections.getModuleInfo(OpendaylightTestRpcServiceService.class));
-        } catch (final Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    @Override
-    protected void setupWithSchema(final SchemaContext context) {
-        final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault()));
-        final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator);
-        final GeneratedClassLoadingStrategy classLoadingStrategy = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
-        final BindingToNormalizedNodeCodec codec = new BindingToNormalizedNodeCodec(classLoadingStrategy, codecRegistry);
-        final DOMRpcRouter domRpcRegistry = new DOMRpcRouter();
-        domRpcRegistry.onGlobalContextUpdated(context);
-        codec.onGlobalContextUpdated(context);
-        final RpcConsumerRegistry consumer = new BindingDOMRpcServiceAdapter(domRpcRegistry, codec);
-        final BindingDOMRpcProviderServiceAdapter provider = new BindingDOMRpcProviderServiceAdapter( domRpcRegistry,codec);
-        rpcRegistry = new HeliumRpcProviderRegistry(consumer,provider);
-    }
-
-    @Test
-    public void testGlobalRpcRegistrations() throws Exception {
-        final OpendaylightTestRpcServiceService one = Mockito.mock(OpendaylightTestRpcServiceService.class);
-        final OpendaylightTestRpcServiceService two = Mockito.mock(OpendaylightTestRpcServiceService.class);
-
-        final RpcRegistration<OpendaylightTestRpcServiceService> regOne = rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, one);
-        assertNotNull(regOne);
-        rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, two);
-        regOne.close();
-        final RpcRegistration<OpendaylightTestRpcServiceService> regTwo = rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, two);
-        assertNotNull(regTwo);
-    }
-
-
-    @Test
-    @Ignore
-    public void nonRoutedRegisteredAsRouted() {
-        final OpendaylightTestRpcServiceService one = Mockito.mock(OpendaylightTestRpcServiceService.class);
-        try {
-            final RoutedRpcRegistration<OpendaylightTestRpcServiceService> reg = rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRpcServiceService.class, one);
-            reg.registerPath(null, BAR_PATH);
-            fail("RpcIsNotRoutedException should be thrown");
-        } catch (final RpcIsNotRoutedException e) {
-            assertNotNull(e.getMessage());
-        } catch (final Exception e) {
-            fail("RpcIsNotRoutedException should be thrown");
-        }
-
-    }
-
-}
index 547d3498c0e7bdec59b054203d3a56682a393c05..2647477c0f3e316f51983b0ad81ac9a5fb353310 100644 (file)
@@ -23,7 +23,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
 import org.opendaylight.controller.md.sal.dom.broker.impl.SerializedDOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
 import org.opendaylight.controller.sal.binding.test.util.MockSchemaService;
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
@@ -77,8 +76,7 @@ public class DataBrokerTestCustomizer {
     }
 
     public NotificationService createNotificationService() {
-        return new BindingDOMNotificationServiceAdapter(bindingToNormalized.getCodecRegistry(), domNotificationRouter,
-                SingletonHolder.INVOKER_FACTORY);
+        return new BindingDOMNotificationServiceAdapter(bindingToNormalized.getCodecRegistry(), domNotificationRouter);
     }
 
     public NotificationPublishService createNotificationPublishService() {
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/codegen/impl/DefaultRuntimeCodeGeneratorTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/codegen/impl/DefaultRuntimeCodeGeneratorTest.java
deleted file mode 100644 (file)
index 7a15619..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * 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.binding.codegen.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import java.util.ArrayList;
-import java.util.List;
-import javassist.ClassPool;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRoutingTable;
-import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
-import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker;
-import org.opendaylight.controller.sal.binding.test.mock.BarListener;
-import org.opendaylight.controller.sal.binding.test.mock.BarUpdate;
-import org.opendaylight.controller.sal.binding.test.mock.FlowDelete;
-import org.opendaylight.controller.sal.binding.test.mock.FooListener;
-import org.opendaylight.controller.sal.binding.test.mock.FooService;
-import org.opendaylight.controller.sal.binding.test.mock.FooUpdate;
-import org.opendaylight.controller.sal.binding.test.mock.ReferencableObject;
-import org.opendaylight.controller.sal.binding.test.mock.ReferencableObjectKey;
-import org.opendaylight.controller.sal.binding.test.mock.SimpleInput;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class DefaultRuntimeCodeGeneratorTest {
-
-    private RuntimeCodeGenerator codeGenerator;
-    private NotificationInvokerFactory invokerFactory;
-
-    @Before
-    public void initialize() {
-        this.codeGenerator = new DefaultRuntimeCodeGenerator(ClassPool.getDefault());
-        this.invokerFactory = codeGenerator.getInvokerFactory();
-    }
-
-    @Test
-    public void testGenerateDirectProxy() {
-        FooService product = codeGenerator.getDirectProxyFor(FooService.class);
-        assertNotNull(product);
-    }
-
-    @Test
-    public void testGenerateRouter() throws Exception {
-        RpcRouter<FooService> product = codeGenerator.getRouterFor(FooService.class,"test");
-        assertNotNull(product);
-        assertNotNull(product.getInvocationProxy());
-
-        assertEquals("2 fields should be generated.", 2, product.getInvocationProxy().getClass().getFields().length);
-
-        verifyRouting(product);
-    }
-
-    @Test
-    public void testInvoker() throws Exception {
-
-        FooListenerImpl fooListener = new FooListenerImpl();
-
-        NotificationInvoker invokerFoo = invokerFactory.invokerFor(fooListener);
-
-
-        assertSame(fooListener,invokerFoo.getDelegate());
-        assertNotNull(invokerFoo.getSupportedNotifications());
-        assertEquals(1, invokerFoo.getSupportedNotifications().size());
-        assertNotNull(invokerFoo.getInvocationProxy());
-
-        FooUpdateImpl fooOne = new FooUpdateImpl();
-        invokerFoo.getInvocationProxy().onNotification(fooOne);
-
-        assertEquals(1, fooListener.receivedFoos.size());
-        assertSame(fooOne, fooListener.receivedFoos.get(0));
-
-        CompositeListenerImpl composite = new CompositeListenerImpl();
-
-        NotificationInvoker invokerComposite = invokerFactory.invokerFor(composite);
-
-        assertNotNull(invokerComposite.getSupportedNotifications());
-        assertEquals(3, invokerComposite.getSupportedNotifications().size());
-        assertNotNull(invokerComposite.getInvocationProxy());
-
-        invokerComposite.getInvocationProxy().onNotification(fooOne);
-
-        assertEquals(1, composite.receivedFoos.size());
-        assertSame(fooOne, composite.receivedFoos.get(0));
-
-        assertEquals(0, composite.receivedBars.size());
-
-        BarUpdateImpl barOne = new BarUpdateImpl();
-
-        invokerComposite.getInvocationProxy().onNotification(barOne);
-
-        assertEquals(1, composite.receivedFoos.size());
-        assertEquals(1, composite.receivedBars.size());
-        assertSame(barOne, composite.receivedBars.get(0));
-
-    }
-
-    private void verifyRouting(final RpcRouter<FooService> product) {
-        assertNotNull("Routing table should be initialized", product.getRoutingTable(BaseIdentity.class));
-
-        RpcRoutingTable<BaseIdentity, FooService> routingTable = product.getRoutingTable(BaseIdentity.class);
-
-        int servicesCount = 2;
-        int instancesPerService = 3;
-
-        InstanceIdentifier<?>[][] identifiers = identifiers(servicesCount, instancesPerService);
-        FooService service[] = new FooService[] { mock(FooService.class, "Instance 0"),
-                mock(FooService.class, "Instance 1") };
-
-        for (int i = 0; i < service.length; i++) {
-            for (InstanceIdentifier<?> instance : identifiers[i]) {
-                routingTable.updateRoute(instance, service[i]);
-            }
-        }
-
-        assertEquals("All instances should be registered.", servicesCount * instancesPerService, routingTable
-                .getRoutes().size());
-
-        SimpleInput[] instance_0_input = new SimpleInputImpl[] { new SimpleInputImpl(identifiers[0][0]),
-                new SimpleInputImpl(identifiers[0][1]), new SimpleInputImpl(identifiers[0][2]) };
-
-        SimpleInput[] instance_1_input = new SimpleInputImpl[] { new SimpleInputImpl(identifiers[1][0]),
-                new SimpleInputImpl(identifiers[1][1]), new SimpleInputImpl(identifiers[1][2]) };
-
-        // We test sending mock messages
-
-        product.getInvocationProxy().simple(instance_0_input[0]);
-        verify(service[0]).simple(instance_0_input[0]);
-
-        product.getInvocationProxy().simple(instance_0_input[1]);
-        product.getInvocationProxy().simple(instance_0_input[2]);
-
-        verify(service[0]).simple(instance_0_input[1]);
-        verify(service[0]).simple(instance_0_input[2]);
-
-        product.getInvocationProxy().simple(instance_1_input[0]);
-
-        // We should have call to instance 1
-        verify(service[1]).simple(instance_1_input[0]);
-
-        /*
-         * Generated RPC service should throw illegalArgumentException
-         * with message if rpc input is null.
-         */
-        try {
-            product.getInvocationProxy().simple(null);
-            fail("Generated RPC router should throw IllegalArgumentException on null input");
-        } catch (IllegalArgumentException e){
-            assertNotNull(e.getMessage());
-        }
-
-
-        /*
-         * Generated RPC service should throw illegalArgumentException
-         * with message if rpc route is null.
-         */
-        try {
-            SimpleInput withoutValue = new SimpleInputImpl(null);
-            product.getInvocationProxy().simple(withoutValue);
-            fail("Generated RPC router should throw IllegalArgumentException on null value for route");
-        } catch (IllegalArgumentException e){
-            assertNotNull(e.getMessage());
-        }
-
-    }
-
-    private InstanceIdentifier<?>[][] identifiers(final int serviceSize, final int instancesPerService) {
-        InstanceIdentifier<?>[][] ret = new InstanceIdentifier[serviceSize][];
-        int service = 0;
-        for (int i = 0; i < serviceSize; i++) {
-
-            InstanceIdentifier<?>[] instanceIdentifiers = new InstanceIdentifier[instancesPerService];
-            ret[i] = instanceIdentifiers;
-            for (int id = 0; id < instancesPerService; id++) {
-                instanceIdentifiers[id] = referencableIdentifier(service * instancesPerService + id);
-            }
-            service++;
-        }
-
-        return ret;
-    }
-
-    private InstanceIdentifier<?> referencableIdentifier(final int i) {
-        return InstanceIdentifier.builder(ReferencableObject.class, new ReferencableObjectKey(i)).build();
-    }
-
-    private static class SimpleInputImpl implements SimpleInput {
-        private final InstanceIdentifier<?> identifier;
-
-        public SimpleInputImpl(final InstanceIdentifier<?> _identifier) {
-            this.identifier = _identifier;
-        }
-
-        @Override
-        public <E extends Augmentation<SimpleInput>> E getAugmentation(final Class<E> augmentationType) {
-            return null;
-        }
-
-        @Override
-        public InstanceIdentifier<?> getIdentifier() {
-            return this.identifier;
-        }
-
-        @Override
-        public Class<? extends DataObject> getImplementedInterface() {
-            return SimpleInput.class;
-        }
-    }
-
-    private static class FooUpdateImpl implements FooUpdate {
-        @Override
-        public Class<? extends DataContainer> getImplementedInterface() {
-            return FooUpdate.class;
-        }
-    }
-
-    private static class BarUpdateImpl implements BarUpdate {
-        @Override
-        public Class<? extends DataContainer> getImplementedInterface() {
-            return BarUpdate.class;
-        }
-
-        @Override
-        public InstanceIdentifier<?> getInheritedIdentifier() {
-            return null;
-        }
-    }
-
-    private static class FooListenerImpl implements FooListener {
-
-        List<FooUpdate> receivedFoos = new ArrayList<>();
-
-        @Override
-        public void onFooUpdate(final FooUpdate notification) {
-            receivedFoos.add(notification);
-        }
-
-    }
-
-    private static class CompositeListenerImpl extends FooListenerImpl implements BarListener {
-
-        List<BarUpdate> receivedBars = new ArrayList<>();
-        List<FlowDelete> receivedDeletes = new ArrayList<>();
-
-        @Override
-        public void onBarUpdate(final BarUpdate notification) {
-            receivedBars.add(notification);
-        }
-
-        @Override
-        public void onFlowDelete(final FlowDelete notification) {
-            receivedDeletes.add(notification);
-        }
-
-    }
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolderTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolderTest.java
deleted file mode 100644 (file)
index 0e4c5cc..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.binding.codegen.impl;
-
-import com.google.common.util.concurrent.ListeningExecutorService;
-import java.lang.reflect.Field;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Ignore
-public class SingletonHolderTest {
-    private static final Logger logger = LoggerFactory.getLogger(SingletonHolderTest.class);
-
-    @Test
-    public void testNotificationExecutor() throws Exception {
-        ListeningExecutorService executor = SingletonHolder.getDefaultNotificationExecutor();
-        ThreadPoolExecutor tpExecutor = (ThreadPoolExecutor) setAccessible(executor.getClass().getDeclaredField("delegate")).get(executor);
-        BlockingQueue<Runnable> queue = tpExecutor.getQueue();
-
-        for (int idx = 0; idx < 100; idx++) {
-            final int idx2 = idx;
-            logger.info("Adding {}\t{}\t{}", idx, queue.size(), tpExecutor.getActiveCount());
-            executor.execute(new Runnable() {
-
-                @Override
-                public void run() {
-                    logger.info("in  {}", idx2);
-                    try {
-                        Thread.sleep(1000);
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                    logger.info("out {}", idx2);
-                }
-            });
-        }
-        executor.shutdown();
-        executor.awaitTermination(10, TimeUnit.SECONDS);
-    }
-
-    private static Field setAccessible(Field field) {
-        field.setAccessible(true);
-        return field;
-    }
-}
index a439e9ea262bfdfcf321367bc78c7a6eca1b0509..1203a72dc3616805c009d3a30a74edf3630a5e53 100644 (file)
@@ -20,19 +20,27 @@ import com.google.common.util.concurrent.MoreExecutors;
 import javassist.ClassPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumNotificationProviderServiceAdapter;
 import org.opendaylight.controller.md.sal.binding.compat.HeliumRpcProviderRegistry;
 import org.opendaylight.controller.md.sal.binding.compat.HydrogenDataBrokerAdapter;
 import org.opendaylight.controller.md.sal.binding.compat.HydrogenMountProvisionServiceAdapter;
 import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
 import org.opendaylight.controller.md.sal.binding.impl.BindingDOMMountPointServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
 import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
 import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcServiceAdapter;
 import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
 import org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter;
 import org.opendaylight.controller.md.sal.dom.broker.impl.SerializedDOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
@@ -41,7 +49,6 @@ import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
 import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
-import org.opendaylight.controller.sal.binding.impl.NotificationBrokerImpl;
 import org.opendaylight.controller.sal.binding.impl.RootBindingAwareBroker;
 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
 import org.opendaylight.controller.sal.core.api.BrokerService;
@@ -65,7 +72,7 @@ public class BindingTestContext implements AutoCloseable {
 
     private RootBindingAwareBroker baBrokerImpl;
 
-    private NotificationBrokerImpl baNotifyImpl;
+    private HeliumNotificationProviderServiceAdapter baNotifyImpl;
 
 
     private BrokerImpl biBrokerImpl;
@@ -93,6 +100,14 @@ public class BindingTestContext implements AutoCloseable {
     private BindingDOMRpcProviderServiceAdapter baProviderRpc;
     private DOMRpcRouter domRouter;
 
+    private NotificationPublishService publishService;
+
+    private NotificationService listenService;
+
+    private DOMNotificationPublishService domPublishService;
+
+    private DOMNotificationService domListenService;
+
 
 
     public DOMDataBroker getDomAsyncDataBroker() {
@@ -249,7 +264,12 @@ public class BindingTestContext implements AutoCloseable {
 
     public void startBindingNotificationBroker() {
         checkState(executor != null);
-        baNotifyImpl = new NotificationBrokerImpl(executor);
+        final DOMNotificationRouter router = DOMNotificationRouter.create(16);
+        domPublishService = router;
+        domListenService = router;
+        publishService = new BindingDOMNotificationPublishServiceAdapter(codec, domPublishService);
+        listenService = new BindingDOMNotificationServiceAdapter(codec, domListenService);
+        baNotifyImpl = new HeliumNotificationProviderServiceAdapter(publishService,listenService);
 
     }
 
index f9c4a043e566b00eabe5b76db8b9809e53a79f21..7bfe254b17e9aa22b3d4ea80c0d664db510acfe9 100644 (file)
                 <module>
                             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
                             <name>binding-notification-broker</name>
+                    <notification-adapter xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                         <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-new-notification-service</type>
+                         <name>binding-notification-adapter</name>
+                    </notification-adapter>
+                    <notification-publish-adapter xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                         <type  xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-new-notification-publish-service</type>
+                         <name>binding-notification-publish-adapter</name>
+                    </notification-publish-adapter>
                         </module>
                         <module>
                             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
                         <module>
                             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-inmemory-data-broker</type>
                             <name>inmemory-data-broker</name>
+
                             <schema-service>
                                 <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
                                 <name>yang-schema-service</name>
                             </schema-service>
+
+                   <config-data-store>
+                        <type xmlns:config-dom-store-spi="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:config-dom-store">config-dom-store-spi:config-dom-datastore</type>
+                        <name>config-store-service</name>
+                    </config-data-store>
+
+                    <operational-data-store>
+                        <type xmlns:operational-dom-store-spi="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:operational-dom-store">operational-dom-store-spi:operational-dom-datastore</type>
+                        <name>operational-store-service</name>
+                    </operational-data-store>
                         </module>
                         <module>
                             <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractDataTreeCandidateNode.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractDataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..c3940e5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * Abstract base class for our internal implementation of {@link DataTreeCandidateNode},
+ * which we instantiate from a serialized stream. We do not retain the before-image and
+ * do not implement {@link #getModifiedChild(PathArgument)}, as that method is only
+ * useful for end users. Instances based on this class should never be leaked outside of
+ * this component.
+ */
+abstract class AbstractDataTreeCandidateNode implements DataTreeCandidateNode {
+    private final ModificationType type;
+
+    protected AbstractDataTreeCandidateNode(final ModificationType type) {
+        this.type = Preconditions.checkNotNull(type);
+    }
+
+    @Override
+    public final DataTreeCandidateNode getModifiedChild(final PathArgument identifier) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public final ModificationType getModificationType() {
+        return type;
+    }
+
+    @Override
+    public final Optional<NormalizedNode<?, ?>> getDataBefore() {
+        throw new UnsupportedOperationException("Before-image not available after serialization");
+    }
+
+    static DataTreeCandidateNode createUnmodified() {
+        return new AbstractDataTreeCandidateNode(ModificationType.UNMODIFIED) {
+            @Override
+            public PathArgument getIdentifier() {
+                throw new UnsupportedOperationException("Root node does not have an identifier");
+            }
+
+            @Override
+            public Optional<NormalizedNode<?, ?>> getDataAfter() {
+                throw new UnsupportedOperationException("After-image not available after serialization");
+            }
+
+            @Override
+            public Collection<DataTreeCandidateNode> getChildNodes() {
+                throw new UnsupportedOperationException("Children not available after serialization");
+            }
+        };
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ChainedCommitCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ChainedCommitCohort.java
new file mode 100644 (file)
index 0000000..4b471cf
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ChainedCommitCohort extends ShardDataTreeCohort {
+    private static final Logger LOG = LoggerFactory.getLogger(ChainedCommitCohort.class);
+    private final ReadWriteShardDataTreeTransaction transaction;
+    private final ShardDataTreeTransactionChain chain;
+    private final ShardDataTreeCohort delegate;
+
+    ChainedCommitCohort(final ShardDataTreeTransactionChain chain, final ReadWriteShardDataTreeTransaction transaction, final ShardDataTreeCohort delegate) {
+        this.transaction = Preconditions.checkNotNull(transaction);
+        this.delegate = Preconditions.checkNotNull(delegate);
+        this.chain = Preconditions.checkNotNull(chain);
+    }
+
+    @Override
+    public ListenableFuture<Void> commit() {
+        final ListenableFuture<Void> ret = delegate.commit();
+
+        Futures.addCallback(ret, new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(Void result) {
+                chain.clearTransaction(transaction);
+                LOG.debug("Committed transaction {}", transaction);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("Transaction {} commit failed, cannot recover", transaction, t);
+            }
+        });
+
+        return ret;
+    }
+
+    @Override
+    public ListenableFuture<Boolean> canCommit() {
+        return delegate.canCommit();
+    }
+
+    @Override
+    public ListenableFuture<Void> preCommit() {
+        return delegate.preCommit();
+    }
+
+    @Override
+    public ListenableFuture<Void> abort() {
+        return delegate.abort();
+    }
+
+    @Override
+    DataTreeCandidateTip getCandidate() {
+        return delegate.getCandidate();
+    }
+}
\ No newline at end of file
index f1f33bfc9340d3ef7ab73711b6b69b176eccd1ba..9a800c1659b624132cbdb5fe22435aeda9c7a626 100644 (file)
@@ -7,9 +7,9 @@
  */
 package org.opendaylight.controller.cluster.datastore;
 
-import akka.actor.ActorSelection;
 import akka.dispatch.OnComplete;
 import java.util.List;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,7 +59,7 @@ final class ChainedTransactionProxy extends TransactionProxy {
      * previous Tx's ready operations haven't completed yet.
      */
     @Override
-    protected Future<ActorSelection> sendFindPrimaryShardAsync(final String shardName) {
+    protected Future<PrimaryShardInfo> sendFindPrimaryShardAsync(final String shardName) {
         // Check if there are any previous ready Futures, otherwise let the super class handle it.
         if(previousReadyFutures.isEmpty()) {
             return super.sendFindPrimaryShardAsync(shardName);
@@ -75,7 +75,7 @@ final class ChainedTransactionProxy extends TransactionProxy {
                 previousReadyFutures, getActorContext().getClientDispatcher());
 
         // Add a callback for completion of the combined Futures.
-        final Promise<ActorSelection> returnPromise = akka.dispatch.Futures.promise();
+        final Promise<PrimaryShardInfo> returnPromise = akka.dispatch.Futures.promise();
         OnComplete<Iterable<Object>> onComplete = new OnComplete<Iterable<Object>>() {
             @Override
             public void onComplete(Throwable failure, Iterable<Object> notUsed) {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeCandidatePayload.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeCandidatePayload.java
new file mode 100644 (file)
index 0000000..54167b2
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+import com.google.protobuf.GeneratedMessage.GeneratedExtension;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeInputStreamReader;
+import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeOutputStreamWriter;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
+import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages.AppendEntries;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class DataTreeCandidatePayload extends Payload implements Externalizable {
+    private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidatePayload.class);
+    private static final long serialVersionUID = 1L;
+    private static final byte DELETE = 0;
+    private static final byte SUBTREE_MODIFIED = 1;
+    private static final byte UNMODIFIED = 2;
+    private static final byte WRITE = 3;
+
+    private transient byte[] serialized;
+
+    public DataTreeCandidatePayload() {
+        // Required by Externalizable
+    }
+
+    private DataTreeCandidatePayload(final byte[] serialized) {
+        this.serialized = Preconditions.checkNotNull(serialized);
+    }
+
+    private static void writeChildren(final NormalizedNodeOutputStreamWriter writer, final DataOutput out,
+            final Collection<DataTreeCandidateNode> children) throws IOException {
+        out.writeInt(children.size());
+        for (DataTreeCandidateNode child : children) {
+            writeNode(writer, out, child);
+        }
+    }
+
+    private static void writeNode(final NormalizedNodeOutputStreamWriter writer, final DataOutput out,
+            final DataTreeCandidateNode node) throws IOException {
+        switch (node.getModificationType()) {
+        case DELETE:
+            out.writeByte(DELETE);
+            writer.writePathArgument(node.getIdentifier());
+            break;
+        case SUBTREE_MODIFIED:
+            out.writeByte(SUBTREE_MODIFIED);
+            writer.writePathArgument(node.getIdentifier());
+            writeChildren(writer, out, node.getChildNodes());
+            break;
+        case WRITE:
+            out.writeByte(WRITE);
+            writer.writeNormalizedNode(node.getDataAfter().get());
+            break;
+        case UNMODIFIED:
+            throw new IllegalArgumentException("Unmodified candidate should never be in the payload");
+        default:
+            throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
+        }
+    }
+
+    static DataTreeCandidatePayload create(DataTreeCandidate candidate) {
+        final ByteArrayDataOutput out = ByteStreams.newDataOutput();
+        try (final NormalizedNodeOutputStreamWriter writer = new NormalizedNodeOutputStreamWriter(out)) {
+            writer.writeYangInstanceIdentifier(candidate.getRootPath());
+
+            final DataTreeCandidateNode node = candidate.getRootNode();
+            switch (node.getModificationType()) {
+            case DELETE:
+                out.writeByte(DELETE);
+                break;
+            case SUBTREE_MODIFIED:
+                out.writeByte(SUBTREE_MODIFIED);
+                writeChildren(writer, out, node.getChildNodes());
+                break;
+            case UNMODIFIED:
+                out.writeByte(UNMODIFIED);
+                break;
+            case WRITE:
+                out.writeByte(WRITE);
+                writer.writeNormalizedNode(node.getDataAfter().get());
+                break;
+            default:
+                throw new IllegalArgumentException("Unhandled node type " + node.getModificationType());
+            }
+
+            writer.close();
+        } catch (IOException e) {
+            throw new IllegalArgumentException(String.format("Failed to serialize candidate %s", candidate), e);
+        }
+
+        return new DataTreeCandidatePayload(out.toByteArray());
+    }
+
+    private static Collection<DataTreeCandidateNode> readChildren(final NormalizedNodeInputStreamReader reader,
+        final DataInput in) throws IOException {
+        final int size = in.readInt();
+        if (size != 0) {
+            final Collection<DataTreeCandidateNode> ret = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                final DataTreeCandidateNode child = readNode(reader, in);
+                if (child != null) {
+                    ret.add(child);
+                }
+            }
+            return ret;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    private static DataTreeCandidateNode readNode(final NormalizedNodeInputStreamReader reader,
+            final DataInput in) throws IOException {
+        final byte type = in.readByte();
+        switch (type) {
+        case DELETE:
+            return DeletedDataTreeCandidateNode.create(reader.readPathArgument());
+        case SUBTREE_MODIFIED:
+            final PathArgument identifier = reader.readPathArgument();
+            final Collection<DataTreeCandidateNode> children = readChildren(reader, in);
+            if (children.isEmpty()) {
+                LOG.debug("Modified node {} does not have any children, not instantiating it", identifier);
+                return null;
+            } else {
+                return ModifiedDataTreeCandidateNode.create(identifier, children);
+            }
+        case UNMODIFIED:
+            return null;
+        case WRITE:
+            return DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
+        default:
+            throw new IllegalArgumentException("Unhandled node type " + type);
+        }
+    }
+
+    private static DataTreeCandidate parseCandidate(final ByteArrayDataInput in) throws IOException {
+        final NormalizedNodeInputStreamReader reader = new NormalizedNodeInputStreamReader(in);
+        final YangInstanceIdentifier rootPath = reader.readYangInstanceIdentifier();
+        final byte type = in.readByte();
+
+        final DataTreeCandidateNode rootNode;
+        switch (type) {
+        case DELETE:
+            rootNode = DeletedDataTreeCandidateNode.create();
+            break;
+        case SUBTREE_MODIFIED:
+            rootNode = ModifiedDataTreeCandidateNode.create(readChildren(reader, in));
+            break;
+        case WRITE:
+            rootNode = DataTreeCandidateNodes.fromNormalizedNode(reader.readNormalizedNode());
+            break;
+        default:
+            throw new IllegalArgumentException("Unhandled node type " + type);
+        }
+
+        return DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode);
+    }
+
+    DataTreeCandidate getCandidate() throws IOException {
+        return parseCandidate(ByteStreams.newDataInput(serialized));
+    }
+
+    @Override
+    @Deprecated
+    @SuppressWarnings("rawtypes")
+    public <T> Map<GeneratedExtension, T> encode() {
+        return null;
+    }
+
+    @Override
+    @Deprecated
+    public Payload decode(final AppendEntries.ReplicatedLogEntry.Payload payload) {
+        return null;
+    }
+
+    @Override
+    public int size() {
+        return serialized.length;
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeByte((byte)serialVersionUID);
+        out.writeInt(serialized.length);
+        out.write(serialized);
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        final long version = in.readByte();
+        Preconditions.checkArgument(version == serialVersionUID, "Unsupported serialization version %s", version);
+
+        final int length = in.readInt();
+        serialized = new byte[length];
+        in.readFully(serialized);
+    }
+}
index 124724b9c20c410760e43402b0f6958d5e7c9f9c..1a27f2e4fcdc19f2a3595764a80e00f963960ea4 100644 (file)
@@ -11,6 +11,7 @@ import akka.actor.ActorRef;
 import akka.actor.ActorSelection;
 import akka.actor.PoisonPill;
 import akka.dispatch.OnComplete;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import javax.annotation.concurrent.GuardedBy;
 import org.opendaylight.controller.cluster.datastore.exceptions.LocalShardNotFoundException;
@@ -106,9 +107,19 @@ final class DataTreeChangeListenerProxy<T extends DOMDataTreeChangeListener> ext
                 } else {
                     RegisterDataTreeChangeListenerReply reply = (RegisterDataTreeChangeListenerReply) result;
                     setListenerRegistrationActor(actorContext.actorSelection(
-                            reply.getListenerRegistrationPath().path()));
+                            reply.getListenerRegistrationPath()));
                 }
             }
         }, actorContext.getClientDispatcher());
     }
+
+    @VisibleForTesting
+    ActorSelection getListenerRegistrationActor() {
+        return listenerRegistrationActor;
+    }
+
+    @VisibleForTesting
+    ActorRef getDataChangeListenerActor() {
+        return dataChangeListenerActor;
+    }
 }
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DeletedDataTreeCandidateNode.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DeletedDataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..2df380b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * A deserialized {@link DataTreeCandidateNode} which represents a deletion.
+ */
+abstract class DeletedDataTreeCandidateNode extends AbstractDataTreeCandidateNode {
+    private DeletedDataTreeCandidateNode() {
+        super(ModificationType.DELETE);
+    }
+
+    static DataTreeCandidateNode create() {
+        return new DeletedDataTreeCandidateNode() {
+            @Override
+            public PathArgument getIdentifier() {
+                throw new UnsupportedOperationException("Root node does not have an identifier");
+            }
+        };
+    }
+
+    static DataTreeCandidateNode create(final PathArgument identifier) {
+        return new DeletedDataTreeCandidateNode() {
+            @Override
+            public final PathArgument getIdentifier() {
+                return identifier;
+            }
+        };
+    }
+
+    @Override
+    public final Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return Optional.absent();
+    }
+
+    @Override
+    public final Collection<DataTreeCandidateNode> getChildNodes() {
+        // We would require the before-image to reconstruct the list of nodes which
+        // were deleted.
+        throw new UnsupportedOperationException("Children not available after serialization");
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ModifiedDataTreeCandidateNode.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ModifiedDataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..208ec33
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * A deserialized {@link DataTreeCandidateNode} which represents a modification in
+ * one of its children.
+ */
+abstract class ModifiedDataTreeCandidateNode extends AbstractDataTreeCandidateNode {
+    private final Collection<DataTreeCandidateNode> children;
+
+    private ModifiedDataTreeCandidateNode(final Collection<DataTreeCandidateNode> children) {
+        super(ModificationType.SUBTREE_MODIFIED);
+        this.children = Preconditions.checkNotNull(children);
+    }
+
+    static DataTreeCandidateNode create(final Collection<DataTreeCandidateNode> children) {
+        return new ModifiedDataTreeCandidateNode(children) {
+            @Override
+            public PathArgument getIdentifier() {
+                throw new UnsupportedOperationException("Root node does not have an identifier");
+            }
+        };
+    }
+
+    static DataTreeCandidateNode create(final PathArgument identifier, final Collection<DataTreeCandidateNode> children) {
+        return new ModifiedDataTreeCandidateNode(children) {
+            @Override
+            public final PathArgument getIdentifier() {
+                return identifier;
+            }
+        };
+    }
+
+    @Override
+    public final Optional<NormalizedNode<?, ?>> getDataAfter() {
+        throw new UnsupportedOperationException("After-image not available after serialization");
+    }
+
+    @Override
+    public final Collection<DataTreeCandidateNode> getChildNodes() {
+        return children;
+    }
+}
index 0f3ab61041c6933bd744470d3dcdcb40e575ff94..cb17335caf1b53bcfe2cdfdc2fe13540ad945165 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.controller.cluster.datastore;
 
 import com.google.common.base.Preconditions;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 
 final class ReadWriteShardDataTreeTransaction extends AbstractShardDataTreeTransaction<DataTreeModification> {
@@ -26,7 +25,7 @@ final class ReadWriteShardDataTreeTransaction extends AbstractShardDataTreeTrans
         parent.abortTransaction(this);
     }
 
-    DOMStoreThreePhaseCommitCohort ready() {
+    ShardDataTreeCohort ready() {
         Preconditions.checkState(close(), "Transaction is already closed");
 
         return parent.finishTransaction(this);
index b53d12c0c8cc420129b281b685cbdea03b2a0568..62d3259a714059f497ca3e97ad08166a3a1106bf 100644 (file)
@@ -63,6 +63,9 @@ import org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyn
 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import scala.concurrent.duration.Duration;
 import scala.concurrent.duration.FiniteDuration;
@@ -291,15 +294,21 @@ public class Shard extends RaftActor {
         }
     }
 
+    private static boolean isEmptyCommit(final DataTreeCandidate candidate) {
+        return ModificationType.UNMODIFIED.equals(candidate.getRootNode().getModificationType());
+    }
+
     void continueCommit(final CohortEntry cohortEntry) throws Exception {
+        final DataTreeCandidate candidate = cohortEntry.getCohort().getCandidate();
+
         // If we do not have any followers and we are not using persistence
         // or if cohortEntry has no modifications
         // we can apply modification to the state immediately
-        if((!hasFollowers() && !persistence().isRecoveryApplicable()) || (!cohortEntry.hasModifications())){
-            applyModificationToState(getSender(), cohortEntry.getTransactionID(), cohortEntry.getModification());
+        if ((!hasFollowers() && !persistence().isRecoveryApplicable()) || isEmptyCommit(candidate)) {
+            applyModificationToState(getSender(), cohortEntry.getTransactionID(), candidate);
         } else {
             Shard.this.persistData(getSender(), cohortEntry.getTransactionID(),
-                    new ModificationPayload(cohortEntry.getModification()));
+                DataTreeCandidatePayload.create(candidate));
         }
     }
 
@@ -309,12 +318,37 @@ public class Shard extends RaftActor {
         }
     }
 
+    private void finishCommit(@Nonnull final ActorRef sender, @Nonnull final String transactionID, @Nonnull final CohortEntry cohortEntry) {
+        LOG.debug("{}: Finishing commit for transaction {}", persistenceId(), cohortEntry.getTransactionID());
+
+        try {
+            // We block on the future here so we don't have to worry about possibly accessing our
+            // state on a different thread outside of our dispatcher. Also, the data store
+            // currently uses a same thread executor anyway.
+            cohortEntry.getCohort().commit().get();
+
+            sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf());
+
+            shardMBean.incrementCommittedTransactionCount();
+            shardMBean.setLastCommittedTransactionTime(System.currentTimeMillis());
+
+        } catch (Exception e) {
+            sender.tell(new akka.actor.Status.Failure(e), getSelf());
+
+            LOG.error("{}, An exception occurred while committing transaction {}", persistenceId(),
+                    transactionID, e);
+            shardMBean.incrementFailedTransactionsCount();
+        } finally {
+            commitCoordinator.currentTransactionComplete(transactionID, true);
+        }
+    }
+
     private void finishCommit(@Nonnull final ActorRef sender, final @Nonnull String transactionID) {
         // With persistence enabled, this method is called via applyState by the leader strategy
         // after the commit has been replicated to a majority of the followers.
 
         CohortEntry cohortEntry = commitCoordinator.getCohortEntryIfCurrent(transactionID);
-        if(cohortEntry == null) {
+        if (cohortEntry == null) {
             // The transaction is no longer the current commit. This can happen if the transaction
             // was aborted prior, most likely due to timeout in the front-end. We need to finish
             // committing the transaction though since it was successfully persisted and replicated
@@ -323,7 +357,13 @@ public class Shard extends RaftActor {
             // transaction.
             cohortEntry = commitCoordinator.getAndRemoveCohortEntry(transactionID);
             if(cohortEntry != null) {
-                commitWithNewTransaction(cohortEntry.getModification());
+                try {
+                    store.applyForeignCandidate(transactionID, cohortEntry.getCohort().getCandidate());
+                } catch (DataValidationFailedException e) {
+                    shardMBean.incrementFailedTransactionsCount();
+                    LOG.error("{}: Failed to re-apply transaction {}", persistenceId(), transactionID, e);
+                }
+
                 sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf());
             } else {
                 // This really shouldn't happen - it likely means that persistence or replication
@@ -334,31 +374,8 @@ public class Shard extends RaftActor {
                 LOG.error(ex.getMessage());
                 sender.tell(new akka.actor.Status.Failure(ex), getSelf());
             }
-
-            return;
-        }
-
-        LOG.debug("{}: Finishing commit for transaction {}", persistenceId(), cohortEntry.getTransactionID());
-
-        try {
-            // We block on the future here so we don't have to worry about possibly accessing our
-            // state on a different thread outside of our dispatcher. Also, the data store
-            // currently uses a same thread executor anyway.
-            cohortEntry.getCohort().commit().get();
-
-            sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf());
-
-            shardMBean.incrementCommittedTransactionCount();
-            shardMBean.setLastCommittedTransactionTime(System.currentTimeMillis());
-
-        } catch (Exception e) {
-            sender.tell(new akka.actor.Status.Failure(e), getSelf());
-
-            LOG.error("{}, An exception occurred while committing transaction {}", persistenceId(),
-                    transactionID, e);
-            shardMBean.incrementFailedTransactionsCount();
-        } finally {
-            commitCoordinator.currentTransactionComplete(transactionID, true);
+        } else {
+            finishCommit(sender, transactionID, cohortEntry);
         }
     }
 
@@ -556,15 +573,25 @@ public class Shard extends RaftActor {
 
     @Override
     protected void applyState(final ActorRef clientActor, final String identifier, final Object data) {
-
-        if(data instanceof ModificationPayload) {
+        if (data instanceof DataTreeCandidatePayload) {
+            if (clientActor == null) {
+                // No clientActor indicates a replica coming from the leader
+                try {
+                    store.applyForeignCandidate(identifier, ((DataTreeCandidatePayload)data).getCandidate());
+                } catch (DataValidationFailedException | IOException e) {
+                    LOG.error("{}: Error applying replica {}", persistenceId(), identifier, e);
+                }
+            } else {
+                // Replication consensus reached, proceed to commit
+                finishCommit(clientActor, identifier);
+            }
+        } else if (data instanceof ModificationPayload) {
             try {
                 applyModificationToState(clientActor, identifier, ((ModificationPayload) data).getModification());
             } catch (ClassNotFoundException | IOException e) {
                 LOG.error("{}: Error extracting ModificationPayload", persistenceId(), e);
             }
-        }
-        else if (data instanceof CompositeModificationPayload) {
+        } else if (data instanceof CompositeModificationPayload) {
             Object modification = ((CompositeModificationPayload) data).getModification();
 
             applyModificationToState(clientActor, identifier, modification);
index 0eb48fd180c169eba511cac1272eb87ab227b819..30947fa6662b4a56d5b091cfe3133d019c9f9a24 100644 (file)
@@ -30,7 +30,6 @@ import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionRe
 import org.opendaylight.controller.cluster.datastore.modification.Modification;
 import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.slf4j.Logger;
 
 /**
@@ -42,7 +41,7 @@ public class ShardCommitCoordinator {
 
     // Interface hook for unit tests to replace or decorate the DOMStoreThreePhaseCommitCohorts.
     public interface CohortDecorator {
-        DOMStoreThreePhaseCommitCohort decorate(String transactionID, DOMStoreThreePhaseCommitCohort actual);
+        ShardDataTreeCohort decorate(String transactionID, ShardDataTreeCohort actual);
     }
 
     private final Cache<String, CohortEntry> cohortCache;
@@ -413,8 +412,7 @@ public class ShardCommitCoordinator {
 
     static class CohortEntry {
         private final String transactionID;
-        private DOMStoreThreePhaseCommitCohort cohort;
-        private final MutableCompositeModification compositeModification;
+        private ShardDataTreeCohort cohort;
         private final ReadWriteShardDataTreeTransaction transaction;
         private ActorRef replySender;
         private Shard shard;
@@ -422,16 +420,14 @@ public class ShardCommitCoordinator {
         private boolean doImmediateCommit;
 
         CohortEntry(String transactionID, ReadWriteShardDataTreeTransaction transaction) {
-            this.compositeModification = new MutableCompositeModification();
             this.transaction = Preconditions.checkNotNull(transaction);
             this.transactionID = transactionID;
         }
 
-        CohortEntry(String transactionID, DOMStoreThreePhaseCommitCohort cohort,
+        CohortEntry(String transactionID, ShardDataTreeCohort cohort,
                 MutableCompositeModification compositeModification) {
             this.transactionID = transactionID;
             this.cohort = cohort;
-            this.compositeModification = compositeModification;
             this.transaction = null;
         }
 
@@ -447,17 +443,12 @@ public class ShardCommitCoordinator {
             return transactionID;
         }
 
-        DOMStoreThreePhaseCommitCohort getCohort() {
+        ShardDataTreeCohort getCohort() {
             return cohort;
         }
 
-        MutableCompositeModification getModification() {
-            return compositeModification;
-        }
-
         void applyModifications(Iterable<Modification> modifications) {
-            for(Modification modification: modifications) {
-                compositeModification.addModification(modification);
+            for (Modification modification : modifications) {
                 modification.apply(transaction.getSnapshot());
             }
         }
@@ -498,9 +489,5 @@ public class ShardCommitCoordinator {
         void setShard(Shard shard) {
             this.shard = shard;
         }
-
-        boolean hasModifications(){
-            return compositeModification.getModifications().size() > 0;
-        }
     }
 }
index 373bf499e0a9b8e90f4d8886c1ce183e787933e0..56c5eb65bf087c3a0d979de2ecaff7b0a65467a2 100644 (file)
@@ -21,14 +21,13 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
 import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent;
 import org.opendaylight.controller.md.sal.dom.store.impl.ResolveDataChangeEventsTask;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -93,7 +92,7 @@ public final class ShardDataTree extends ShardDataTreeTransactionParent {
         return ensureTransactionChain(chainId).newReadWriteTransaction(txId);
     }
 
-    void notifyListeners(final DataTreeCandidateTip candidate) {
+    void notifyListeners(final DataTreeCandidate candidate) {
         LOG.debug("Notifying listeners on candidate {}", candidate);
 
         // DataTreeChanges first, as they are more light-weight
@@ -116,7 +115,7 @@ public final class ShardDataTree extends ShardDataTreeTransactionParent {
         if (chain != null) {
             chain.close();
         } else {
-            LOG.warn("Closing non-existent transaction chain {}", transactionChainId);
+            LOG.debug("Closing non-existent transaction chain {}", transactionChainId);
         }
     }
 
@@ -152,15 +151,30 @@ public final class ShardDataTree extends ShardDataTreeTransactionParent {
         return new SimpleEntry<>(reg, event);
     }
 
+    void applyForeignCandidate(final String identifier, final DataTreeCandidate foreign) throws DataValidationFailedException {
+        LOG.debug("Applying foreign transaction {}", identifier);
+
+        final DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        DataTreeCandidates.applyToModification(mod, foreign);
+        mod.ready();
+
+        LOG.trace("Applying foreign modification {}", mod);
+        dataTree.validate(mod);
+        final DataTreeCandidate candidate = dataTree.prepare(mod);
+        dataTree.commit(candidate);
+        notifyListeners(candidate);
+    }
+
     @Override
     void abortTransaction(final AbstractShardDataTreeTransaction<?> transaction) {
         // Intentional no-op
     }
 
     @Override
-    DOMStoreThreePhaseCommitCohort finishTransaction(final ReadWriteShardDataTreeTransaction transaction) {
+    ShardDataTreeCohort finishTransaction(final ReadWriteShardDataTreeTransaction transaction) {
         final DataTreeModification snapshot = transaction.getSnapshot();
         snapshot.ready();
-        return new ShardDataTreeCohort(this, snapshot);
+        return new SimpleShardDataTreeCohort(this, snapshot);
     }
+
 }
index 11b3ca8ed707e12f8456650fb423ca468a845660..213e36a570ce1c445e11f8d6e175b37abf9a7388 100644 (file)
@@ -7,72 +7,23 @@
  */
 package org.opendaylight.controller.cluster.datastore;
 
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.Futures;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.util.concurrent.ListenableFuture;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-final class ShardDataTreeCohort implements DOMStoreThreePhaseCommitCohort {
-    private static final Logger LOG = LoggerFactory.getLogger(ShardDataTreeCohort.class);
-    private static final ListenableFuture<Boolean> TRUE_FUTURE = Futures.immediateFuture(Boolean.TRUE);
-    private static final ListenableFuture<Void> VOID_FUTURE = Futures.immediateFuture(null);
-    private final DataTreeModification transaction;
-    private final ShardDataTree dataTree;
-    private DataTreeCandidateTip candidate;
-
-    ShardDataTreeCohort(final ShardDataTree dataTree, final DataTreeModification transaction) {
-        this.dataTree = Preconditions.checkNotNull(dataTree);
-        this.transaction = Preconditions.checkNotNull(transaction);
-    }
-
-    @Override
-    public ListenableFuture<Boolean> canCommit() {
-        try {
-            dataTree.getDataTree().validate(transaction);
-            LOG.debug("Transaction {} validated", transaction);
-            return TRUE_FUTURE;
-        } catch (Exception e) {
-            return Futures.immediateFailedFuture(e);
-        }
-    }
-
-    @Override
-    public ListenableFuture<Void> preCommit() {
-        try {
-            candidate = dataTree.getDataTree().prepare(transaction);
-            /*
-             * FIXME: this is the place where we should be interacting with persistence, specifically by invoking
-             *        persist on the candidate (which gives us a Future).
-             */
-            LOG.debug("Transaction {} prepared candidate {}", transaction, candidate);
-            return VOID_FUTURE;
-        } catch (Exception e) {
-            LOG.debug("Transaction {} failed to prepare", transaction, e);
-            return Futures.immediateFailedFuture(e);
-        }
-    }
-
-    @Override
-    public ListenableFuture<Void> abort() {
-        // No-op, really
-        return VOID_FUTURE;
+public abstract class ShardDataTreeCohort {
+    ShardDataTreeCohort() {
+        // Prevent foreign instantiation
     }
 
-    @Override
-    public ListenableFuture<Void> commit() {
-        try {
-            dataTree.getDataTree().commit(candidate);
-        } catch (Exception e) {
-            LOG.error("Transaction {} failed to commit", transaction, e);
-            return Futures.immediateFailedFuture(e);
-        }
+    abstract DataTreeCandidateTip getCandidate();
 
-        LOG.debug("Transaction {} committed, proceeding to notify", transaction);
-        dataTree.notifyListeners(candidate);
-        return VOID_FUTURE;
-    }
+    @VisibleForTesting
+    public abstract ListenableFuture<Boolean> canCommit();
+    @VisibleForTesting
+    public abstract ListenableFuture<Void> preCommit();
+    @VisibleForTesting
+    public abstract ListenableFuture<Void> abort();
+    @VisibleForTesting
+    public abstract ListenableFuture<Void> commit();
 }
index 780d940128b7eafe48cb38f87e8cf102bde370c3..183c2192e405e2a48a52c664ef90033f5da38597 100644 (file)
@@ -9,12 +9,7 @@ package org.opendaylight.controller.cluster.datastore;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import javax.annotation.concurrent.NotThreadSafe;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.controller.sal.core.spi.data.ForwardingDOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -77,17 +72,17 @@ final class ShardDataTreeTransactionChain extends ShardDataTreeTransactionParent
     }
 
     @Override
-    protected DOMStoreThreePhaseCommitCohort finishTransaction(final ReadWriteShardDataTreeTransaction transaction) {
+    protected ShardDataTreeCohort finishTransaction(final ReadWriteShardDataTreeTransaction transaction) {
         Preconditions.checkState(openTransaction != null, "Attempted to finish transaction %s while none is outstanding", transaction);
 
         // dataTree is finalizing ready the transaction, we just record it for the next
         // transaction in chain
-        final DOMStoreThreePhaseCommitCohort delegate = dataTree.finishTransaction(transaction);
+        final ShardDataTreeCohort delegate = dataTree.finishTransaction(transaction);
         openTransaction = null;
         previousTx = transaction;
         LOG.debug("Committing transaction {}", transaction);
 
-        return new CommitCohort(transaction, delegate);
+        return new ChainedCommitCohort(this, transaction, delegate);
     }
 
     @Override
@@ -95,40 +90,9 @@ final class ShardDataTreeTransactionChain extends ShardDataTreeTransactionParent
         return MoreObjects.toStringHelper(this).add("id", chainId).toString();
     }
 
-    private final class CommitCohort extends ForwardingDOMStoreThreePhaseCommitCohort {
-        private final ReadWriteShardDataTreeTransaction transaction;
-        private final DOMStoreThreePhaseCommitCohort delegate;
-
-        CommitCohort(final ReadWriteShardDataTreeTransaction transaction, final DOMStoreThreePhaseCommitCohort delegate) {
-            this.transaction = Preconditions.checkNotNull(transaction);
-            this.delegate = Preconditions.checkNotNull(delegate);
-        }
-
-        @Override
-        protected DOMStoreThreePhaseCommitCohort delegate() {
-            return delegate;
-        }
-
-        @Override
-        public ListenableFuture<Void> commit() {
-            final ListenableFuture<Void> ret = super.commit();
-
-            Futures.addCallback(ret, new FutureCallback<Void>() {
-                @Override
-                public void onSuccess(Void result) {
-                    if (transaction.equals(previousTx)) {
-                        previousTx = null;
-                    }
-                    LOG.debug("Committed transaction {}", transaction);
-                }
-
-                @Override
-                public void onFailure(Throwable t) {
-                    LOG.error("Transaction {} commit failed, cannot recover", transaction, t);
-                }
-            });
-
-            return ret;
+    void clearTransaction(ReadWriteShardDataTreeTransaction transaction) {
+        if (transaction.equals(previousTx)) {
+            previousTx = null;
         }
     }
 }
index 6cc1408eae52bd7fcf8bab941a7d3a5d84e42bc2..ee04aff515df8c4288e12e459137f26d4f6ab472 100644 (file)
@@ -7,9 +7,7 @@
  */
 package org.opendaylight.controller.cluster.datastore;
 
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-
 abstract class ShardDataTreeTransactionParent {
     abstract void abortTransaction(AbstractShardDataTreeTransaction<?> transaction);
-    abstract DOMStoreThreePhaseCommitCohort finishTransaction(ReadWriteShardDataTreeTransaction transaction);
+    abstract ShardDataTreeCohort finishTransaction(ReadWriteShardDataTreeTransaction transaction);
 }
index f9d305001567ad4b47706169de873ffafb36fb47..797641978d2cd47cc7eed57c12e77e2334cb943c 100644 (file)
@@ -7,9 +7,7 @@
  */
 package org.opendaylight.controller.cluster.datastore;
 
-import com.google.common.collect.Lists;
 import java.io.IOException;
-import java.util.List;
 import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload;
 import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
 import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils;
@@ -17,10 +15,10 @@ import org.opendaylight.controller.cluster.raft.RaftActorRecoveryCohort;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
 import org.slf4j.Logger;
@@ -36,52 +34,55 @@ import org.slf4j.Logger;
  */
 class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
     private static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
-    private final ShardDataTree store;
-    private List<ModificationPayload> currentLogRecoveryBatch;
+    private final DataTree store;
     private final String shardName;
     private final Logger log;
+    private DataTreeModification transaction;
+    private int size;
 
     ShardRecoveryCoordinator(ShardDataTree store, String shardName, Logger log) {
-        this.store = store;
+        this.store = store.getDataTree();
         this.shardName = shardName;
         this.log = log;
     }
 
     @Override
     public void startLogRecoveryBatch(int maxBatchSize) {
-        currentLogRecoveryBatch = Lists.newArrayListWithCapacity(maxBatchSize);
-
         log.debug("{}: starting log recovery batch with max size {}", shardName, maxBatchSize);
+        transaction = store.takeSnapshot().newModification();
+        size = 0;
     }
 
     @Override
     public void appendRecoveredLogEntry(Payload payload) {
         try {
-            if(payload instanceof ModificationPayload) {
-                currentLogRecoveryBatch.add((ModificationPayload) payload);
+            if (payload instanceof DataTreeCandidatePayload) {
+                DataTreeCandidates.applyToModification(transaction, ((DataTreeCandidatePayload)payload).getCandidate());
+                size++;
+            } else if (payload instanceof ModificationPayload) {
+                MutableCompositeModification.fromSerializable(
+                    ((ModificationPayload) payload).getModification()).apply(transaction);
+                size++;
             } else if (payload instanceof CompositeModificationPayload) {
-                currentLogRecoveryBatch.add(new ModificationPayload(MutableCompositeModification.fromSerializable(
-                        ((CompositeModificationPayload) payload).getModification())));
+                MutableCompositeModification.fromSerializable(
+                    ((CompositeModificationPayload) payload).getModification()).apply(transaction);
+                size++;
             } else if (payload instanceof CompositeModificationByteStringPayload) {
-                currentLogRecoveryBatch.add(new ModificationPayload(MutableCompositeModification.fromSerializable(
-                        ((CompositeModificationByteStringPayload) payload).getModification())));
+                MutableCompositeModification.fromSerializable(
+                        ((CompositeModificationByteStringPayload) payload).getModification()).apply(transaction);
+                size++;
             } else {
                 log.error("{}: Unknown payload {} received during recovery", shardName, payload);
             }
-        } catch (IOException e) {
+        } catch (IOException | ClassNotFoundException e) {
             log.error("{}: Error extracting ModificationPayload", shardName, e);
         }
-
     }
 
-    private void commitTransaction(ReadWriteShardDataTreeTransaction transaction) {
-        DOMStoreThreePhaseCommitCohort commitCohort = store.finishTransaction(transaction);
-        try {
-            commitCohort.preCommit().get();
-            commitCohort.commit().get();
-        } catch (Exception e) {
-            log.error("{}: Failed to commit Tx on recovery", shardName, e);
-        }
+    private void commitTransaction(DataTreeModification tx) throws DataValidationFailedException {
+        tx.ready();
+        store.validate(tx);
+        store.commit(store.prepare(tx));
     }
 
     /**
@@ -89,21 +90,13 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
      */
     @Override
     public void applyCurrentLogRecoveryBatch() {
-        log.debug("{}: Applying current log recovery batch with size {}", shardName, currentLogRecoveryBatch.size());
-
-        ReadWriteShardDataTreeTransaction writeTx = store.newReadWriteTransaction(shardName + "-recovery", null);
-        DataTreeModification snapshot = writeTx.getSnapshot();
-        for (ModificationPayload payload : currentLogRecoveryBatch) {
-            try {
-                MutableCompositeModification.fromSerializable(payload.getModification()).apply(snapshot);
-            } catch (Exception e) {
-                log.error("{}: Error extracting ModificationPayload", shardName, e);
-            }
+        log.debug("{}: Applying current log recovery batch with size {}", shardName, size);
+        try {
+            commitTransaction(transaction);
+        } catch (DataValidationFailedException e) {
+            log.error("{}: Failed to apply recovery batch", shardName, e);
         }
-
-        commitTransaction(writeTx);
-
-        currentLogRecoveryBatch = null;
+        transaction = null;
     }
 
     /**
@@ -115,19 +108,13 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
     public void applyRecoverySnapshot(final byte[] snapshotBytes) {
         log.debug("{}: Applying recovered snapshot", shardName);
 
-        // Intentionally bypass normal transaction to side-step persistence/replication
-        final DataTree tree = store.getDataTree();
-        DataTreeModification writeTx = tree.takeSnapshot().newModification();
-
-        NormalizedNode<?, ?> node = SerializationUtils.deserializeNormalizedNode(snapshotBytes);
-
-        writeTx.write(ROOT, node);
-        writeTx.ready();
+        final NormalizedNode<?, ?> node = SerializationUtils.deserializeNormalizedNode(snapshotBytes);
+        final DataTreeModification tx = store.takeSnapshot().newModification();
+        tx.write(ROOT, node);
         try {
-            tree.validate(writeTx);
-            tree.commit(tree.prepare(writeTx));
+            commitTransaction(tx);
         } catch (DataValidationFailedException e) {
-            log.error("{}: Failed to validate recovery snapshot", shardName, e);
+            log.error("{}: Failed to apply recovery snapshot", shardName, e);
         }
     }
 }
index 35d8e922f25ee60f4279fd67a99c320095e6012f..600509a26b87b480156f4baf843f2b887ed4f655 100644 (file)
@@ -14,7 +14,6 @@ import org.opendaylight.controller.cluster.datastore.identifiers.ShardTransactio
 import org.opendaylight.controller.cluster.datastore.messages.CreateSnapshot;
 import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils;
 import org.opendaylight.controller.cluster.raft.RaftActorSnapshotCohort;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
@@ -86,7 +85,7 @@ class ShardSnapshotCohort implements RaftActorSnapshotCohort {
 
     void syncCommitTransaction(final ReadWriteShardDataTreeTransaction transaction)
             throws ExecutionException, InterruptedException {
-        DOMStoreThreePhaseCommitCohort commitCohort = store.finishTransaction(transaction);
+        ShardDataTreeCohort commitCohort = store.finishTransaction(transaction);
         commitCohort.preCommit().get();
         commitCohort.commit().get();
     }
index 69a696f2942ecd3035dc9f52ef2b9831188f8e9a..365f97dd3f8f7797ceb953f2b9b71606b4ba099b 100644 (file)
@@ -31,7 +31,6 @@ import org.opendaylight.controller.cluster.datastore.modification.MergeModificat
 import org.opendaylight.controller.cluster.datastore.modification.Modification;
 import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
 import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 
 /**
  * @author: syedbahm
@@ -197,7 +196,7 @@ public class ShardWriteTransaction extends ShardTransaction {
 
         LOG.debug("readyTransaction : {}", transactionID);
 
-        DOMStoreThreePhaseCommitCohort cohort =  transaction.ready();
+        ShardDataTreeCohort cohort =  transaction.ready();
 
         getShardActor().forward(new ForwardedReadyTransaction(transactionID, getClientTxVersion(),
                 cohort, compositeModification, returnSerialized, doImmediateCommit), getContext());
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java
new file mode 100644 (file)
index 0000000..9f22ce8
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class SimpleShardDataTreeCohort extends ShardDataTreeCohort {
+    private static final Logger LOG = LoggerFactory.getLogger(SimpleShardDataTreeCohort.class);
+    private static final ListenableFuture<Boolean> TRUE_FUTURE = Futures.immediateFuture(Boolean.TRUE);
+    private static final ListenableFuture<Void> VOID_FUTURE = Futures.immediateFuture(null);
+    private final DataTreeModification transaction;
+    private final ShardDataTree dataTree;
+    private DataTreeCandidateTip candidate;
+
+    SimpleShardDataTreeCohort(final ShardDataTree dataTree, final DataTreeModification transaction) {
+        this.dataTree = Preconditions.checkNotNull(dataTree);
+        this.transaction = Preconditions.checkNotNull(transaction);
+    }
+
+    @Override
+    DataTreeCandidateTip getCandidate() {
+        return candidate;
+    }
+
+    @Override
+    public ListenableFuture<Boolean> canCommit() {
+        try {
+            dataTree.getDataTree().validate(transaction);
+            LOG.debug("Transaction {} validated", transaction);
+            return TRUE_FUTURE;
+        } catch (Exception e) {
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    @Override
+    public ListenableFuture<Void> preCommit() {
+        try {
+            candidate = dataTree.getDataTree().prepare(transaction);
+            /*
+             * FIXME: this is the place where we should be interacting with persistence, specifically by invoking
+             *        persist on the candidate (which gives us a Future).
+             */
+            LOG.debug("Transaction {} prepared candidate {}", transaction, candidate);
+            return VOID_FUTURE;
+        } catch (Exception e) {
+            LOG.debug("Transaction {} failed to prepare", transaction, e);
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    @Override
+    public ListenableFuture<Void> abort() {
+        // No-op, really
+        return VOID_FUTURE;
+    }
+
+    @Override
+    public ListenableFuture<Void> commit() {
+        try {
+            dataTree.getDataTree().commit(candidate);
+        } catch (Exception e) {
+            LOG.error("Transaction {} failed to commit", transaction, e);
+            return Futures.immediateFailedFuture(e);
+        }
+
+        LOG.debug("Transaction {} committed, proceeding to notify", transaction);
+        dataTree.notifyListeners(candidate);
+        return VOID_FUTURE;
+    }
+}
index 5531b5f5401676a13d02a08b69683a2f31ca077a..cf261cbd2af103b70f3dfce9885903a88ee293e6 100644 (file)
@@ -112,7 +112,7 @@ public class TransactionChainProxy implements DOMStoreTransactionChain {
         currentState = CLOSED_STATE;
 
         // Send a close transaction chain request to each and every shard
-        actorContext.broadcast(new CloseTransactionChain(transactionChainId));
+        actorContext.broadcast(new CloseTransactionChain(transactionChainId).toSerializable());
     }
 
     private ChainedTransactionProxy allocateWriteTransaction(TransactionProxy.TransactionType type) {
index f12fdd99eab792080069f38bebe1a721a71f1b32..e397ab501c064adf98c5ee6c2f6708f05eb6f2fe 100644 (file)
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import org.opendaylight.controller.cluster.datastore.compat.PreLithiumTransactionContextImpl;
 import org.opendaylight.controller.cluster.datastore.identifiers.TransactionIdentifier;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
 import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory;
 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.opendaylight.controller.cluster.datastore.utils.NormalizedNodeAggregator;
@@ -159,7 +160,7 @@ public class TransactionProxy extends AbstractDOMStoreTransaction<TransactionIde
         return false;
     }
 
-    private boolean isRootPath(YangInstanceIdentifier path){
+    private static boolean isRootPath(YangInstanceIdentifier path) {
         return !path.getPathArguments().iterator().hasNext();
     }
 
@@ -477,7 +478,7 @@ public class TransactionProxy extends AbstractDOMStoreTransaction<TransactionIde
         return ShardStrategyFactory.getStrategy(path).findShard(path);
     }
 
-    protected Future<ActorSelection> sendFindPrimaryShardAsync(String shardName) {
+    protected Future<PrimaryShardInfo> sendFindPrimaryShardAsync(String shardName) {
         return actorContext.findPrimaryShardAsync(shardName);
     }
 
@@ -497,20 +498,20 @@ public class TransactionProxy extends AbstractDOMStoreTransaction<TransactionIde
     private TransactionFutureCallback getOrCreateTxFutureCallback(String shardName) {
         TransactionFutureCallback txFutureCallback = txFutureCallbackMap.get(shardName);
         if(txFutureCallback == null) {
-            Future<ActorSelection> findPrimaryFuture = sendFindPrimaryShardAsync(shardName);
+            Future<PrimaryShardInfo> findPrimaryFuture = sendFindPrimaryShardAsync(shardName);
 
             final TransactionFutureCallback newTxFutureCallback = new TransactionFutureCallback(this, shardName);
 
             txFutureCallback = newTxFutureCallback;
             txFutureCallbackMap.put(shardName, txFutureCallback);
 
-            findPrimaryFuture.onComplete(new OnComplete<ActorSelection>() {
+            findPrimaryFuture.onComplete(new OnComplete<PrimaryShardInfo>() {
                 @Override
-                public void onComplete(Throwable failure, ActorSelection primaryShard) {
+                public void onComplete(Throwable failure, PrimaryShardInfo primaryShardInfo) {
                     if(failure != null) {
                         newTxFutureCallback.createTransactionContext(failure, null);
                     } else {
-                        newTxFutureCallback.setPrimaryShard(primaryShard);
+                        newTxFutureCallback.setPrimaryShard(primaryShardInfo.getPrimaryShardActor());
                     }
                 }
             }, actorContext.getClientDispatcher());
index cdd7859a3019d1dded7d53ae84e8b39d6acfa717..2f48ab9d1be137562f170d354e7580b74e6b7da6 100644 (file)
@@ -7,8 +7,8 @@
  */
 package org.opendaylight.controller.cluster.datastore.messages;
 
+import org.opendaylight.controller.cluster.datastore.ShardDataTreeCohort;
 import org.opendaylight.controller.cluster.datastore.modification.Modification;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 
 /**
  * Transaction ReadyTransaction message that is forwarded to the local Shard from the ShardTransaction.
@@ -17,14 +17,14 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCoh
  */
 public class ForwardedReadyTransaction {
     private final String transactionID;
-    private final DOMStoreThreePhaseCommitCohort cohort;
+    private final ShardDataTreeCohort cohort;
     private final Modification modification;
     private final boolean returnSerialized;
     private final boolean doImmediateCommit;
     private final short txnClientVersion;
 
     public ForwardedReadyTransaction(String transactionID, short txnClientVersion,
-            DOMStoreThreePhaseCommitCohort cohort, Modification modification,
+            ShardDataTreeCohort cohort, Modification modification,
             boolean returnSerialized, boolean doImmediateCommit) {
         this.transactionID = transactionID;
         this.cohort = cohort;
@@ -38,7 +38,7 @@ public class ForwardedReadyTransaction {
         return transactionID;
     }
 
-    public DOMStoreThreePhaseCommitCohort getCohort() {
+    public ShardDataTreeCohort getCohort() {
         return cohort;
     }
 
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/PrimaryShardInfo.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/PrimaryShardInfo.java
new file mode 100644 (file)
index 0000000..bbeb1aa
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.cluster.datastore.messages;
+
+import akka.actor.ActorSelection;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+
+/**
+ * Local message DTO that contains information about the primary shard.
+ *
+ * @author Thomas Pantelis
+ */
+public class PrimaryShardInfo {
+    private final ActorSelection primaryShardActor;
+    private final Optional<DataTree> localShardDataTree;
+
+    public PrimaryShardInfo(@Nonnull ActorSelection primaryShardActor, @Nonnull Optional<DataTree> localShardDataTree) {
+        this.primaryShardActor = Preconditions.checkNotNull(primaryShardActor);
+        this.localShardDataTree = Preconditions.checkNotNull(localShardDataTree);
+    }
+
+    /**
+     * Returns an ActorSelection representing the primary shard actor.
+     */
+    public @Nonnull ActorSelection getPrimaryShardActor() {
+        return primaryShardActor;
+    }
+
+    /**
+     * Returns an Optional whose value contains the primary shard's DataTree if the primary shard is local
+     * to the caller. Otherwise the Optional value is absent.
+     */
+    public @Nonnull Optional<DataTree> getLocalShardDataTree() {
+        return localShardDataTree;
+    }
+}
index 88682ae7f31950944d1537e3c0376498bc9e45e8..4c950ce1f11e3f2fcacea7ea414201cafeaa5e4d 100644 (file)
@@ -7,8 +7,10 @@
  */
 package org.opendaylight.controller.cluster.datastore.messages;
 
+import akka.actor.ActorPath;
 import akka.actor.ActorRef;
 import com.google.common.base.Preconditions;
+
 import java.io.Serializable;
 
 /**
@@ -22,7 +24,7 @@ public final class RegisterDataTreeChangeListenerReply implements Serializable {
         this.listenerRegistrationPath = Preconditions.checkNotNull(listenerRegistrationPath);
     }
 
-    public ActorRef getListenerRegistrationPath() {
-        return listenerRegistrationPath;
+    public ActorPath getListenerRegistrationPath() {
+        return listenerRegistrationPath.path();
     }
 }
index 17d988005fadf32c8209837671d43d0aa2981b60..afa773b4615e8905fffea10d42dbabd1cb046b0e 100644 (file)
@@ -45,8 +45,10 @@ import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
 import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
 import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
 import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
 import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
 import org.opendaylight.controller.cluster.reporting.MetricsReporter;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -96,7 +98,7 @@ public class ActorContext {
     private Timeout transactionCommitOperationTimeout;
     private Timeout shardInitializationTimeout;
     private final Dispatchers dispatchers;
-    private Cache<String, Future<ActorSelection>> primaryShardActorSelectionCache;
+    private Cache<String, Future<PrimaryShardInfo>> primaryShardInfoCache;
 
     private volatile SchemaContext schemaContext;
     private volatile boolean updated;
@@ -141,7 +143,7 @@ public class ActorContext {
 
         shardInitializationTimeout = new Timeout(datastoreContext.getShardInitializationTimeout().duration().$times(2));
 
-        primaryShardActorSelectionCache = CacheBuilder.newBuilder()
+        primaryShardInfoCache = CacheBuilder.newBuilder()
                 .expireAfterWrite(datastoreContext.getShardLeaderElectionTimeout().duration().toMillis(), TimeUnit.MILLISECONDS)
                 .build();
     }
@@ -196,24 +198,25 @@ public class ActorContext {
         return schemaContext;
     }
 
-    public Future<ActorSelection> findPrimaryShardAsync(final String shardName) {
-        Future<ActorSelection> ret = primaryShardActorSelectionCache.getIfPresent(shardName);
+    public Future<PrimaryShardInfo> findPrimaryShardAsync(final String shardName) {
+        Future<PrimaryShardInfo> ret = primaryShardInfoCache.getIfPresent(shardName);
         if(ret != null){
             return ret;
         }
         Future<Object> future = executeOperationAsync(shardManager,
                 new FindPrimary(shardName, true), shardInitializationTimeout);
 
-        return future.transform(new Mapper<Object, ActorSelection>() {
+        return future.transform(new Mapper<Object, PrimaryShardInfo>() {
             @Override
-            public ActorSelection checkedApply(Object response) throws Exception {
+            public PrimaryShardInfo checkedApply(Object response) throws Exception {
                 if(response instanceof PrimaryFound) {
                     PrimaryFound found = (PrimaryFound)response;
 
                     LOG.debug("Primary found {}", found.getPrimaryPath());
                     ActorSelection actorSelection = actorSystem.actorSelection(found.getPrimaryPath());
-                    primaryShardActorSelectionCache.put(shardName, Futures.successful(actorSelection));
-                    return actorSelection;
+                    PrimaryShardInfo info = new PrimaryShardInfo(actorSelection, Optional.<DataTree>absent());
+                    primaryShardInfoCache.put(shardName, Futures.successful(info));
+                    return info;
                 } else if(response instanceof NotInitializedException) {
                     throw (NotInitializedException)response;
                 } else if(response instanceof PrimaryNotFoundException) {
@@ -387,15 +390,15 @@ public class ActorContext {
     public void broadcast(final Object message){
         for(final String shardName : configuration.getAllShardNames()){
 
-            Future<ActorSelection> primaryFuture = findPrimaryShardAsync(shardName);
-            primaryFuture.onComplete(new OnComplete<ActorSelection>() {
+            Future<PrimaryShardInfo> primaryFuture = findPrimaryShardAsync(shardName);
+            primaryFuture.onComplete(new OnComplete<PrimaryShardInfo>() {
                 @Override
-                public void onComplete(Throwable failure, ActorSelection primaryShard) {
+                public void onComplete(Throwable failure, PrimaryShardInfo primaryShardInfo) {
                     if(failure != null) {
                         LOG.warn("broadcast failed to send message {} to shard {}:  {}",
                                 message.getClass().getSimpleName(), shardName, failure);
                     } else {
-                        primaryShard.tell(message, ActorRef.noSender());
+                        primaryShardInfo.getPrimaryShardActor().tell(message, ActorRef.noSender());
                     }
                 }
             }, getClientDispatcher());
@@ -553,7 +556,7 @@ public class ActorContext {
     }
 
     @VisibleForTesting
-    Cache<String, Future<ActorSelection>> getPrimaryShardActorSelectionCache() {
-        return primaryShardActorSelectionCache;
+    Cache<String, Future<PrimaryShardInfo>> getPrimaryShardInfoCache() {
+        return primaryShardInfoCache;
     }
 }
index 03f2bb7ad090d9f8acccda0a5829bc17880485b6..1100f3a7fa2c0fd584dc57618a6cd54339aa4b60 100644 (file)
@@ -21,7 +21,6 @@ import akka.japi.Creator;
 import akka.testkit.TestActorRef;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.Uninterruptibles;
 import java.util.Collections;
@@ -42,10 +41,6 @@ import org.opendaylight.controller.cluster.datastore.modification.WriteModificat
 import org.opendaylight.controller.cluster.raft.utils.InMemoryJournal;
 import org.opendaylight.controller.cluster.raft.utils.InMemorySnapshotStore;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
@@ -53,6 +48,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
@@ -172,49 +168,35 @@ public abstract class AbstractShardTest extends AbstractActorTest{
         Assert.fail(String.format("Expected last applied: %d, Actual: %d", expectedValue, lastApplied));
     }
 
-    protected NormalizedNode<?, ?> readStore(final InMemoryDOMDataStore store) throws ReadFailedException {
-        DOMStoreReadTransaction transaction = store.newReadOnlyTransaction();
-        CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
-            transaction.read(YangInstanceIdentifier.builder().build());
-
-        Optional<NormalizedNode<?, ?>> optional = read.checkedGet();
-
-        NormalizedNode<?, ?> normalizedNode = optional.get();
-
-        transaction.close();
-
-        return normalizedNode;
-    }
-
-    protected DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName,
+    protected ShardDataTreeCohort setupMockWriteTransaction(final String cohortName,
             final ShardDataTree dataStore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
             final MutableCompositeModification modification) {
         return setupMockWriteTransaction(cohortName, dataStore, path, data, modification, null);
     }
 
-    protected DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName,
+    protected ShardDataTreeCohort setupMockWriteTransaction(final String cohortName,
             final ShardDataTree dataStore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
             final MutableCompositeModification modification,
-            final Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>> preCommit) {
+            final Function<ShardDataTreeCohort, ListenableFuture<Void>> preCommit) {
 
         ReadWriteShardDataTreeTransaction tx = dataStore.newReadWriteTransaction("setup-mock-" + cohortName, null);
         tx.getSnapshot().write(path, data);
-        DOMStoreThreePhaseCommitCohort cohort = createDelegatingMockCohort(cohortName, dataStore.finishTransaction(tx), preCommit);
+        ShardDataTreeCohort cohort = createDelegatingMockCohort(cohortName, dataStore.finishTransaction(tx), preCommit);
 
         modification.addModification(new WriteModification(path, data));
 
         return cohort;
     }
 
-    protected DOMStoreThreePhaseCommitCohort createDelegatingMockCohort(final String cohortName,
-            final DOMStoreThreePhaseCommitCohort actual) {
+    protected ShardDataTreeCohort createDelegatingMockCohort(final String cohortName,
+            final ShardDataTreeCohort actual) {
         return createDelegatingMockCohort(cohortName, actual, null);
     }
 
-    protected DOMStoreThreePhaseCommitCohort createDelegatingMockCohort(final String cohortName,
-            final DOMStoreThreePhaseCommitCohort actual,
-            final Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>> preCommit) {
-        DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, cohortName);
+    protected ShardDataTreeCohort createDelegatingMockCohort(final String cohortName,
+            final ShardDataTreeCohort actual,
+            final Function<ShardDataTreeCohort, ListenableFuture<Void>> preCommit) {
+        ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, cohortName);
 
         doAnswer(new Answer<ListenableFuture<Boolean>>() {
             @Override
@@ -248,6 +230,13 @@ public abstract class AbstractShardTest extends AbstractActorTest{
             }
         }).when(cohort).abort();
 
+        doAnswer(new Answer<DataTreeCandidateTip>() {
+            @Override
+            public DataTreeCandidateTip answer(final InvocationOnMock invocation) {
+                return actual.getCandidate();
+            }
+        }).when(cohort).getCandidate();
+
         return cohort;
     }
 
@@ -275,7 +264,7 @@ public abstract class AbstractShardTest extends AbstractActorTest{
         ReadWriteShardDataTreeTransaction transaction = store.newReadWriteTransaction("writeToStore", null);
 
         transaction.getSnapshot().write(id, node);
-        DOMStoreThreePhaseCommitCohort cohort = transaction.ready();
+        ShardDataTreeCohort cohort = transaction.ready();
         cohort.canCommit().get();
         cohort.preCommit().get();
         cohort.commit();
index abe7f7678c3c817da1ebde3f1e3a579c781fe096..a64a5802b8387102bdfac19e4535a440088ccf55 100644 (file)
@@ -54,6 +54,7 @@ import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionR
 import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.DataExists;
 import org.opendaylight.controller.cluster.datastore.messages.DataExistsReply;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
 import org.opendaylight.controller.cluster.datastore.messages.ReadData;
 import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply;
 import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
@@ -71,6 +72,7 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -309,6 +311,11 @@ public abstract class AbstractTransactionProxyTest {
         return setupActorContextWithoutInitialCreateTransaction(actorSystem, DefaultShardStrategy.DEFAULT_SHARD);
     }
 
+    protected Future<PrimaryShardInfo> primaryShardInfoReply(ActorSystem actorSystem, ActorRef actorRef) {
+        return Futures.successful(new PrimaryShardInfo(actorSystem.actorSelection(actorRef.path()),
+                Optional.<DataTree>absent()));
+    }
+
     protected ActorRef setupActorContextWithoutInitialCreateTransaction(ActorSystem actorSystem, String shardName) {
         ActorRef actorRef = actorSystem.actorOf(Props.create(DoNothingActor.class));
         log.info("Created mock shard actor {}", actorRef);
@@ -316,7 +323,7 @@ public abstract class AbstractTransactionProxyTest {
         doReturn(actorSystem.actorSelection(actorRef.path())).
                 when(mockActorContext).actorSelection(actorRef.path().toString());
 
-        doReturn(Futures.successful(actorSystem.actorSelection(actorRef.path()))).
+        doReturn(primaryShardInfoReply(actorSystem, actorRef)).
                 when(mockActorContext).findPrimaryShardAsync(eq(shardName));
 
         doReturn(false).when(mockActorContext).isPathLocal(actorRef.path().toString());
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeCandidatePayloadTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeCandidatePayloadTest.java
new file mode 100644 (file)
index 0000000..781c3db
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import java.io.IOException;
+import java.util.Collection;
+import org.apache.commons.lang3.SerializationUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+
+public class DataTreeCandidatePayloadTest {
+    private DataTreeCandidate candidate;
+
+    private static DataTreeCandidateNode findNode(final Collection<DataTreeCandidateNode> nodes, final PathArgument arg) {
+        for (DataTreeCandidateNode node : nodes) {
+            if (arg.equals(node.getIdentifier())) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    private static void assertChildrenEquals(final Collection<DataTreeCandidateNode> expected,
+            final Collection<DataTreeCandidateNode> actual) {
+        // Make sure all expected nodes are there
+        for (DataTreeCandidateNode exp : expected) {
+            final DataTreeCandidateNode act = findNode(actual, exp.getIdentifier());
+            assertNotNull("missing expected child", act);
+            assertCandidateNodeEquals(exp, act);
+        }
+        // Make sure no nodes are present which are not in the expected set
+        for (DataTreeCandidateNode act : actual) {
+            final DataTreeCandidateNode exp = findNode(expected, act.getIdentifier());
+            assertNull("unexpected child", exp);
+        }
+    }
+
+    private static void assertCandidateEquals(final DataTreeCandidate expected, final DataTreeCandidate actual) {
+        assertEquals("root path", expected.getRootPath(), actual.getRootPath());
+
+        final DataTreeCandidateNode expRoot = expected.getRootNode();
+        final DataTreeCandidateNode actRoot = expected.getRootNode();
+        assertEquals("root type", expRoot.getModificationType(), actRoot.getModificationType());
+
+        switch (actRoot.getModificationType()) {
+        case DELETE:
+        case WRITE:
+            assertEquals("root data", expRoot.getDataAfter(), actRoot.getDataAfter());
+            break;
+        case SUBTREE_MODIFIED:
+            assertChildrenEquals(expRoot.getChildNodes(), actRoot.getChildNodes());
+            break;
+        default:
+            fail("Unexpect root type " + actRoot.getModificationType());
+            break;
+        }
+
+        assertCandidateNodeEquals(expected.getRootNode(), actual.getRootNode());
+    }
+
+    private static void assertCandidateNodeEquals(final DataTreeCandidateNode expected, final DataTreeCandidateNode actual) {
+        assertEquals("child type", expected.getModificationType(), actual.getModificationType());
+        assertEquals("child identifier", expected.getIdentifier(), actual.getIdentifier());
+
+        switch (actual.getModificationType()) {
+        case DELETE:
+        case WRITE:
+            assertEquals("child data", expected.getDataAfter(), actual.getDataAfter());
+            break;
+        case SUBTREE_MODIFIED:
+            assertChildrenEquals(expected.getChildNodes(), actual.getChildNodes());
+            break;
+        default:
+            fail("Unexpect root type " + actual.getModificationType());
+            break;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        final YangInstanceIdentifier writePath = TestModel.TEST_PATH;
+        final NormalizedNode<?, ?> writeData = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)).
+                withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build();
+        candidate = DataTreeCandidates.fromNormalizedNode(writePath, writeData);
+    }
+
+    @Test
+    public void testCandidateSerialization() throws IOException {
+        final DataTreeCandidatePayload payload = DataTreeCandidatePayload.create(candidate);
+        assertEquals("payload size", 141, payload.size());
+    }
+
+    @Test
+    public void testCandidateSerDes() throws IOException {
+        final DataTreeCandidatePayload payload = DataTreeCandidatePayload.create(candidate);
+        assertCandidateEquals(candidate, payload.getCandidate());
+    }
+
+    @Test
+    public void testPayloadSerDes() throws IOException {
+        final DataTreeCandidatePayload payload = DataTreeCandidatePayload.create(candidate);
+        assertCandidateEquals(candidate, SerializationUtils.clone(payload).getCandidate());
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActorTest.java
new file mode 100644 (file)
index 0000000..37a6197
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.DeadLetter;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import com.google.common.collect.ImmutableList;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.opendaylight.controller.cluster.datastore.messages.DataTreeChanged;
+import org.opendaylight.controller.cluster.datastore.messages.DataTreeChangedReply;
+import org.opendaylight.controller.cluster.datastore.messages.EnableNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+public class DataTreeChangeListenerActorTest extends AbstractActorTest {
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test
+    public void testDataChangedWhenNotificationsAreEnabled(){
+        new JavaTestKit(getSystem()) {{
+            final DataTreeCandidate mockTreeCandidate = Mockito.mock(DataTreeCandidate.class);
+            final ImmutableList<DataTreeCandidate> mockCandidates = ImmutableList.of(mockTreeCandidate);
+            final DOMDataTreeChangeListener mockListener = Mockito.mock(DOMDataTreeChangeListener.class);
+            final Props props = DataTreeChangeListenerActor.props(mockListener);
+            final ActorRef subject = getSystem().actorOf(props, "testDataTreeChangedNotificationsEnabled");
+
+            // Let the DataChangeListener know that notifications should be enabled
+            subject.tell(new EnableNotification(true), getRef());
+
+            subject.tell(new DataTreeChanged(mockCandidates),
+                    getRef());
+
+            expectMsgClass(DataTreeChangedReply.class);
+
+            Mockito.verify(mockListener).onDataTreeChanged(mockCandidates);
+        }};
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test
+    public void testDataChangedWhenNotificationsAreDisabled(){
+        new JavaTestKit(getSystem()) {{
+            final DataTreeCandidate mockTreeCandidate = Mockito.mock(DataTreeCandidate.class);
+            final ImmutableList<DataTreeCandidate> mockCandidates = ImmutableList.of(mockTreeCandidate);
+            final DOMDataTreeChangeListener mockListener = Mockito.mock(DOMDataTreeChangeListener.class);
+            final Props props = DataTreeChangeListenerActor.props(mockListener);
+            final ActorRef subject =
+                    getSystem().actorOf(props, "testDataTreeChangedNotificationsDisabled");
+
+            subject.tell(new DataTreeChanged(mockCandidates),
+                    getRef());
+
+            new Within(duration("1 seconds")) {
+                @Override
+                protected void run() {
+                    expectNoMsg();
+
+                    Mockito.verify(mockListener, Mockito.never()).onDataTreeChanged(
+                            Matchers.anyCollectionOf(DataTreeCandidate.class));
+                }
+            };
+        }};
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test
+    public void testDataChangedWithNoSender(){
+        new JavaTestKit(getSystem()) {{
+            final DataTreeCandidate mockTreeCandidate = Mockito.mock(DataTreeCandidate.class);
+            final ImmutableList<DataTreeCandidate> mockCandidates = ImmutableList.of(mockTreeCandidate);
+            final DOMDataTreeChangeListener mockListener = Mockito.mock(DOMDataTreeChangeListener.class);
+            final Props props = DataTreeChangeListenerActor.props(mockListener);
+            final ActorRef subject = getSystem().actorOf(props, "testDataTreeChangedWithNoSender");
+
+            getSystem().eventStream().subscribe(getRef(), DeadLetter.class);
+
+            subject.tell(new DataTreeChanged(mockCandidates), ActorRef.noSender());
+
+            // Make sure no DataChangedReply is sent to DeadLetters.
+            while(true) {
+                DeadLetter deadLetter;
+                try {
+                    deadLetter = expectMsgClass(duration("1 seconds"), DeadLetter.class);
+                } catch (AssertionError e) {
+                    // Timed out - got no DeadLetter - this is good
+                    break;
+                }
+
+                // We may get DeadLetters for other messages we don't care about.
+                Assert.assertFalse("Unexpected DataTreeChangedReply",
+                        deadLetter.message() instanceof DataTreeChangedReply);
+            }
+        }};
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test
+    public void testDataChangedWithListenerRuntimeEx(){
+        new JavaTestKit(getSystem()) {{
+            final DataTreeCandidate mockTreeCandidate1 = Mockito.mock(DataTreeCandidate.class);
+            final ImmutableList<DataTreeCandidate> mockCandidates1 = ImmutableList.of(mockTreeCandidate1);
+            final DataTreeCandidate mockTreeCandidate2 = Mockito.mock(DataTreeCandidate.class);
+            final ImmutableList<DataTreeCandidate> mockCandidates2 = ImmutableList.of(mockTreeCandidate2);
+            final DataTreeCandidate mockTreeCandidate3 = Mockito.mock(DataTreeCandidate.class);
+            final ImmutableList<DataTreeCandidate> mockCandidates3 = ImmutableList.of(mockTreeCandidate3);
+
+            final DOMDataTreeChangeListener mockListener = Mockito.mock(DOMDataTreeChangeListener.class);
+            Mockito.doThrow(new RuntimeException("mock")).when(mockListener).onDataTreeChanged(mockCandidates2);
+
+            Props props = DataTreeChangeListenerActor.props(mockListener);
+            ActorRef subject = getSystem().actorOf(props, "testDataTreeChangedWithListenerRuntimeEx");
+
+            // Let the DataChangeListener know that notifications should be enabled
+            subject.tell(new EnableNotification(true), getRef());
+
+            subject.tell(new DataTreeChanged(mockCandidates1),getRef());
+            expectMsgClass(DataTreeChangedReply.class);
+
+            subject.tell(new DataTreeChanged(mockCandidates2),getRef());
+            expectMsgClass(DataTreeChangedReply.class);
+
+            subject.tell(new DataTreeChanged(mockCandidates3),getRef());
+            expectMsgClass(DataTreeChangedReply.class);
+
+            Mockito.verify(mockListener).onDataTreeChanged(mockCandidates1);
+            Mockito.verify(mockListener).onDataTreeChanged(mockCandidates2);
+            Mockito.verify(mockListener).onDataTreeChanged(mockCandidates3);
+        }};
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerProxyTest.java
new file mode 100644 (file)
index 0000000..0dc0706
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.actor.Props;
+import akka.actor.Terminated;
+import akka.dispatch.ExecutionContexts;
+import akka.dispatch.Futures;
+import akka.testkit.JavaTestKit;
+import akka.util.Timeout;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.concurrent.TimeUnit;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.cluster.datastore.exceptions.NotInitializedException;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterDataTreeChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterDataTreeChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.Dispatchers;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import scala.concurrent.ExecutionContextExecutor;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+
+public class DataTreeChangeListenerProxyTest extends AbstractActorTest {
+    @SuppressWarnings("unchecked")
+    private final DOMDataTreeChangeListener mockListener = mock(DOMDataTreeChangeListener.class);
+
+    @Test(timeout=10000)
+    public void testSuccessfulRegistration() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = new ActorContext(getSystem(), getRef(),
+                    mock(ClusterWrapper.class), mock(Configuration.class));
+
+            final DataTreeChangeListenerProxy<DOMDataTreeChangeListener> proxy =
+                    new DataTreeChangeListenerProxy<>(actorContext, mockListener);
+
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+            new Thread() {
+                @Override
+                public void run() {
+                    proxy.init("shard-1", path);
+                }
+
+            }.start();
+
+            FiniteDuration timeout = duration("5 seconds");
+            FindLocalShard findLocalShard = expectMsgClass(timeout, FindLocalShard.class);
+            Assert.assertEquals("getShardName", "shard-1", findLocalShard.getShardName());
+
+            reply(new LocalShardFound(getRef()));
+
+            RegisterDataTreeChangeListener registerMsg = expectMsgClass(timeout, RegisterDataTreeChangeListener.class);
+            Assert.assertEquals("getPath", path, registerMsg.getPath());
+
+            reply(new RegisterDataTreeChangeListenerReply(getRef()));
+
+
+            for(int i = 0; (i < 20 * 5) && proxy.getListenerRegistrationActor() == null; i++) {
+                Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
+            }
+
+            Assert.assertEquals("getListenerRegistrationActor", getSystem().actorSelection(getRef().path()),
+                    proxy.getListenerRegistrationActor());
+
+            watch(proxy.getDataChangeListenerActor());
+
+            proxy.close();
+
+            // The listener registration actor should get a Close message
+            expectMsgClass(timeout, CloseDataTreeChangeListenerRegistration.class);
+
+            // The DataChangeListener actor should be terminated
+            expectMsgClass(timeout, Terminated.class);
+
+            proxy.close();
+
+            expectNoMsg();
+        }};
+    }
+
+    @Test(timeout=10000)
+    public void testLocalShardNotFound() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = new ActorContext(getSystem(), getRef(),
+                    mock(ClusterWrapper.class), mock(Configuration.class));
+
+            final DataTreeChangeListenerProxy<DOMDataTreeChangeListener> proxy =
+                    new DataTreeChangeListenerProxy<>(actorContext, mockListener);
+
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+            new Thread() {
+                @Override
+                public void run() {
+                    proxy.init("shard-1", path);
+                }
+
+            }.start();
+
+            FiniteDuration timeout = duration("5 seconds");
+            FindLocalShard findLocalShard = expectMsgClass(timeout, FindLocalShard.class);
+            Assert.assertEquals("getShardName", "shard-1", findLocalShard.getShardName());
+
+            reply(new LocalShardNotFound("shard-1"));
+
+            expectNoMsg(duration("1 seconds"));
+        }};
+    }
+
+    @Test(timeout=10000)
+    public void testLocalShardNotInitialized() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = new ActorContext(getSystem(), getRef(),
+                    mock(ClusterWrapper.class), mock(Configuration.class));
+
+            final DataTreeChangeListenerProxy<DOMDataTreeChangeListener> proxy =
+                    new DataTreeChangeListenerProxy<>(actorContext, mockListener);
+
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+            new Thread() {
+                @Override
+                public void run() {
+                    proxy.init("shard-1", path);
+                }
+
+            }.start();
+
+            FiniteDuration timeout = duration("5 seconds");
+            FindLocalShard findLocalShard = expectMsgClass(timeout, FindLocalShard.class);
+            Assert.assertEquals("getShardName", "shard-1", findLocalShard.getShardName());
+
+            reply(new NotInitializedException("not initialized"));
+
+            new Within(duration("1 seconds")) {
+                @Override
+                protected void run() {
+                    expectNoMsg();
+                }
+            };
+        }};
+    }
+
+    @Test
+    public void testFailedRegistration() {
+        new JavaTestKit(getSystem()) {{
+            ActorSystem mockActorSystem = mock(ActorSystem.class);
+
+            ActorRef mockActor = getSystem().actorOf(Props.create(DoNothingActor.class),
+                    "testFailedRegistration");
+            doReturn(mockActor).when(mockActorSystem).actorOf(any(Props.class));
+            ExecutionContextExecutor executor = ExecutionContexts.fromExecutor(
+                    MoreExecutors.sameThreadExecutor());
+
+
+            ActorContext actorContext = mock(ActorContext.class);
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+
+            doReturn(executor).when(actorContext).getClientDispatcher();
+            doReturn(mockActorSystem).when(actorContext).getActorSystem();
+
+            String shardName = "shard-1";
+            final DataTreeChangeListenerProxy<DOMDataTreeChangeListener> proxy =
+                    new DataTreeChangeListenerProxy<>(actorContext, mockListener);
+
+            doReturn(duration("5 seconds")).when(actorContext).getOperationDuration();
+            doReturn(Futures.successful(getRef())).when(actorContext).findLocalShardAsync(eq(shardName));
+            doReturn(Futures.failed(new RuntimeException("mock"))).
+                    when(actorContext).executeOperationAsync(any(ActorRef.class),
+                    any(Object.class), any(Timeout.class));
+            doReturn(mock(DatastoreContext.class)).when(actorContext).getDatastoreContext();
+
+            proxy.init("shard-1", path);
+
+            Assert.assertEquals("getListenerRegistrationActor", null,
+                    proxy.getListenerRegistrationActor());
+        }};
+    }
+
+    @Test
+    public void testCloseBeforeRegistration() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = mock(ActorContext.class);
+
+            String shardName = "shard-1";
+
+            doReturn(DatastoreContext.newBuilder().build()).when(actorContext).getDatastoreContext();
+            doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(actorContext).getClientDispatcher();
+            doReturn(getSystem()).when(actorContext).getActorSystem();
+            doReturn(Dispatchers.DEFAULT_DISPATCHER_PATH).when(actorContext).getNotificationDispatcherPath();
+            doReturn(getSystem().actorSelection(getRef().path())).
+                    when(actorContext).actorSelection(getRef().path());
+            doReturn(duration("5 seconds")).when(actorContext).getOperationDuration();
+            doReturn(Futures.successful(getRef())).when(actorContext).findLocalShardAsync(eq(shardName));
+
+            final DataTreeChangeListenerProxy<DOMDataTreeChangeListener> proxy =
+                    new DataTreeChangeListenerProxy<>(actorContext, mockListener);
+
+
+            Answer<Future<Object>> answer = new Answer<Future<Object>>() {
+                @Override
+                public Future<Object> answer(InvocationOnMock invocation) {
+                    proxy.close();
+                    return Futures.successful((Object)new RegisterDataTreeChangeListenerReply(getRef()));
+                }
+            };
+
+            doAnswer(answer).when(actorContext).executeOperationAsync(any(ActorRef.class),
+                    any(Object.class), any(Timeout.class));
+
+            proxy.init(shardName, YangInstanceIdentifier.of(TestModel.TEST_QNAME));
+
+            expectMsgClass(duration("5 seconds"), CloseDataTreeChangeListenerRegistration.class);
+
+            Assert.assertEquals("getListenerRegistrationActor", null,
+                    proxy.getListenerRegistrationActor());
+        }};
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerRegistrationActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerRegistrationActorTest.java
new file mode 100644 (file)
index 0000000..5695911
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataTreeChangeListenerRegistrationReply;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public class DataTreeChangeListenerRegistrationActorTest extends AbstractActorTest {
+    private static final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+
+    static {
+        store.onGlobalContextUpdated(TestModel.createTestContext());
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Test
+    public void testOnReceiveCloseListenerRegistration() throws Exception {
+        new JavaTestKit(getSystem()) {{
+            final ListenerRegistration mockListenerReg = Mockito.mock(ListenerRegistration.class);
+            final Props props = DataTreeChangeListenerRegistrationActor.props(mockListenerReg);
+            final ActorRef subject = getSystem().actorOf(props, "testCloseListenerRegistration");
+
+            subject.tell(CloseDataTreeChangeListenerRegistration.getInstance(), getRef());
+
+            expectMsgClass(duration("1 second"), CloseDataTreeChangeListenerRegistrationReply.class);
+
+            Mockito.verify(mockListenerReg).close();
+        }};
+    }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListenerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ForwardingDataTreeChangeListenerTest.java
new file mode 100644 (file)
index 0000000..fb8baf1
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.cluster.datastore.messages.DataTreeChanged;
+import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+public class ForwardingDataTreeChangeListenerTest extends AbstractActorTest {
+
+    @Test
+    public void testOnDataChanged() throws Exception {
+        final Props props = Props.create(MessageCollectorActor.class);
+        final ActorRef actorRef = getSystem().actorOf(props);
+
+        ForwardingDataTreeChangeListener forwardingListener = new ForwardingDataTreeChangeListener(
+                getSystem().actorSelection(actorRef.path()));
+
+        Collection<DataTreeCandidate> expected = Arrays.asList(Mockito.mock(DataTreeCandidate.class));
+        forwardingListener.onDataTreeChanged(expected);
+
+        DataTreeChanged actual = MessageCollectorActor.expectFirstMatching(actorRef, DataTreeChanged.class);
+        Assert.assertSame(expected, actual.getChanges());
+    }
+}
index 22ce50b90dff49f0f960176741adeb214c913e9e..4cbc121a950e87d22228e66418d5a77c96de3d0f 100644 (file)
@@ -25,7 +25,6 @@ import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.Uninterruptibles;
 import java.io.IOException;
 import java.util.Collections;
@@ -34,7 +33,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -59,6 +57,8 @@ import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply;
 import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
 import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
 import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterDataTreeChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterDataTreeChangeListenerReply;
 import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
 import org.opendaylight.controller.cluster.datastore.modification.DeleteModification;
 import org.opendaylight.controller.cluster.datastore.modification.MergeModification;
@@ -68,6 +68,7 @@ import org.opendaylight.controller.cluster.datastore.modification.MutableComposi
 import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
 import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
 import org.opendaylight.controller.cluster.datastore.utils.MockDataChangeListener;
+import org.opendaylight.controller.cluster.datastore.utils.MockDataTreeChangeListener;
 import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils;
 import org.opendaylight.controller.cluster.notifications.RegisterRoleChangeListener;
 import org.opendaylight.controller.cluster.notifications.RegisterRoleChangeListenerReply;
@@ -88,11 +89,9 @@ import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelpe
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
 import org.opendaylight.controller.protobuff.messages.cohort3pc.ThreePhaseCommitCohortMessages;
 import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -100,6 +99,13 @@ import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -108,6 +114,7 @@ import scala.concurrent.Future;
 import scala.concurrent.duration.FiniteDuration;
 
 public class ShardTest extends AbstractShardTest {
+    private static final QName CARS_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:cars", "2014-03-13", "cars");
 
     @Test
     public void testRegisterChangeListener() throws Exception {
@@ -242,6 +249,110 @@ public class ShardTest extends AbstractShardTest {
         }};
     }
 
+    @Test
+    public void testRegisterDataTreeChangeListener() throws Exception {
+        new ShardTestKit(getSystem()) {{
+            TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+                    newShardProps(), "testRegisterDataTreeChangeListener");
+
+            waitUntilLeader(shard);
+
+            shard.tell(new UpdateSchemaContext(SchemaContextHelper.full()), ActorRef.noSender());
+
+            MockDataTreeChangeListener listener = new MockDataTreeChangeListener(1);
+            ActorRef dclActor = getSystem().actorOf(DataTreeChangeListenerActor.props(listener),
+                    "testRegisterDataTreeChangeListener-DataTreeChangeListener");
+
+            shard.tell(new RegisterDataTreeChangeListener(TestModel.TEST_PATH, dclActor), getRef());
+
+            RegisterDataTreeChangeListenerReply reply = expectMsgClass(duration("3 seconds"),
+                    RegisterDataTreeChangeListenerReply.class);
+            String replyPath = reply.getListenerRegistrationPath().toString();
+            assertTrue("Incorrect reply path: " + replyPath, replyPath.matches(
+                    "akka:\\/\\/test\\/user\\/testRegisterDataTreeChangeListener\\/\\$.*"));
+
+            YangInstanceIdentifier path = TestModel.TEST_PATH;
+            writeToStore(shard, path, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+            listener.waitForChangeEvents();
+
+            dclActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+        }};
+    }
+
+    @SuppressWarnings("serial")
+    @Test
+    public void testDataTreeChangeListenerNotifiedWhenNotTheLeaderOnRegistration() throws Exception {
+        new ShardTestKit(getSystem()) {{
+            final CountDownLatch onFirstElectionTimeout = new CountDownLatch(1);
+            final CountDownLatch onChangeListenerRegistered = new CountDownLatch(1);
+            Creator<Shard> creator = new Creator<Shard>() {
+                boolean firstElectionTimeout = true;
+
+                @Override
+                public Shard create() throws Exception {
+                    return new Shard(shardID, Collections.<String,String>emptyMap(),
+                            dataStoreContextBuilder.persistent(false).build(), SCHEMA_CONTEXT) {
+                        @Override
+                        public void onReceiveCommand(final Object message) throws Exception {
+                            if(message instanceof ElectionTimeout && firstElectionTimeout) {
+                                firstElectionTimeout = false;
+                                final ActorRef self = getSelf();
+                                new Thread() {
+                                    @Override
+                                    public void run() {
+                                        Uninterruptibles.awaitUninterruptibly(
+                                                onChangeListenerRegistered, 5, TimeUnit.SECONDS);
+                                        self.tell(message, self);
+                                    }
+                                }.start();
+
+                                onFirstElectionTimeout.countDown();
+                            } else {
+                                super.onReceiveCommand(message);
+                            }
+                        }
+                    };
+                }
+            };
+
+            MockDataTreeChangeListener listener = new MockDataTreeChangeListener(1);
+            ActorRef dclActor = getSystem().actorOf(DataTreeChangeListenerActor.props(listener),
+                    "testDataTreeChangeListenerNotifiedWhenNotTheLeaderOnRegistration-DataChangeListener");
+
+            TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+                    Props.create(new DelegatingShardCreator(creator)),
+                    "testDataTreeChangeListenerNotifiedWhenNotTheLeaderOnRegistration");
+
+            YangInstanceIdentifier path = TestModel.TEST_PATH;
+            writeToStore(shard, path, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+            assertEquals("Got first ElectionTimeout", true,
+                    onFirstElectionTimeout.await(5, TimeUnit.SECONDS));
+
+            shard.tell(new RegisterDataTreeChangeListener(path, dclActor), getRef());
+            RegisterDataTreeChangeListenerReply reply = expectMsgClass(duration("5 seconds"),
+                    RegisterDataTreeChangeListenerReply.class);
+            assertNotNull("getListenerRegistratioznPath", reply.getListenerRegistrationPath());
+
+            shard.tell(new FindLeader(), getRef());
+            FindLeaderReply findLeadeReply =
+                    expectMsgClass(duration("5 seconds"), FindLeaderReply.class);
+            assertNull("Expected the shard not to be the leader", findLeadeReply.getLeaderActor());
+
+            writeToStore(shard, path, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+            onChangeListenerRegistered.countDown();
+
+            // TODO: investigate why we do not receive data chage events
+            listener.waitForChangeEvents();
+
+            dclActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+        }};
+    }
+
     @Test
     public void testCreateTransaction(){
         new ShardTestKit(getSystem()) {{
@@ -379,10 +490,25 @@ public class ShardTest extends AbstractShardTest {
     }
 
     @Test
-    public void testRecovery() throws Exception {
+    public void testApplyStateWithCandidatePayload() throws Exception {
 
-        // Set up the InMemorySnapshotStore.
+        TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(), "testApplyState");
+
+        NormalizedNode<?, ?> node = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+        DataTreeCandidate candidate = DataTreeCandidates.fromNormalizedNode(TestModel.TEST_PATH, node);
 
+        ApplyState applyState = new ApplyState(null, "test", new ReplicatedLogImplEntry(1, 2,
+                DataTreeCandidatePayload.create(candidate)));
+
+        shard.underlyingActor().onReceiveCommand(applyState);
+
+        NormalizedNode<?,?> actual = readStore(shard, TestModel.TEST_PATH);
+        assertEquals("Applied state", node, actual);
+
+        shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+    }
+
+    DataTree setupInMemorySnapshotStore() throws DataValidationFailedException {
         DataTree testStore = InMemoryDataTreeFactory.getInstance().create();
         testStore.setSchemaContext(SCHEMA_CONTEXT);
 
@@ -393,6 +519,55 @@ public class ShardTest extends AbstractShardTest {
         InMemorySnapshotStore.addSnapshot(shardID.toString(), Snapshot.create(
                 SerializationUtils.serializeNormalizedNode(root),
                 Collections.<ReplicatedLogEntry>emptyList(), 0, 1, -1, -1));
+        return testStore;
+    }
+
+    private static DataTreeCandidatePayload payloadForModification(DataTree source, DataTreeModification mod) throws DataValidationFailedException {
+        source.validate(mod);
+        final DataTreeCandidate candidate = source.prepare(mod);
+        source.commit(candidate);
+        return DataTreeCandidatePayload.create(candidate);
+    }
+
+    @Test
+    public void testDataTreeCandidateRecovery() throws Exception {
+        // Set up the InMemorySnapshotStore.
+        final DataTree source = setupInMemorySnapshotStore();
+
+        final DataTreeModification writeMod = source.takeSnapshot().newModification();
+        writeMod.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+
+        // Set up the InMemoryJournal.
+        InMemoryJournal.addEntry(shardID.toString(), 0, new ReplicatedLogImplEntry(0, 1, payloadForModification(source, writeMod)));
+
+        int nListEntries = 16;
+        Set<Integer> listEntryKeys = new HashSet<>();
+
+        // Add some ModificationPayload entries
+        for (int i = 1; i <= nListEntries; i++) {
+            listEntryKeys.add(Integer.valueOf(i));
+
+            YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+                    .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build();
+
+            final DataTreeModification mod = source.takeSnapshot().newModification();
+            mod.merge(path, ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i));
+
+            InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1,
+                payloadForModification(source, mod)));
+        }
+
+        InMemoryJournal.addEntry(shardID.toString(), nListEntries + 1,
+                new ApplyJournalEntries(nListEntries));
+
+        testRecovery(listEntryKeys);
+    }
+
+    @Test
+    public void testModicationRecovery() throws Exception {
+
+        // Set up the InMemorySnapshotStore.
+        setupInMemorySnapshotStore();
 
         // Set up the InMemoryJournal.
 
@@ -420,7 +595,7 @@ public class ShardTest extends AbstractShardTest {
         testRecovery(listEntryKeys);
     }
 
-    private ModificationPayload newModificationPayload(final Modification... mods) throws IOException {
+    private static ModificationPayload newModificationPayload(final Modification... mods) throws IOException {
         MutableCompositeModification compMod = new MutableCompositeModification();
         for(Modification mod: mods) {
             compMod.addModification(mod);
@@ -429,7 +604,6 @@ public class ShardTest extends AbstractShardTest {
         return new ModificationPayload(compMod);
     }
 
-    @SuppressWarnings({ "unchecked" })
     @Test
     public void testConcurrentThreePhaseCommits() throws Throwable {
         new ShardTestKit(getSystem()) {{
@@ -445,19 +619,19 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
+            ShardDataTreeCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
                     TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), modification1);
 
             String transactionID2 = "tx2";
             MutableCompositeModification modification2 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
+            ShardDataTreeCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
                     TestModel.OUTER_LIST_PATH,
                     ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(),
                     modification2);
 
             String transactionID3 = "tx3";
             MutableCompositeModification modification3 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
+            ShardDataTreeCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
                     YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
                         .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build(),
                     ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1),
@@ -605,12 +779,12 @@ public class ShardTest extends AbstractShardTest {
         }};
     }
 
-    private BatchedModifications newBatchedModifications(String transactionID, YangInstanceIdentifier path,
+    private static BatchedModifications newBatchedModifications(String transactionID, YangInstanceIdentifier path,
             NormalizedNode<?, ?> data, boolean ready, boolean doCommitOnReady) {
         return newBatchedModifications(transactionID, null, path, data, ready, doCommitOnReady);
     }
 
-    private BatchedModifications newBatchedModifications(String transactionID, String transactionChainID,
+    private static BatchedModifications newBatchedModifications(String transactionID, String transactionChainID,
             YangInstanceIdentifier path, NormalizedNode<?, ?> data, boolean ready, boolean doCommitOnReady) {
         BatchedModifications batched = new BatchedModifications(transactionID, CURRENT_VERSION, transactionChainID);
         batched.addModification(new WriteModification(path, data));
@@ -631,10 +805,10 @@ public class ShardTest extends AbstractShardTest {
             final String transactionID = "tx";
             FiniteDuration duration = duration("5 seconds");
 
-            final AtomicReference<DOMStoreThreePhaseCommitCohort> mockCohort = new AtomicReference<>();
+            final AtomicReference<ShardDataTreeCohort> mockCohort = new AtomicReference<>();
             ShardCommitCoordinator.CohortDecorator cohortDecorator = new ShardCommitCoordinator.CohortDecorator() {
                 @Override
-                public DOMStoreThreePhaseCommitCohort decorate(String txID, DOMStoreThreePhaseCommitCohort actual) {
+                public ShardDataTreeCohort decorate(String txID, ShardDataTreeCohort actual) {
                     if(mockCohort.get() == null) {
                         mockCohort.set(createDelegatingMockCohort("cohort", actual));
                     }
@@ -699,10 +873,10 @@ public class ShardTest extends AbstractShardTest {
             final String transactionID = "tx";
             FiniteDuration duration = duration("5 seconds");
 
-            final AtomicReference<DOMStoreThreePhaseCommitCohort> mockCohort = new AtomicReference<>();
+            final AtomicReference<ShardDataTreeCohort> mockCohort = new AtomicReference<>();
             ShardCommitCoordinator.CohortDecorator cohortDecorator = new ShardCommitCoordinator.CohortDecorator() {
                 @Override
-                public DOMStoreThreePhaseCommitCohort decorate(String txID, DOMStoreThreePhaseCommitCohort actual) {
+                public ShardDataTreeCohort decorate(String txID, ShardDataTreeCohort actual) {
                     if(mockCohort.get() == null) {
                         mockCohort.set(createDelegatingMockCohort("cohort", actual));
                     }
@@ -745,7 +919,7 @@ public class ShardTest extends AbstractShardTest {
     }
 
     @SuppressWarnings("unchecked")
-    private void verifyOuterListEntry(final TestActorRef<Shard> shard, Object expIDValue) throws Exception {
+    private static void verifyOuterListEntry(final TestActorRef<Shard> shard, Object expIDValue) throws Exception {
         NormalizedNode<?, ?> outerList = readStore(shard, TestModel.OUTER_LIST_PATH);
         assertNotNull(TestModel.OUTER_LIST_QNAME.getLocalName() + " not found", outerList);
         assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " value is not Iterable",
@@ -818,6 +992,8 @@ public class ShardTest extends AbstractShardTest {
         final AtomicBoolean overrideLeaderCalls = new AtomicBoolean();
         new ShardTestKit(getSystem()) {{
             Creator<Shard> creator = new Creator<Shard>() {
+                private static final long serialVersionUID = 1L;
+
                 @Override
                 public Shard create() throws Exception {
                     return new Shard(shardID, Collections.<String,String>emptyMap(),
@@ -867,7 +1043,7 @@ public class ShardTest extends AbstractShardTest {
             String transactionID = "tx1";
             MutableCompositeModification modification = new MutableCompositeModification();
             NormalizedNode<?, ?> containerNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
-            DOMStoreThreePhaseCommitCohort cohort = setupMockWriteTransaction("cohort", dataStore,
+            ShardDataTreeCohort cohort = setupMockWriteTransaction("cohort", dataStore,
                     TestModel.TEST_PATH, containerNode, modification);
 
             FiniteDuration duration = duration("5 seconds");
@@ -909,7 +1085,7 @@ public class ShardTest extends AbstractShardTest {
             String transactionID = "tx";
             MutableCompositeModification modification = new MutableCompositeModification();
             NormalizedNode<?, ?> containerNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
-            DOMStoreThreePhaseCommitCohort cohort = setupMockWriteTransaction("cohort", dataStore,
+            ShardDataTreeCohort cohort = setupMockWriteTransaction("cohort", dataStore,
                     TestModel.TEST_PATH, containerNode, modification);
 
             FiniteDuration duration = duration("5 seconds");
@@ -945,6 +1121,25 @@ public class ShardTest extends AbstractShardTest {
         }};
     }
 
+    private static DataTreeCandidateTip mockCandidate(final String name) {
+        DataTreeCandidateTip mockCandidate = mock(DataTreeCandidateTip.class, name);
+        DataTreeCandidateNode mockCandidateNode = mock(DataTreeCandidateNode.class, name + "-node");
+        doReturn(ModificationType.WRITE).when(mockCandidateNode).getModificationType();
+        doReturn(Optional.of(ImmutableNodes.containerNode(CARS_QNAME))).when(mockCandidateNode).getDataAfter();
+        doReturn(YangInstanceIdentifier.builder().build()).when(mockCandidate).getRootPath();
+        doReturn(mockCandidateNode).when(mockCandidate).getRootNode();
+        return mockCandidate;
+    }
+
+    private static DataTreeCandidateTip mockUnmodifiedCandidate(final String name) {
+        DataTreeCandidateTip mockCandidate = mock(DataTreeCandidateTip.class, name);
+        DataTreeCandidateNode mockCandidateNode = mock(DataTreeCandidateNode.class, name + "-node");
+        doReturn(ModificationType.UNMODIFIED).when(mockCandidateNode).getModificationType();
+        doReturn(YangInstanceIdentifier.builder().build()).when(mockCandidate).getRootPath();
+        doReturn(mockCandidateNode).when(mockCandidate).getRootNode();
+        return mockCandidate;
+    }
+
     @Test
     public void testCommitWhenTransactionHasNoModifications(){
         // Note that persistence is enabled which would normally result in the entry getting written to the journal
@@ -959,10 +1154,11 @@ public class ShardTest extends AbstractShardTest {
 
                 String transactionID = "tx1";
                 MutableCompositeModification modification = new MutableCompositeModification();
-                DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+                ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, "cohort1");
                 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).canCommit();
                 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).preCommit();
                 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).commit();
+                doReturn(mockUnmodifiedCandidate("cohort1-candidate")).when(cohort).getCandidate();
 
                 FiniteDuration duration = duration("5 seconds");
 
@@ -1014,10 +1210,11 @@ public class ShardTest extends AbstractShardTest {
                 String transactionID = "tx1";
                 MutableCompositeModification modification = new MutableCompositeModification();
                 modification.addModification(new DeleteModification(YangInstanceIdentifier.builder().build()));
-                DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+                ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, "cohort1");
                 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).canCommit();
                 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).preCommit();
                 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).commit();
+                doReturn(mockCandidate("cohort1-candidate")).when(cohort).getCandidate();
 
                 FiniteDuration duration = duration("5 seconds");
 
@@ -1070,14 +1267,15 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort1 = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort1).canCommit();
             doReturn(Futures.immediateFuture(null)).when(cohort1).preCommit();
             doReturn(Futures.immediateFailedFuture(new IllegalStateException("mock"))).when(cohort1).commit();
+            doReturn(mockCandidate("cohort1-candidate")).when(cohort1).getCandidate();
 
             String transactionID2 = "tx2";
             MutableCompositeModification modification2 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort2 = mock(DOMStoreThreePhaseCommitCohort.class, "cohort2");
+            ShardDataTreeCohort cohort2 = mock(ShardDataTreeCohort.class, "cohort2");
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort2).canCommit();
 
             FiniteDuration duration = duration("5 seconds");
@@ -1146,13 +1344,13 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort1 = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort1).canCommit();
             doReturn(Futures.immediateFailedFuture(new IllegalStateException("mock"))).when(cohort1).preCommit();
 
             String transactionID2 = "tx2";
             MutableCompositeModification modification2 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort2 = mock(DOMStoreThreePhaseCommitCohort.class, "cohort2");
+            ShardDataTreeCohort cohort2 = mock(ShardDataTreeCohort.class, "cohort2");
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort2).canCommit();
 
             FiniteDuration duration = duration("5 seconds");
@@ -1222,7 +1420,7 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFailedFuture(new IllegalStateException("mock"))).when(cohort).canCommit();
 
             // Simulate the ForwardedReadyTransaction messages that would be sent
@@ -1270,7 +1468,7 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFuture(Boolean.FALSE)).when(cohort).canCommit();
 
             // Simulate the ForwardedReadyTransaction messages that would be sent
@@ -1320,7 +1518,7 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFailedFuture(new IllegalStateException("mock"))).when(cohort).canCommit();
 
             // Simulate the ForwardedReadyTransaction messages that would be sent
@@ -1339,6 +1537,11 @@ public class ShardTest extends AbstractShardTest {
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).canCommit();
             doReturn(Futures.immediateFuture(null)).when(cohort).preCommit();
             doReturn(Futures.immediateFuture(null)).when(cohort).commit();
+            DataTreeCandidateTip candidate = mock(DataTreeCandidateTip.class);
+            DataTreeCandidateNode candidateRoot = mock(DataTreeCandidateNode.class);
+            doReturn(ModificationType.UNMODIFIED).when(candidateRoot).getModificationType();
+            doReturn(candidateRoot).when(candidate).getRootNode();
+            doReturn(candidate).when(cohort).getCandidate();
 
             shard.tell(new ForwardedReadyTransaction(transactionID2, CURRENT_VERSION,
                     cohort, modification, true, true), getRef());
@@ -1362,7 +1565,7 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID = "tx1";
             MutableCompositeModification modification = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFuture(Boolean.FALSE)).when(cohort).canCommit();
 
             // Simulate the ForwardedReadyTransaction messages that would be sent
@@ -1381,6 +1584,11 @@ public class ShardTest extends AbstractShardTest {
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort).canCommit();
             doReturn(Futures.immediateFuture(null)).when(cohort).preCommit();
             doReturn(Futures.immediateFuture(null)).when(cohort).commit();
+            DataTreeCandidateTip candidate = mock(DataTreeCandidateTip.class);
+            DataTreeCandidateNode candidateRoot = mock(DataTreeCandidateNode.class);
+            doReturn(ModificationType.UNMODIFIED).when(candidateRoot).getModificationType();
+            doReturn(candidateRoot).when(candidate).getRootNode();
+            doReturn(candidate).when(cohort).getCandidate();
 
             shard.tell(new ForwardedReadyTransaction(transactionID2, CURRENT_VERSION,
                     cohort, modification, true, true), getRef());
@@ -1404,10 +1612,10 @@ public class ShardTest extends AbstractShardTest {
             ShardDataTree dataStore = shard.underlyingActor().getDataStore();
 
             final String transactionID = "tx1";
-            Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>> preCommit =
-                          new Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>>() {
+            Function<ShardDataTreeCohort, ListenableFuture<Void>> preCommit =
+                          new Function<ShardDataTreeCohort, ListenableFuture<Void>>() {
                 @Override
-                public ListenableFuture<Void> apply(final DOMStoreThreePhaseCommitCohort cohort) {
+                public ListenableFuture<Void> apply(final ShardDataTreeCohort cohort) {
                     ListenableFuture<Void> preCommitFuture = cohort.preCommit();
 
                     // Simulate an AbortTransaction message occurring during replication, after
@@ -1425,7 +1633,7 @@ public class ShardTest extends AbstractShardTest {
             };
 
             MutableCompositeModification modification = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort = setupMockWriteTransaction("cohort1", dataStore,
+            ShardDataTreeCohort cohort = setupMockWriteTransaction("cohort1", dataStore,
                     TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME),
                     modification, preCommit);
 
@@ -1475,7 +1683,7 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
+            ShardDataTreeCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
                     YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
                         .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build(),
                     ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1),
@@ -1487,7 +1695,7 @@ public class ShardTest extends AbstractShardTest {
             MutableCompositeModification modification2 = new MutableCompositeModification();
             YangInstanceIdentifier listNodePath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
                 .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2).build();
-            DOMStoreThreePhaseCommitCohort cohort2 = setupMockWriteTransaction("cohort3", dataStore,
+            ShardDataTreeCohort cohort2 = setupMockWriteTransaction("cohort3", dataStore,
                     listNodePath,
                     ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2),
                     modification2);
@@ -1546,19 +1754,19 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
+            ShardDataTreeCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
                     TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), modification1);
 
             String transactionID2 = "tx2";
             MutableCompositeModification modification2 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
+            ShardDataTreeCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
                     TestModel.OUTER_LIST_PATH,
                     ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(),
                     modification2);
 
             String transactionID3 = "tx3";
             MutableCompositeModification modification3 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
+            ShardDataTreeCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
                     TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), modification3);
 
             // Ready the Tx's
@@ -1620,13 +1828,13 @@ public class ShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = mock(DOMStoreThreePhaseCommitCohort.class, "cohort1");
+            ShardDataTreeCohort cohort1 = mock(ShardDataTreeCohort.class, "cohort1");
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort1).canCommit();
             doReturn(Futures.immediateFuture(null)).when(cohort1).abort();
 
             String transactionID2 = "tx2";
             MutableCompositeModification modification2 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort2 = mock(DOMStoreThreePhaseCommitCohort.class, "cohort2");
+            ShardDataTreeCohort cohort2 = mock(ShardDataTreeCohort.class, "cohort2");
             doReturn(Futures.immediateFuture(Boolean.TRUE)).when(cohort2).canCommit();
 
             FiniteDuration duration = duration("5 seconds");
@@ -1782,29 +1990,29 @@ public class ShardTest extends AbstractShardTest {
     /**
      * This test simply verifies that the applySnapShot logic will work
      * @throws ReadFailedException
+     * @throws DataValidationFailedException
      */
     @Test
-    public void testInMemoryDataStoreRestore() throws ReadFailedException {
-        InMemoryDOMDataStore store = new InMemoryDOMDataStore("test", MoreExecutors.sameThreadExecutor());
-
-        store.onGlobalContextUpdated(SCHEMA_CONTEXT);
+    public void testInMemoryDataTreeRestore() throws ReadFailedException, DataValidationFailedException {
+        DataTree store = InMemoryDataTreeFactory.getInstance().create();
+        store.setSchemaContext(SCHEMA_CONTEXT);
 
-        DOMStoreWriteTransaction putTransaction = store.newWriteOnlyTransaction();
+        DataTreeModification putTransaction = store.takeSnapshot().newModification();
         putTransaction.write(TestModel.TEST_PATH,
             ImmutableNodes.containerNode(TestModel.TEST_QNAME));
-        commitTransaction(putTransaction);
+        commitTransaction(store, putTransaction);
 
 
-        NormalizedNode<?, ?> expected = readStore(store);
+        NormalizedNode<?, ?> expected = readStore(store, YangInstanceIdentifier.builder().build());
 
-        DOMStoreWriteTransaction writeTransaction = store.newWriteOnlyTransaction();
+        DataTreeModification writeTransaction = store.takeSnapshot().newModification();
 
         writeTransaction.delete(YangInstanceIdentifier.builder().build());
         writeTransaction.write(YangInstanceIdentifier.builder().build(), expected);
 
-        commitTransaction(writeTransaction);
+        commitTransaction(store, writeTransaction);
 
-        NormalizedNode<?, ?> actual = readStore(store);
+        NormalizedNode<?, ?> actual = readStore(store, YangInstanceIdentifier.builder().build());
 
         assertEquals(expected, actual);
     }
@@ -1913,15 +2121,9 @@ public class ShardTest extends AbstractShardTest {
         shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
     }
 
-    private void commitTransaction(final DOMStoreWriteTransaction transaction) {
-        DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready();
-        ListenableFuture<Void> future =
-            commitCohort.preCommit();
-        try {
-            future.get();
-            future = commitCohort.commit();
-            future.get();
-        } catch (InterruptedException | ExecutionException e) {
-        }
+    private static void commitTransaction(DataTree store, final DataTreeModification modification) throws DataValidationFailedException {
+        modification.ready();
+        store.validate(modification);
+        store.commit(store.prepare(modification));
     }
 }
index 93c6ddbe7398be9b2054bb67a5b9b69bac47c09c..844feb2f47e988b89498a80946d97aecbff8c049 100644 (file)
@@ -130,7 +130,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest {
         if (exToThrow instanceof PrimaryNotFoundException) {
             doReturn(Futures.failed(exToThrow)).when(mockActorContext).findPrimaryShardAsync(anyString());
         } else {
-            doReturn(Futures.successful(getSystem().actorSelection(actorRef.path()))).
+            doReturn(primaryShardInfoReply(getSystem(), actorRef)).
                     when(mockActorContext).findPrimaryShardAsync(anyString());
         }
 
@@ -209,7 +209,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest {
         doReturn(getSystem().actorSelection(actorRef.path())).when(mockActorContext).
             actorSelection(actorRef.path().toString());
 
-        doReturn(Futures.successful(getSystem().actorSelection(actorRef.path()))).
+        doReturn(primaryShardInfoReply(getSystem(), actorRef)).
             when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD));
 
         doReturn(Futures.successful(new Object())).when(mockActorContext).executeOperationAsync(
@@ -834,7 +834,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest {
                 when(mockActorContext).actorSelection(shardActorRef.path().toString());
 
         if(shardFound) {
-            doReturn(Futures.successful(actorSystem.actorSelection(shardActorRef.path()))).
+            doReturn(primaryShardInfoReply(actorSystem, shardActorRef)).
                     when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD));
         } else {
             doReturn(Futures.failed(new PrimaryNotFoundException("test")))
@@ -1399,7 +1399,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest {
         doReturn(getSystem().actorSelection(shardActorRef.path())).
                 when(mockActorContext).actorSelection(shardActorRef.path().toString());
 
-        doReturn(Futures.successful(getSystem().actorSelection(shardActorRef.path()))).
+        doReturn(primaryShardInfoReply(getSystem(), shardActorRef)).
                 when(mockActorContext).findPrimaryShardAsync(eq(shardName));
 
         doReturn(true).when(mockActorContext).isPathLocal(shardActorRef.path().toString());
index 96cd3e45ebb0441dc93f86fe913d17dc5c19c751..a2309be48f4e09f5b102418611bf7d035943a589 100644 (file)
@@ -32,6 +32,7 @@ import org.mockito.InOrder;
 import org.opendaylight.controller.cluster.datastore.AbstractShardTest;
 import org.opendaylight.controller.cluster.datastore.Shard;
 import org.opendaylight.controller.cluster.datastore.ShardDataTree;
+import org.opendaylight.controller.cluster.datastore.ShardDataTreeCohort;
 import org.opendaylight.controller.cluster.datastore.ShardTestKit;
 import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply;
@@ -57,7 +58,6 @@ import org.opendaylight.controller.cluster.raft.utils.InMemoryJournal;
 import org.opendaylight.controller.cluster.raft.utils.InMemorySnapshotStore;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
@@ -219,19 +219,19 @@ public class PreLithiumShardTest extends AbstractShardTest {
 
             String transactionID1 = "tx1";
             MutableCompositeModification modification1 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
+            ShardDataTreeCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
                     TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), modification1);
 
             String transactionID2 = "tx2";
             MutableCompositeModification modification2 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
+            ShardDataTreeCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
                     TestModel.OUTER_LIST_PATH,
                     ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(),
                     modification2);
 
             String transactionID3 = "tx3";
             MutableCompositeModification modification3 = new MutableCompositeModification();
-            DOMStoreThreePhaseCommitCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
+            ShardDataTreeCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
                     YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
                         .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build(),
                     ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1),
index bbfff70e2dac8623cb66328a023a5fd1cf62ea99..7016ada5257c034d5b1c64557e5d8ed9e8ed21fb 100644 (file)
@@ -8,7 +8,7 @@
 package org.opendaylight.controller.cluster.datastore.modification;
 
 import static org.junit.Assert.assertEquals;
-import org.apache.commons.lang.SerializationUtils;
+import org.apache.commons.lang3.SerializationUtils;
 import org.junit.Test;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -43,8 +43,7 @@ public class ModificationPayloadTest {
         assertEquals("getPath", writePath, write.getPath());
         assertEquals("getData", writeData, write.getData());
 
-        ModificationPayload cloned =
-                (ModificationPayload) SerializationUtils.clone(payload);
+        ModificationPayload cloned = SerializationUtils.clone(payload);
 
         deserialized = (MutableCompositeModification) payload.getModification();
 
index 6b4f6337785a753e68e1330f8296927aeb70b005..bc80937897c97104a7b8a17ac1d0cf9eff672143 100644 (file)
@@ -42,6 +42,7 @@ import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
 import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
 import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
 import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import scala.concurrent.Await;
@@ -411,32 +412,36 @@ public class ActorContextTest extends AbstractActorTest{
             DatastoreContext dataStoreContext = DatastoreContext.newBuilder().dataStoreType("config").
                     shardLeaderElectionTimeout(100, TimeUnit.MILLISECONDS).build();
 
+            final String expPrimaryPath = "akka://test-system/find-primary-shard";
             ActorContext actorContext =
                     new ActorContext(getSystem(), shardManager, mock(ClusterWrapper.class),
                             mock(Configuration.class), dataStoreContext) {
                         @Override
                         protected Future<Object> doAsk(ActorRef actorRef, Object message, Timeout timeout) {
-                            return Futures.successful((Object) new PrimaryFound("akka://test-system/test"));
+                            return Futures.successful((Object) new PrimaryFound(expPrimaryPath));
                         }
                     };
 
 
-            Future<ActorSelection> foobar = actorContext.findPrimaryShardAsync("foobar");
-            ActorSelection actual = Await.result(foobar, Duration.apply(5000, TimeUnit.MILLISECONDS));
+            Future<PrimaryShardInfo> foobar = actorContext.findPrimaryShardAsync("foobar");
+            PrimaryShardInfo actual = Await.result(foobar, Duration.apply(5000, TimeUnit.MILLISECONDS));
 
             assertNotNull(actual);
+            assertEquals("LocalShardDataTree present", false, actual.getLocalShardDataTree().isPresent());
+            assertTrue("Unexpected PrimaryShardActor path " + actual.getPrimaryShardActor().path(),
+                    expPrimaryPath.endsWith(actual.getPrimaryShardActor().pathString()));
 
-            Future<ActorSelection> cached = actorContext.getPrimaryShardActorSelectionCache().getIfPresent("foobar");
+            Future<PrimaryShardInfo> cached = actorContext.getPrimaryShardInfoCache().getIfPresent("foobar");
 
-            ActorSelection cachedSelection = Await.result(cached, FiniteDuration.apply(1, TimeUnit.MILLISECONDS));
+            PrimaryShardInfo cachedInfo = Await.result(cached, FiniteDuration.apply(1, TimeUnit.MILLISECONDS));
 
-            assertEquals(cachedSelection, actual);
+            assertEquals(cachedInfo, actual);
 
             // Wait for 200 Milliseconds. The cached entry should have been removed.
 
             Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS);
 
-            cached = actorContext.getPrimaryShardActorSelectionCache().getIfPresent("foobar");
+            cached = actorContext.getPrimaryShardInfoCache().getIfPresent("foobar");
 
             assertNull(cached);
 
@@ -461,7 +466,7 @@ public class ActorContextTest extends AbstractActorTest{
                     };
 
 
-            Future<ActorSelection> foobar = actorContext.findPrimaryShardAsync("foobar");
+            Future<PrimaryShardInfo> foobar = actorContext.findPrimaryShardAsync("foobar");
 
             try {
                 Await.result(foobar, Duration.apply(100, TimeUnit.MILLISECONDS));
@@ -470,7 +475,7 @@ public class ActorContextTest extends AbstractActorTest{
 
             }
 
-            Future<ActorSelection> cached = actorContext.getPrimaryShardActorSelectionCache().getIfPresent("foobar");
+            Future<PrimaryShardInfo> cached = actorContext.getPrimaryShardInfoCache().getIfPresent("foobar");
 
             assertNull(cached);
     }
@@ -494,7 +499,7 @@ public class ActorContextTest extends AbstractActorTest{
                     };
 
 
-            Future<ActorSelection> foobar = actorContext.findPrimaryShardAsync("foobar");
+            Future<PrimaryShardInfo> foobar = actorContext.findPrimaryShardAsync("foobar");
 
             try {
                 Await.result(foobar, Duration.apply(100, TimeUnit.MILLISECONDS));
@@ -503,7 +508,7 @@ public class ActorContextTest extends AbstractActorTest{
 
             }
 
-            Future<ActorSelection> cached = actorContext.getPrimaryShardActorSelectionCache().getIfPresent("foobar");
+            Future<PrimaryShardInfo> cached = actorContext.getPrimaryShardInfoCache().getIfPresent("foobar");
 
             assertNull(cached);
     }
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataTreeChangeListener.java
new file mode 100644 (file)
index 0000000..d06fc43
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.controller.cluster.datastore.utils;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Uninterruptibles;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class MockDataTreeChangeListener implements DOMDataTreeChangeListener {
+
+    private final List<Collection<DataTreeCandidate>> changeList =
+            Collections.synchronizedList(Lists.<Collection<DataTreeCandidate>>newArrayList());
+
+    private volatile CountDownLatch changeLatch;
+    private int expChangeEventCount;
+
+    public MockDataTreeChangeListener(int expChangeEventCount) {
+        reset(expChangeEventCount);
+    }
+
+    public void reset(int expChangeEventCount) {
+        changeLatch = new CountDownLatch(expChangeEventCount);
+        this.expChangeEventCount = expChangeEventCount;
+        changeList.clear();
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
+        changeList.add(changes);
+        changeLatch.countDown();
+    }
+
+    public void waitForChangeEvents() {
+        boolean done = Uninterruptibles.awaitUninterruptibly(changeLatch, 5, TimeUnit.SECONDS);
+        if(!done) {
+            fail(String.format("Missing change notifications. Expected: %d. Actual: %d",
+                    expChangeEventCount, (expChangeEventCount - changeLatch.getCount())));
+        }
+    }
+
+    public void expectNoMoreChanges(String assertMsg) {
+        Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
+        assertEquals(assertMsg, expChangeEventCount, changeList.size());
+    }
+}
index 42406080361601de2d3c53bb91a8f9c62fc7a886..60420dcf236ac8d19a51bb9111bf7c9d3b6880f7 100644 (file)
@@ -7,54 +7,54 @@
  */
 package org.opendaylight.controller.md.cluster.datastore.model;
 
+import com.google.common.io.Resources;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
-import java.util.Set;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 public class TestModel {
 
-  public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
-          "test");
-
-  public static final QName JUNK_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:junk", "2014-03-13",
-          "junk");
-
-
-  public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
-  public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
-  public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
-  public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
-  public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
-  public static final QName DESC_QNAME = QName.create(TEST_QNAME, "desc");
-  public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
-  private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
-
-  public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
-  public static final YangInstanceIdentifier JUNK_PATH = YangInstanceIdentifier.of(JUNK_QNAME);
-  public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).
-          node(OUTER_LIST_QNAME).build();
-  public static final YangInstanceIdentifier INNER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).
-          node(OUTER_LIST_QNAME).node(INNER_LIST_QNAME).build();
-  public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two");
-  public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three");
-
-
-  public static final InputStream getDatastoreTestInputStream() {
-    return getInputStream(DATASTORE_TEST_YANG);
-  }
-
-  private static InputStream getInputStream(final String resourceName) {
-    return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
-  }
-
-  public static SchemaContext createTestContext() {
-    YangParserImpl parser = new YangParserImpl();
-    Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
-    return parser.resolveSchemaContext(modules);
-  }
+    public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+            "test");
+
+    public static final QName JUNK_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:junk", "2014-03-13",
+            "junk");
+
+
+    public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+    public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+    public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+    public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+    public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+    public static final QName DESC_QNAME = QName.create(TEST_QNAME, "desc");
+    public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+    private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+    public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+    public static final YangInstanceIdentifier JUNK_PATH = YangInstanceIdentifier.of(JUNK_QNAME);
+    public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).
+            node(OUTER_LIST_QNAME).build();
+    public static final YangInstanceIdentifier INNER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).
+            node(OUTER_LIST_QNAME).node(INNER_LIST_QNAME).build();
+    public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two");
+    public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three");
+
+
+    public static final InputStream getDatastoreTestInputStream() {
+        return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext() {
+        YangParserImpl parser = new YangParserImpl();
+        try {
+            return parser.parseSources(Collections.singleton(Resources.asByteSource(TestModel.class.getResource(DATASTORE_TEST_YANG))));
+        } catch (IOException | YangSyntaxErrorException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
 }
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataTreeListenerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataTreeListenerTest.java
new file mode 100644 (file)
index 0000000..c3038ca
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * 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.controller.md.sal.dom.broker.impl;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMDataTreeListenerTest {
+
+    private SchemaContext schemaContext;
+    private AbstractDOMDataBroker domBroker;
+    private ListeningExecutorService executor;
+    private ExecutorService futureExecutor;
+    private CommitExecutorService commitExecutor;
+
+    private static final DataContainerChild<?, ?> OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
+            .build();
+
+    private static final DataContainerChild<?, ?> OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
+            .build();
+
+    private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
+            .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+            .withChild(OUTER_LIST)
+            .build();
+
+    private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
+            .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+            .withChild(OUTER_LIST_2)
+            .build();
+
+    private static DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
+            LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
+
+    private static DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
+            LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
+
+    @Before
+    public void setupStore() {
+        InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
+                MoreExecutors.newDirectExecutorService());
+        InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
+                MoreExecutors.newDirectExecutorService());
+        schemaContext = TestModel.createTestContext();
+
+        operStore.onGlobalContextUpdated(schemaContext);
+        configStore.onGlobalContextUpdated(schemaContext);
+
+        ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder() //
+                .put(CONFIGURATION, configStore) //
+                .put(OPERATIONAL, operStore) //
+                .build();
+
+        commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
+        futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
+        executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
+                TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
+        domBroker = new SerializedDOMDataBroker(stores, executor);
+    }
+
+    @After
+    public void tearDown() {
+        if (executor != null) {
+            executor.shutdownNow();
+        }
+
+        if (futureExecutor != null) {
+            futureExecutor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void writeContainerEmptyTreeTest() throws InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+        final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(1, listener.getReceivedChanges().size());
+        final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
+        final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
+                new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
+        assertNotNull(modifiedChild);
+        checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
+        listenerReg.close();
+    }
+
+    @Test
+    public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
+        writeTx.submit().checkedGet();
+
+        latch.await(1, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
+        listenerReg.close();
+    }
+
+    @Test
+    public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
+        CountDownLatch latch = new CountDownLatch(2);
+
+        DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+        assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+                dataTreeChangeService);
+
+        DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+        writeTx.submit().checkedGet();
+
+        final TestDataTreeListener listener = new TestDataTreeListener(latch);
+        final ListenerRegistration<TestDataTreeListener> listenerReg =
+                dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
+
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
+
+        final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+        final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
+
+        final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                .withChild(outerListEntry2)
+                .withChild(outerListEntry3)
+                .build();
+
+        writeTx = domBroker.newWriteOnlyTransaction();
+        writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
+                outerListEntry2);
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
+                outerListEntry3);
+        writeTx.submit();
+
+        latch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(2, listener.getReceivedChanges().size());
+        Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+        assertEquals(1, changes.size());
+
+        DataTreeCandidate candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+        checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
+
+        changes = listener.getReceivedChanges().get(1);
+        assertEquals(1, changes.size());
+
+        candidate = changes.iterator().next();
+        assertNotNull(candidate);
+        candidateRoot = candidate.getRootNode();
+        checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
+        final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
+        checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
+        final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
+        checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
+        final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
+        checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
+        listenerReg.close();
+    }
+
+    private static void checkChange(NormalizedNode<?, ?> expectedBefore,
+                                    NormalizedNode<?, ?> expectedAfter,
+                                    ModificationType expectedMod,
+                                    DataTreeCandidateNode candidateNode) {
+        if (expectedBefore != null) {
+            assertTrue(candidateNode.getDataBefore().isPresent());
+            assertEquals(expectedBefore, candidateNode.getDataBefore().get());
+        } else {
+            assertFalse(candidateNode.getDataBefore().isPresent());
+        }
+
+        if (expectedAfter != null) {
+            assertTrue(candidateNode.getDataAfter().isPresent());
+            assertEquals(expectedAfter, candidateNode.getDataAfter().get());
+        } else {
+            assertFalse(candidateNode.getDataAfter().isPresent());
+        }
+
+        assertEquals(expectedMod, candidateNode.getModificationType());
+    }
+
+    private DOMDataTreeChangeService getDOMDataTreeChangeService() {
+        final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
+                .get(DOMDataTreeChangeService.class);
+        if (extension == null) {
+            return null;
+        }
+        DOMDataTreeChangeService dataTreeChangeService = null;
+        if (extension instanceof DOMDataTreeChangeService) {
+            dataTreeChangeService = (DOMDataTreeChangeService) extension;
+        }
+        return dataTreeChangeService;
+    }
+
+
+    static class CommitExecutorService extends ForwardingExecutorService {
+
+        ExecutorService delegate;
+
+        public CommitExecutorService(final ExecutorService delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        protected ExecutorService delegate() {
+            return delegate;
+        }
+    }
+
+    static class TestDataTreeListener implements DOMDataTreeChangeListener {
+
+        private List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
+        private CountDownLatch latch;
+
+        public TestDataTreeListener(final CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
+            receivedChanges.add(changes);
+            latch.countDown();
+        }
+
+        public List<Collection<DataTreeCandidate>> getReceivedChanges() {
+            return receivedChanges;
+        }
+    }
+}
index f043d2cb84274f870ea8e05fec14ee3ce4069042..106abca3ec44fd674c54dfe282a8eb8c73ba7ff0 100644 (file)
@@ -49,7 +49,8 @@ public abstract class AbstractDOMStoreTransaction<T> implements DOMStoreTransact
      * @return The context in which this transaction was allocated, or null
      *         if the context was not recorded.
      */
-    @Nullable public final Throwable getDebugContext() {
+    @Nullable
+    public final Throwable getDebugContext() {
         return debugContext;
     }
 
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java
new file mode 100644 (file)
index 0000000..b7776b2
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * 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.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot} supplier
+ * and backend commit coordinator.
+ *
+ * @param <T> transaction identifier type
+ */
+@Beta
+public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T> implements DOMStoreTransactionChain {
+    private static abstract class State {
+        /**
+         * Allocate a new snapshot.
+         *
+         * @return A new snapshot
+         */
+        protected abstract DataTreeSnapshot getSnapshot();
+    }
+
+    private static final class Idle extends State {
+        private final AbstractSnapshotBackedTransactionChain<?> chain;
+
+        Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
+            this.chain = Preconditions.checkNotNull(chain);
+        }
+
+        @Override
+        protected DataTreeSnapshot getSnapshot() {
+            return chain.takeSnapshot();
+        }
+    }
+
+    /**
+     * We have a transaction out there.
+     */
+    private static final class Allocated extends State {
+        private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
+                AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
+        private final DOMStoreWriteTransaction transaction;
+        private volatile DataTreeSnapshot snapshot;
+
+        Allocated(final DOMStoreWriteTransaction transaction) {
+            this.transaction = Preconditions.checkNotNull(transaction);
+        }
+
+        public DOMStoreWriteTransaction getTransaction() {
+            return transaction;
+        }
+
+        @Override
+        protected DataTreeSnapshot getSnapshot() {
+            final DataTreeSnapshot ret = snapshot;
+            Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
+            return ret;
+        }
+
+        void setSnapshot(final DataTreeSnapshot snapshot) {
+            final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
+            Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
+        }
+    }
+
+    /**
+     * Chain is logically shut down, no further allocation allowed.
+     */
+    private static final class Shutdown extends State {
+        private final String message;
+
+        Shutdown(final String message) {
+            this.message = Preconditions.checkNotNull(message);
+        }
+
+        @Override
+        protected DataTreeSnapshot getSnapshot() {
+            throw new IllegalStateException(message);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<AbstractSnapshotBackedTransactionChain, State> STATE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state");
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class);
+    private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
+    private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
+    private final Idle idleState;
+    private volatile State state;
+
+    protected AbstractSnapshotBackedTransactionChain() {
+        idleState = new Idle(this);
+        state = idleState;
+    }
+
+    private Entry<State, DataTreeSnapshot> getSnapshot() {
+        final State localState = state;
+        return new SimpleEntry<>(localState, localState.getSnapshot());
+    }
+
+    private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
+        final State state = new Allocated(transaction);
+        return STATE_UPDATER.compareAndSet(this, expected, state);
+    }
+
+    @Override
+    public final DOMStoreReadTransaction newReadOnlyTransaction() {
+        final Entry<State, DataTreeSnapshot> entry = getSnapshot();
+        return SnapshotBackedTransactions.newReadTransaction(nextTransactionIdentifier(), getDebugTransactions(), entry.getValue());
+    }
+
+    @Override
+    public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
+        Entry<State, DataTreeSnapshot> entry;
+        DOMStoreReadWriteTransaction ret;
+
+        do {
+            entry = getSnapshot();
+            ret = new SnapshotBackedReadWriteTransaction<T>(nextTransactionIdentifier(),
+                getDebugTransactions(), entry.getValue(), this);
+        } while (!recordTransaction(entry.getKey(), ret));
+
+        return ret;
+    }
+
+    @Override
+    public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
+        Entry<State, DataTreeSnapshot> entry;
+        DOMStoreWriteTransaction ret;
+
+        do {
+            entry = getSnapshot();
+            ret = new SnapshotBackedWriteTransaction<T>(nextTransactionIdentifier(),
+                getDebugTransactions(), entry.getValue(), this);
+        } while (!recordTransaction(entry.getKey(), ret));
+
+        return ret;
+    }
+
+    @Override
+    protected final void transactionAborted(final SnapshotBackedWriteTransaction<T> tx) {
+        final State localState = state;
+        if (localState instanceof Allocated) {
+            final Allocated allocated = (Allocated)localState;
+            if (allocated.getTransaction().equals(tx)) {
+                final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
+                if (!success) {
+                    LOG.warn("Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
+                        tx, this, localState, state);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+        final State localState = state;
+
+        if (localState instanceof Allocated) {
+            final Allocated allocated = (Allocated)localState;
+            final DOMStoreWriteTransaction transaction = allocated.getTransaction();
+            Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
+            allocated.setSnapshot(tree);
+        } else {
+            LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
+        }
+
+        return createCohort(tx, tree);
+    }
+
+    @Override
+    public final void close() {
+        final State localState = state;
+
+        do {
+            Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
+
+            if (FAILED.equals(localState)) {
+                LOG.debug("Ignoring user close in failed state");
+                return;
+            }
+        } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
+    }
+
+    /**
+     * Notify the base logic that a previously-submitted transaction has been committed successfully.
+     *
+     * @param transaction Transaction which completed successfully.
+     */
+    protected final void onTransactionCommited(final SnapshotBackedWriteTransaction<T> transaction) {
+        // If the committed transaction was the one we allocated last,
+        // we clear it and the ready snapshot, so the next transaction
+        // allocated refers to the data tree directly.
+        final State localState = state;
+
+        if (!(localState instanceof Allocated)) {
+            // This can legally happen if the chain is shut down before the transaction was committed
+            // by the backend.
+            LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
+            return;
+        }
+
+        final Allocated allocated = (Allocated)localState;
+        final DOMStoreWriteTransaction tx = allocated.getTransaction();
+        if (!tx.equals(transaction)) {
+            LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
+            return;
+        }
+
+        if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
+            LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
+        }
+    }
+
+    /**
+     * Notify the base logic that a previously-submitted transaction has failed.
+     *
+     * @param transaction Transaction which failed.
+     * @param cause Failure cause
+     */
+    protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction, final Throwable cause) {
+        LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
+        state = FAILED;
+    }
+
+    /**
+     * Return the next transaction identifier.
+     *
+     * @return transaction identifier.
+     */
+    protected abstract T nextTransactionIdentifier();
+
+    /**
+     * Inquire as to whether transactions should record their allocation context.
+     *
+     * @return True if allocation context should be recorded.
+     */
+    protected abstract boolean getDebugTransactions();
+
+    /**
+     * Take a fresh {@link DataTreeSnapshot} from the backend.
+     *
+     * @return A new snapshot.
+     */
+    protected abstract DataTreeSnapshot takeSnapshot();
+
+    /**
+     * Create a cohort for driving the transaction through the commit process.
+     *
+     * @param transaction Transaction handle
+     * @param modification {@link DataTreeModification} which needs to be applied to the backend
+     * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
+     */
+    protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+}
@@ -5,16 +5,15 @@
  * 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.md.sal.dom.store.impl;
+package org.opendaylight.controller.sal.core.spi.data;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
@@ -28,14 +27,21 @@ import org.slf4j.LoggerFactory;
  * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
  * which delegates most of its calls to similar methods provided by underlying snapshot.
  *
+ * <T> identifier type
  */
-final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction<Object>
-                                          implements DOMStoreReadTransaction {
-
+@Beta
+public final class SnapshotBackedReadTransaction<T> extends AbstractDOMStoreTransaction<T> implements DOMStoreReadTransaction {
     private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class);
     private volatile DataTreeSnapshot stableSnapshot;
 
-    public SnapshotBackedReadTransaction(final Object identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+    /**
+     * Creates a new read-only transaction.
+     *
+     * @param identifier Transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     */
+    SnapshotBackedReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) {
         super(identifier, debug);
         this.stableSnapshot = Preconditions.checkNotNull(snapshot);
         LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot);
@@ -71,8 +77,7 @@ final class SnapshotBackedReadTransaction extends AbstractDOMStoreTransaction<Ob
         checkNotNull(path, "Path must not be null.");
 
         try {
-            return Futures.immediateCheckedFuture(
-                read(path).checkedGet().isPresent());
+            return Futures.immediateCheckedFuture(read(path).checkedGet().isPresent());
         } catch (ReadFailedException e) {
             return Futures.immediateFailedCheckedFuture(e);
         }
@@ -5,14 +5,15 @@
  * 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.md.sal.dom.store.impl;
+package org.opendaylight.controller.sal.core.spi.data;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
@@ -23,20 +24,15 @@ import org.slf4j.LoggerFactory;
  * Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot}
  * and executed according to {@link TransactionReadyPrototype}.
  *
+ * @param <T> identifier type
  */
-final class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements DOMStoreReadWriteTransaction {
+@Beta
+public final class SnapshotBackedReadWriteTransaction<T> extends SnapshotBackedWriteTransaction<T> implements DOMStoreReadWriteTransaction {
     private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class);
 
-    /**
-     * Creates new read-write transaction.
-     *
-     * @param identifier transaction Identifier
-     * @param snapshot Snapshot which will be modified.
-     * @param readyImpl Implementation of ready method.
-     */
-    protected SnapshotBackedReadWriteTransaction(final Object identifier, final boolean debug,
-            final DataTreeSnapshot snapshot, final TransactionReadyPrototype store) {
-        super(identifier, debug, snapshot, store);
+    SnapshotBackedReadWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        super(identifier, debug, snapshot, readyImpl);
     }
 
     @Override
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedTransactions.java
new file mode 100644 (file)
index 0000000..3368c8a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+
+/**
+ * Public utility class for instantiating snapshot-backed transactions.
+ */
+@Beta
+public final class SnapshotBackedTransactions {
+    private SnapshotBackedTransactions() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Creates a new read-only transaction.
+     *
+     * @param identifier Transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     */
+    public static <T> SnapshotBackedReadTransaction<T> newReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+        return new SnapshotBackedReadTransaction<T>(identifier, debug, snapshot);
+    }
+
+    /**
+     * Creates a new read-write transaction.
+     *
+     * @param identifier transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     * @param readyImpl Implementation of ready method.
+     */
+    public static <T> SnapshotBackedReadWriteTransaction<T> newReadWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        return new SnapshotBackedReadWriteTransaction<T>(identifier, debug, snapshot, readyImpl);
+    }
+
+    /**
+     * Creates a new write-only transaction.
+     *
+     * @param identifier transaction Identifier
+     * @param debug Enable transaction debugging
+     * @param snapshot Snapshot which will be modified.
+     * @param readyImpl Implementation of ready method.
+     */
+    public static <T> SnapshotBackedWriteTransaction<T> newWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+        return new SnapshotBackedWriteTransaction<T>(identifier, debug, snapshot, readyImpl);
+    }
+}
@@ -5,17 +5,15 @@
  * 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.md.sal.dom.store.impl;
+package org.opendaylight.controller.sal.core.spi.data;
 
 import static com.google.common.base.Preconditions.checkState;
+import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
@@ -26,33 +24,27 @@ import org.slf4j.LoggerFactory;
 /**
  * Implementation of Write transaction which is backed by
  * {@link DataTreeSnapshot} and executed according to
- * {@link org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype}.
+ * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype}.
  *
+ * @param <T> Identifier type
  */
-class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction<Object> implements DOMStoreWriteTransaction {
+@Beta
+public class SnapshotBackedWriteTransaction<T> extends AbstractDOMStoreTransaction<T> implements DOMStoreWriteTransaction {
     private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class);
+    @SuppressWarnings("rawtypes")
     private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, TransactionReadyPrototype> READY_UPDATER =
             AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, TransactionReadyPrototype.class, "readyImpl");
+    @SuppressWarnings("rawtypes")
     private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, DataTreeModification> TREE_UPDATER =
             AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, DataTreeModification.class, "mutableTree");
 
     // non-null when not ready
-    private volatile TransactionReadyPrototype readyImpl;
+    private volatile TransactionReadyPrototype<T> readyImpl;
     // non-null when not committed/closed
     private volatile DataTreeModification mutableTree;
 
-    /**
-     * Creates new write-only transaction.
-     *
-     * @param identifier
-     *            transaction Identifier
-     * @param snapshot
-     *            Snapshot which will be modified.
-     * @param readyImpl
-     *            Implementation of ready method.
-     */
-    public SnapshotBackedWriteTransaction(final Object identifier, final boolean debug,
-            final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) {
+    SnapshotBackedWriteTransaction(final T identifier, final boolean debug,
+            final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
         super(identifier, debug);
         this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null.");
         mutableTree = snapshot.newModification();
@@ -126,7 +118,7 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction<Object>
      * @param path Path to read
      * @return null if the the transaction has been closed;
      */
-    protected final Optional<NormalizedNode<?, ?>> readSnapshotNode(final YangInstanceIdentifier path) {
+    final Optional<NormalizedNode<?, ?>> readSnapshotNode(final YangInstanceIdentifier path) {
         return readyImpl == null ? null : mutableTree.readNode(path);
     }
 
@@ -136,7 +128,8 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction<Object>
 
     @Override
     public DOMStoreThreePhaseCommitCohort ready() {
-        final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null);
+        @SuppressWarnings("unchecked")
+        final TransactionReadyPrototype<T> wasReady = READY_UPDATER.getAndSet(this, null);
         checkState(wasReady != null, "Transaction %s is no longer open", getIdentifier());
 
         LOG.debug("Store transaction: {} : Ready", getIdentifier());
@@ -149,7 +142,8 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction<Object>
 
     @Override
     public void close() {
-        final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null);
+        @SuppressWarnings("unchecked")
+        final TransactionReadyPrototype<T> wasReady = READY_UPDATER.getAndSet(this, null);
         if (wasReady != null) {
             LOG.debug("Store transaction: {} : Closed", getIdentifier());
             TREE_UPDATER.lazySet(this, null);
@@ -166,21 +160,22 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction<Object>
 
     /**
      * Prototype implementation of
-     * {@link #ready(org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction)}
+     * {@link #ready(org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction)}
      *
      * This class is intended to be implemented by Transaction factories
-     * responsible for allocation of {@link org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction} and
+     * responsible for allocation of {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction} and
      * providing underlying logic for applying implementation.
      *
+     * @param <T> identifier type
      */
-    abstract static class TransactionReadyPrototype {
+    public abstract static class TransactionReadyPrototype<T> {
         /**
          * Called when a transaction is closed without being readied. This is not invoked for
          * transactions which are ready.
          *
          * @param tx Transaction which got aborted.
          */
-        protected abstract void transactionAborted(final SnapshotBackedWriteTransaction tx);
+        protected abstract void transactionAborted(final SnapshotBackedWriteTransaction<T> tx);
 
         /**
          * Returns a commit coordinator associated with supplied transactions.
@@ -193,6 +188,6 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction<Object>
          *            Modified data tree which has been constructed.
          * @return DOMStoreThreePhaseCommitCohort associated with transaction
          */
-        protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction tx, DataTreeModification tree);
+        protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx, DataTreeModification tree);
     }
 }
\ No newline at end of file
index 05e3d5cb26e5944dc46de6a6d3e7b7fc87ad56b3..35d891dac025f7acc5cfb0095f40974ad2dfb601 100644 (file)
@@ -8,44 +8,24 @@
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.controller.sal.core.spi.data.ForwardingDOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 
-final class ChainedTransactionCommitImpl extends ForwardingDOMStoreThreePhaseCommitCohort {
-    private final SnapshotBackedWriteTransaction transaction;
-    private final DOMStoreThreePhaseCommitCohort delegate;
+final class ChainedTransactionCommitImpl extends InMemoryDOMStoreThreePhaseCommitCohort {
     private final DOMStoreTransactionChainImpl txChain;
 
-    ChainedTransactionCommitImpl(final SnapshotBackedWriteTransaction transaction,
-            final DOMStoreThreePhaseCommitCohort delegate, final DOMStoreTransactionChainImpl txChain) {
-        this.transaction = Preconditions.checkNotNull(transaction);
-        this.delegate = Preconditions.checkNotNull(delegate);
+    ChainedTransactionCommitImpl(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> transaction,
+        final DataTreeModification modification, final DOMStoreTransactionChainImpl txChain) {
+        super(store, transaction, modification);
         this.txChain = Preconditions.checkNotNull(txChain);
     }
 
-    @Override
-    protected DOMStoreThreePhaseCommitCohort delegate() {
-        return delegate;
-    }
-
     @Override
     public ListenableFuture<Void> commit() {
-        ListenableFuture<Void> commitFuture = super.commit();
-        Futures.addCallback(commitFuture, new FutureCallback<Void>() {
-            @Override
-            public void onFailure(final Throwable t) {
-                txChain.onTransactionFailed(transaction, t);
-            }
-
-            @Override
-            public void onSuccess(final Void result) {
-                txChain.onTransactionCommited(transaction);
-            }
-        });
-        return commitFuture;
+        ListenableFuture<Void> ret = super.commit();
+        txChain.transactionCommited(getTransaction());
+        return ret;
     }
 
 }
\ No newline at end of file
index 3f731cf18b66bb9305cc1ef40b867f4904343f04..2cf79d899b3682bf06696e2465ad035b56c9e2a2 100644 (file)
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
 import com.google.common.base.Preconditions;
-import java.util.AbstractMap.SimpleEntry;
-import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.AbstractSnapshotBackedTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-final class DOMStoreTransactionChainImpl extends TransactionReadyPrototype implements DOMStoreTransactionChain {
-    private static abstract class State {
-        /**
-         * Allocate a new snapshot.
-         *
-         * @return A new snapshot
-         */
-        protected abstract DataTreeSnapshot getSnapshot();
-    }
-
-    private static final class Idle extends State {
-        private final InMemoryDOMDataStore store;
-
-        Idle(final InMemoryDOMDataStore store) {
-            this.store = Preconditions.checkNotNull(store);
-        }
-
-        @Override
-        protected DataTreeSnapshot getSnapshot() {
-            return store.takeSnapshot();
-        }
-    }
-
-    /**
-     * We have a transaction out there.
-     */
-    private static final class Allocated extends State {
-        private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
-                AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
-        private final DOMStoreWriteTransaction transaction;
-        private volatile DataTreeSnapshot snapshot;
-
-        Allocated(final DOMStoreWriteTransaction transaction) {
-            this.transaction = Preconditions.checkNotNull(transaction);
-        }
-
-        public DOMStoreWriteTransaction getTransaction() {
-            return transaction;
-        }
-
-        @Override
-        protected DataTreeSnapshot getSnapshot() {
-            final DataTreeSnapshot ret = snapshot;
-            Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
-            return ret;
-        }
-
-        void setSnapshot(final DataTreeSnapshot snapshot) {
-            final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
-            Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
-        }
-    }
-
-    /**
-     * Chain is logically shut down, no further allocation allowed.
-     */
-    private static final class Shutdown extends State {
-        private final String message;
-
-        Shutdown(final String message) {
-            this.message = Preconditions.checkNotNull(message);
-        }
-
-        @Override
-        protected DataTreeSnapshot getSnapshot() {
-            throw new IllegalStateException(message);
-        }
-    }
-
-    private static final AtomicReferenceFieldUpdater<DOMStoreTransactionChainImpl, State> STATE_UPDATER =
-            AtomicReferenceFieldUpdater.newUpdater(DOMStoreTransactionChainImpl.class, State.class, "state");
-    private static final Logger LOG = LoggerFactory.getLogger(DOMStoreTransactionChainImpl.class);
-    private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
-    private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
+final class DOMStoreTransactionChainImpl extends AbstractSnapshotBackedTransactionChain<String> {
     private final InMemoryDOMDataStore store;
-    private final Idle idleState;
-    private volatile State state;
 
     DOMStoreTransactionChainImpl(final InMemoryDOMDataStore store) {
         this.store = Preconditions.checkNotNull(store);
-        idleState = new Idle(store);
-        state = idleState;
-    }
-
-    private Entry<State, DataTreeSnapshot> getSnapshot() {
-        final State localState = state;
-        return new SimpleEntry<>(localState, localState.getSnapshot());
-    }
-
-    private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
-        final State state = new Allocated(transaction);
-        return STATE_UPDATER.compareAndSet(this, expected, state);
     }
 
     @Override
-    public DOMStoreReadTransaction newReadOnlyTransaction() {
-        final Entry<State, DataTreeSnapshot> entry = getSnapshot();
-        return new SnapshotBackedReadTransaction(store.nextIdentifier(), store.getDebugTransactions(), entry.getValue());
+    protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+        return new ChainedTransactionCommitImpl(store, tx, modification, this);
     }
 
     @Override
-    public DOMStoreReadWriteTransaction newReadWriteTransaction() {
-        Entry<State, DataTreeSnapshot> entry;
-        DOMStoreReadWriteTransaction ret;
-
-        do {
-            entry = getSnapshot();
-            ret = new SnapshotBackedReadWriteTransaction(store.nextIdentifier(),
-                store.getDebugTransactions(), entry.getValue(), this);
-        } while (!recordTransaction(entry.getKey(), ret));
-
-        return ret;
-    }
-
-    @Override
-    public DOMStoreWriteTransaction newWriteOnlyTransaction() {
-        Entry<State, DataTreeSnapshot> entry;
-        DOMStoreWriteTransaction ret;
-
-        do {
-            entry = getSnapshot();
-            ret = new SnapshotBackedWriteTransaction(store.nextIdentifier(),
-                store.getDebugTransactions(), entry.getValue(), this);
-        } while (!recordTransaction(entry.getKey(), ret));
-
-        return ret;
+    protected DataTreeSnapshot takeSnapshot() {
+        return store.takeSnapshot();
     }
 
     @Override
-    protected void transactionAborted(final SnapshotBackedWriteTransaction tx) {
-        final State localState = state;
-        if (localState instanceof Allocated) {
-            final Allocated allocated = (Allocated)localState;
-            if (allocated.getTransaction().equals(tx)) {
-                final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
-                if (!success) {
-                    LOG.info("State already transitioned from {} to {}", localState, state);
-                }
-            }
-        }
+    protected String nextTransactionIdentifier() {
+        return store.nextIdentifier();
     }
 
     @Override
-    protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) {
-        final State localState = state;
-
-        if (localState instanceof Allocated) {
-            final Allocated allocated = (Allocated)localState;
-            final DOMStoreWriteTransaction transaction = allocated.getTransaction();
-            Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
-            allocated.setSnapshot(tree);
-        } else {
-            LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
-        }
-
-        return new ChainedTransactionCommitImpl(tx, store.transactionReady(tx, tree), this);
+    protected boolean getDebugTransactions() {
+        return store.getDebugTransactions();
     }
 
-    @Override
-    public void close() {
-        final State localState = state;
-
-        do {
-            Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
-
-            if (FAILED.equals(localState)) {
-                LOG.debug("Ignoring user close in failed state");
-                return;
-            }
-        } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
-    }
-
-    void onTransactionFailed(final SnapshotBackedWriteTransaction transaction, final Throwable t) {
-        LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, t);
-        state = FAILED;
-    }
-
-    void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) {
-        // If the committed transaction was the one we allocated last,
-        // we clear it and the ready snapshot, so the next transaction
-        // allocated refers to the data tree directly.
-        final State localState = state;
-
-        if (!(localState instanceof Allocated)) {
-            LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
-            return;
-        }
-
-        final Allocated allocated = (Allocated)localState;
-        final DOMStoreWriteTransaction tx = allocated.getTransaction();
-        if (!tx.equals(transaction)) {
-            LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
-            return;
-        }
-
-        if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
-            LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
-        }
+    void transactionCommited(final SnapshotBackedWriteTransaction<String> transaction) {
+        super.onTransactionCommited(transaction);
     }
-}
\ No newline at end of file
+}
index 354abcf69fe1040a1e24822b7db78aa6e674909d..a85d8ac3fb645ecc5a5815df3aeaf46bb8fc4187 100644 (file)
@@ -7,22 +7,15 @@
  */
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
-import static com.google.common.base.Preconditions.checkState;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
-import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
-import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
-import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
@@ -30,6 +23,9 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCoh
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -38,7 +34,6 @@ import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
 import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
@@ -55,14 +50,12 @@ import org.slf4j.LoggerFactory;
  *
  * Implementation of {@link DOMStore} which uses {@link DataTree} and other
  * classes such as {@link SnapshotBackedWriteTransaction}.
- * {@link SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask}
+ * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask}
  * to implement {@link DOMStore} contract.
  *
  */
-public class InMemoryDOMDataStore extends TransactionReadyPrototype implements DOMStore, Identifiable<String>, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher {
+public class InMemoryDOMDataStore extends TransactionReadyPrototype<String> implements DOMStore, Identifiable<String>, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher {
     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
-    private static final ListenableFuture<Void> SUCCESSFUL_FUTURE = Futures.immediateFuture(null);
-    private static final ListenableFuture<Boolean> CAN_COMMIT_FUTURE = Futures.immediateFuture(Boolean.TRUE);
 
     private static final Invoker<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> DCL_NOTIFICATION_MGR_INVOKER =
             new Invoker<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent>() {
@@ -120,17 +113,17 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype implements D
 
     @Override
     public DOMStoreReadTransaction newReadOnlyTransaction() {
-        return new SnapshotBackedReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot());
+        return SnapshotBackedTransactions.newReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot());
     }
 
     @Override
     public DOMStoreReadWriteTransaction newReadWriteTransaction() {
-        return new SnapshotBackedReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
+        return SnapshotBackedTransactions.newReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
     }
 
     @Override
     public DOMStoreWriteTransaction newWriteOnlyTransaction() {
-        return new SnapshotBackedWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
+        return SnapshotBackedTransactions.newWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
     }
 
     @Override
@@ -214,99 +207,31 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype implements D
     }
 
     @Override
-    protected void transactionAborted(final SnapshotBackedWriteTransaction tx) {
+    protected void transactionAborted(final SnapshotBackedWriteTransaction<String> tx) {
         LOG.debug("Tx: {} is closed.", tx.getIdentifier());
     }
 
     @Override
-    protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) {
-        LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), tree);
-        return new ThreePhaseCommitImpl(tx, tree);
+    protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+        LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), modification);
+        return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification);
     }
 
-    Object nextIdentifier() {
+    String nextIdentifier() {
         return name + "-" + txCounter.getAndIncrement();
     }
 
-    private static void warnDebugContext(final AbstractDOMStoreTransaction<?> transaction) {
-        final Throwable ctx = transaction.getDebugContext();
-        if (ctx != null) {
-            LOG.warn("Transaction {} has been allocated in the following context", transaction.getIdentifier(), ctx);
-        }
+    void validate(final DataTreeModification modification) throws DataValidationFailedException {
+        dataTree.validate(modification);
     }
 
-    private final class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort {
-        private final SnapshotBackedWriteTransaction transaction;
-        private final DataTreeModification modification;
-
-        private ResolveDataChangeEventsTask listenerResolver;
-        private DataTreeCandidate candidate;
-
-        public ThreePhaseCommitImpl(final SnapshotBackedWriteTransaction writeTransaction, final DataTreeModification modification) {
-            this.transaction = writeTransaction;
-            this.modification = modification;
-        }
-
-        @Override
-        public ListenableFuture<Boolean> canCommit() {
-            try {
-                dataTree.validate(modification);
-                LOG.debug("Store Transaction: {} can be committed", transaction.getIdentifier());
-                return CAN_COMMIT_FUTURE;
-            } catch (ConflictingModificationAppliedException e) {
-                LOG.warn("Store Tx: {} Conflicting modification for {}.", transaction.getIdentifier(),
-                        e.getPath());
-                warnDebugContext(transaction);
-                return Futures.immediateFailedFuture(new OptimisticLockFailedException("Optimistic lock failed.", e));
-            } catch (DataValidationFailedException e) {
-                LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(),
-                        e.getPath(), e);
-                warnDebugContext(transaction);
-
-                // For debugging purposes, allow dumping of the modification. Coupled with the above
-                // precondition log, it should allow us to understand what went on.
-                LOG.trace("Store Tx: {} modifications: {} tree: {}", modification, dataTree);
-
-                return Futures.immediateFailedFuture(new TransactionCommitFailedException("Data did not pass validation.", e));
-            } catch (Exception e) {
-                LOG.warn("Unexpected failure in validation phase", e);
-                return Futures.immediateFailedFuture(e);
-            }
-        }
-
-        @Override
-        public ListenableFuture<Void> preCommit() {
-            try {
-                candidate = dataTree.prepare(modification);
-                listenerResolver = ResolveDataChangeEventsTask.create(candidate, listenerTree);
-                return SUCCESSFUL_FUTURE;
-            } catch (Exception e) {
-                LOG.warn("Unexpected failure in pre-commit phase", e);
-                return Futures.immediateFailedFuture(e);
-            }
-        }
-
-        @Override
-        public ListenableFuture<Void> abort() {
-            candidate = null;
-            return SUCCESSFUL_FUTURE;
-        }
-
-        @Override
-        public ListenableFuture<Void> commit() {
-            checkState(candidate != null, "Proposed subtree must be computed");
-
-            /*
-             * The commit has to occur atomically with regard to listener
-             * registrations.
-             */
-            synchronized (InMemoryDOMDataStore.this) {
-                dataTree.commit(candidate);
-                changePublisher.publishChange(candidate);
-                listenerResolver.resolve(dataChangeListenerNotificationManager);
-            }
+    DataTreeCandidate prepare(final DataTreeModification modification) {
+        return dataTree.prepare(modification);
+    }
 
-            return SUCCESSFUL_FUTURE;
-        }
+    synchronized void commit(final DataTreeCandidate candidate) {
+        dataTree.commit(candidate);
+        changePublisher.publishChange(candidate);
+        ResolveDataChangeEventsTask.create(candidate, listenerTree).resolve(dataChangeListenerNotificationManager);
     }
 }
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java
new file mode 100644 (file)
index 0000000..dba71bf
--- /dev/null
@@ -0,0 +1,100 @@
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InMemoryDOMStoreThreePhaseCommitCohort implements DOMStoreThreePhaseCommitCohort {
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreThreePhaseCommitCohort.class);
+    private static final ListenableFuture<Void> SUCCESSFUL_FUTURE = Futures.immediateFuture(null);
+    private static final ListenableFuture<Boolean> CAN_COMMIT_FUTURE = Futures.immediateFuture(Boolean.TRUE);
+    private final SnapshotBackedWriteTransaction<String> transaction;
+    private final DataTreeModification modification;
+    private final InMemoryDOMDataStore store;
+    private DataTreeCandidate candidate;
+
+    public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> writeTransaction, final DataTreeModification modification) {
+        this.transaction = Preconditions.checkNotNull(writeTransaction);
+        this.modification = Preconditions.checkNotNull(modification);
+        this.store = Preconditions.checkNotNull(store);
+    }
+
+    private static void warnDebugContext(final AbstractDOMStoreTransaction<?> transaction) {
+        final Throwable ctx = transaction.getDebugContext();
+        if (ctx != null) {
+            LOG.warn("Transaction {} has been allocated in the following context", transaction.getIdentifier(), ctx);
+        }
+    }
+
+    @Override
+    public final ListenableFuture<Boolean> canCommit() {
+        try {
+            store.validate(modification);
+            LOG.debug("Store Transaction: {} can be committed", getTransaction().getIdentifier());
+            return CAN_COMMIT_FUTURE;
+        } catch (ConflictingModificationAppliedException e) {
+            LOG.warn("Store Tx: {} Conflicting modification for {}.", getTransaction().getIdentifier(),
+                    e.getPath());
+            warnDebugContext(getTransaction());
+            return Futures.immediateFailedFuture(new OptimisticLockFailedException("Optimistic lock failed.", e));
+        } catch (DataValidationFailedException e) {
+            LOG.warn("Store Tx: {} Data Precondition failed for {}.", getTransaction().getIdentifier(),
+                    e.getPath(), e);
+            warnDebugContext(getTransaction());
+
+            // For debugging purposes, allow dumping of the modification. Coupled with the above
+            // precondition log, it should allow us to understand what went on.
+            LOG.trace("Store Tx: {} modifications: {} tree: {}", modification, store);
+
+            return Futures.immediateFailedFuture(new TransactionCommitFailedException("Data did not pass validation.", e));
+        } catch (Exception e) {
+            LOG.warn("Unexpected failure in validation phase", e);
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    @Override
+    public final ListenableFuture<Void> preCommit() {
+        try {
+            candidate = store.prepare(modification);
+            return SUCCESSFUL_FUTURE;
+        } catch (Exception e) {
+            LOG.warn("Unexpected failure in pre-commit phase", e);
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    @Override
+    public final ListenableFuture<Void> abort() {
+        candidate = null;
+        return SUCCESSFUL_FUTURE;
+    }
+
+    protected final SnapshotBackedWriteTransaction<String> getTransaction() {
+        return transaction;
+    }
+
+    @Override
+    public ListenableFuture<Void> commit() {
+        checkState(candidate != null, "Proposed subtree must be computed");
+
+        /*
+         * The commit has to occur atomically with regard to listener
+         * registrations.
+         */
+        store.commit(candidate);
+        return SUCCESSFUL_FUTURE;
+    }
+}
\ No newline at end of file
index 9de4892d9140b1afc7ea3a93ef532ca1bf97edeb..568f88376cbc816b9ea151a68fb36b1d4a5f15a7 100644 (file)
@@ -21,12 +21,13 @@ import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -37,7 +38,6 @@ import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-
 public class InMemoryDataStoreTest {
 
     private SchemaContext schemaContext;
@@ -268,7 +268,7 @@ public class InMemoryDataStoreTest {
         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
 
-        DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction("1", true, mockSnapshot);
+        DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
 
         doReadAndThrowEx( readTx );
     }
@@ -292,14 +292,14 @@ public class InMemoryDataStoreTest {
         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification )
         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
         Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
-        TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class );
-        DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction("1", false, mockSnapshot, mockReady);
+        @SuppressWarnings("unchecked")
+        TransactionReadyPrototype<String> mockReady = Mockito.mock( TransactionReadyPrototype.class );
+        DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction("1", false, mockSnapshot, mockReady);
 
         doReadAndThrowEx( readTx );
     }
 
-    private void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
-
+    private static void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
         try {
             readTx.read(TestModel.TEST_PATH).get();
         } catch( ExecutionException e ) {
index db9b702fed43e86b7a8446f6e705b5463326e890..c0b57de9a0d32ed26c60fa64e8dcfb551c62a354 100644 (file)
@@ -118,7 +118,7 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
      * Create rpc implementation capable of handling RPC for monitoring and notifications even before the schemas of remote device are downloaded
      */
     static NetconfDeviceRpc getRpcForInitialization(final NetconfDeviceCommunicator listener) {
-        return new NetconfDeviceRpc(INIT_SCHEMA_CTX, listener, new NetconfMessageTransformer(INIT_SCHEMA_CTX));
+        return new NetconfDeviceRpc(INIT_SCHEMA_CTX, listener, new NetconfMessageTransformer(INIT_SCHEMA_CTX, false));
     }
 
 
@@ -216,7 +216,7 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
 
     @VisibleForTesting
     void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final DOMRpcService deviceRpc) {
-        messageTransformer = new NetconfMessageTransformer(result);
+        messageTransformer = new NetconfMessageTransformer(result, true);
 
         updateTransformer(messageTransformer);
         // salFacade.onDeviceConnected has to be called before the notification handler is initialized
@@ -461,7 +461,7 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferenc
         }
 
         private NetconfDeviceRpc getDeviceSpecificRpc(final SchemaContext result) {
-            return new NetconfDeviceRpc(result, listener, new NetconfMessageTransformer(result));
+            return new NetconfDeviceRpc(result, listener, new NetconfMessageTransformer(result, true));
         }
 
         private Collection<SourceIdentifier> stripMissingSource(final Collection<SourceIdentifier> requiredSources, final SourceIdentifier sIdToRemove) {
index 303f3e692390ff8a396781377da9c2b99f2e4cd3..819edce320d30f827f90f3c7358ed95ebefed0d4 100644 (file)
@@ -101,10 +101,10 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
     private final Multimap<QName, NotificationDefinition> mappedNotifications;
     private final DomToNormalizedNodeParserFactory parserFactory;
 
-    public NetconfMessageTransformer(final SchemaContext schemaContext) {
+    public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) {
         this.counter = new MessageCounter();
         this.schemaContext = schemaContext;
-        parserFactory = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, schemaContext);
+        parserFactory = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, schemaContext, strictParsing);
 
         mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION);
         mappedNotifications = Multimaps.index(schemaContext.getNotifications(), QNAME_NOREV_FUNCTION);
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java
deleted file mode 100644 (file)
index d0b9efc..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * 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.controller.sal.connect.netconf.util;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import javax.xml.transform.dom.DOMSource;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.yangtools.concepts.Identifiable;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.ModifyAction;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-/**
- * Transforms an instance of yang instance identifier to a filter like structure in normalized node format. Can be also used to nest the edit-config rpc content.
- * For each argument of the id, a specific normalized node is created to ensure schema context conformance.
- */
-public abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable<T> {
-
-    private final T identifier;
-
-    @Override
-    public T getIdentifier() {
-        return identifier;
-    }
-
-    protected InstanceIdToNodes(final T identifier) {
-        this.identifier = identifier;
-    }
-
-    abstract InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException;
-
-    public abstract NormalizedNode<?, ?> create(YangInstanceIdentifier legacyData, Optional<NormalizedNode<?, ?>> deepestChild, Optional<ModifyAction> operation);
-
-    private static abstract class SimpleTypeNormalization<T extends PathArgument> extends InstanceIdToNodes<T> {
-
-        protected SimpleTypeNormalization(final T identifier) {
-            super(identifier);
-        }
-
-        @Override
-        public NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
-            checkNotNull(id);
-            final PathArgument pathArgument = Iterables.get(id.getPathArguments(), 0);
-            final NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> builder = getBuilder(pathArgument);
-
-            if(deepestChild.isPresent()) {
-                builder.withValue(deepestChild.get().getValue());
-            }
-
-            addModifyOpIfPresent(operation, builder);
-            return builder.build();
-        }
-
-        protected abstract NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> getBuilder(PathArgument node);
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
-            return null;
-        }
-    }
-
-
-    public void addModifyOpIfPresent(final Optional<ModifyAction> operation, final AttributesBuilder<?> builder) {
-        if(operation.isPresent()) {
-            builder.withAttributes(Collections.singletonMap(NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME, NetconfMessageTransformUtil.modifyOperationToXmlString(operation.get())));
-        }
-    }
-
-    private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
-
-        protected LeafNormalization(final LeafSchemaNode potential) {
-            super(new NodeIdentifier(potential.getQName()));
-        }
-
-        @Override
-        protected NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> getBuilder(final PathArgument node) {
-            return Builders.leafBuilder().withNodeIdentifier(getIdentifier());
-        }
-    }
-
-    private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
-
-        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
-            super(new NodeWithValue(potential.getQName(), null));
-        }
-
-        @Override
-        protected NormalizedNodeAttrBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> getBuilder(final PathArgument node) {
-            Preconditions.checkArgument(node instanceof NodeWithValue);
-            return Builders.leafSetEntryBuilder().withNodeIdentifier((NodeWithValue) node).withValue(((NodeWithValue) node).getValue());
-        }
-
-    }
-
-    private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
-            InstanceIdToNodes<T> {
-
-        protected CompositeNodeNormalizationOperation(final T identifier) {
-            super(identifier);
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public final NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> lastChild, final Optional<ModifyAction> operation) {
-            checkNotNull(id);
-            final Iterator<PathArgument> iterator = id.getPathArguments().iterator();
-            final PathArgument legacyData = iterator.next();
-
-            if (!isMixin(this) && getIdentifier().getNodeType() != null) {
-                checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
-                        "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
-            }
-            final NormalizedNodeContainerBuilder builder = createBuilder(legacyData);
-
-            if (iterator.hasNext()) {
-                final PathArgument childPath = iterator.next();
-                final InstanceIdToNodes childOp = getChildOperation(childPath);
-
-                final YangInstanceIdentifier childId = YangInstanceIdentifier.create(Iterables.skip(id.getPathArguments(), 1));
-                builder.addChild(childOp.create(childId, lastChild, operation));
-            } else {
-                if(lastChild.isPresent()) {
-                    builder.withValue(Lists.newArrayList((Collection<?>) lastChild.get().getValue()));
-                }
-                if(operation.isPresent()) {
-                    Preconditions.checkArgument(builder instanceof AttributesBuilder<?>);
-                    addModifyOpIfPresent(operation, ((AttributesBuilder<?>) builder));
-                }
-            }
-
-            return builder.build();
-        }
-
-        private InstanceIdToNodes getChildOperation(final PathArgument childPath) {
-            final InstanceIdToNodes childOp;
-            try {
-                childOp = getChild(childPath);
-            } catch (final DataNormalizationException e) {
-                throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
-            }
-            checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
-            return childOp;
-        }
-
-        @SuppressWarnings("rawtypes")
-        protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode);
-    }
-
-    static boolean isMixin(final InstanceIdToNodes<?> op) {
-        return op instanceof MixinNormalizationOp;
-    }
-
-    private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
-            CompositeNodeNormalizationOperation<T> {
-
-        private final DataNodeContainer schema;
-        private final Map<PathArgument, InstanceIdToNodes<?>> byArg;
-
-        protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
-            super(identifier);
-            this.schema = schema;
-            this.byArg = new ConcurrentHashMap<>();
-        }
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
-            InstanceIdToNodes<?> potential = byArg.get(child);
-            if (potential != null) {
-                return potential;
-            }
-            potential = fromLocalSchema(child);
-            return register(potential);
-        }
-
-        private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
-            if (child instanceof AugmentationIdentifier) {
-                return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
-                        .iterator().next());
-            }
-            return fromSchemaAndQNameChecked(schema, child.getNodeType());
-        }
-
-        private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
-            if (potential != null) {
-                byArg.put(potential.getIdentifier(), potential);
-            }
-            return potential;
-        }
-    }
-
-    private static final class ListItemNormalization extends
-            DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
-
-        protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
-            super(identifier, schema);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument currentArg) {
-            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
-                    .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
-            for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
-                builder.addChild(Builders.leafBuilder()
-                        //
-                        .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
-                        .build());
-            }
-            return builder;
-        }
-
-    }
-
-    private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
-
-        protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
-            super(new NodeIdentifier(schema.getQName()), schema);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-    }
-
-    private static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
-
-        protected ContainerTransformation(final ContainerSchemaNode schema) {
-            super(new NodeIdentifier(schema.getQName()), schema);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
-        }
-    }
-
-    /**
-     * Marker interface for Mixin nodes normalization operations
-     */
-    private interface MixinNormalizationOp {}
-
-
-    private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
-
-
-        public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
-            super(potential);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
-        }
-    }
-
-    private static class UnorderedLeafListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
-
-        private final InstanceIdToNodes<?> innerOp;
-
-        public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
-            super(new NodeIdentifier(potential.getQName()));
-            innerOp = new LeafListEntryNormalization(potential);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
-            if (child instanceof NodeWithValue) {
-                return innerOp;
-            }
-            return null;
-        }
-    }
-
-    private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> implements MixinNormalizationOp {
-
-        public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
-            //super();
-            super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema));
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
-        }
-    }
-
-    private static class UnorderedMapMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
-
-        private final ListItemNormalization innerNode;
-
-        public UnorderedMapMixinNormalization(final ListSchemaNode list) {
-            super(new NodeIdentifier(list.getQName()));
-            this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
-                    Collections.<QName, Object>emptyMap()), list);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
-            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
-                return innerNode;
-            }
-            return null;
-        }
-    }
-
-    private static class UnkeyedListMixinNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
-
-        private final UnkeyedListItemNormalization innerNode;
-
-        public UnkeyedListMixinNormalization(final ListSchemaNode list) {
-            super(new NodeIdentifier(list.getQName()));
-            this.innerNode = new UnkeyedListItemNormalization(list);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
-            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
-                return innerNode;
-            }
-            return null;
-        }
-
-    }
-
-    private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
-
-        public OrderedMapMixinNormalization(final ListSchemaNode list) {
-            super(list);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-    }
-
-    private static class ChoiceNodeNormalization extends CompositeNodeNormalizationOperation<NodeIdentifier> implements MixinNormalizationOp {
-
-        private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
-
-        protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
-            super(new NodeIdentifier(schema.getQName()));
-            final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
-
-            for (final ChoiceCaseNode caze : schema.getCases()) {
-                for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
-                    final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
-                    byArgBuilder.put(childOp.getIdentifier(), childOp);
-                }
-            }
-            byArg = byArgBuilder.build();
-        }
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) {
-            return byArg.get(child);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
-            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
-        }
-    }
-
-    private static class AnyXmlNormalization extends InstanceIdToNodes<NodeIdentifier> {
-
-        protected AnyXmlNormalization(final AnyXmlSchemaNode schema) {
-            super(new NodeIdentifier(schema.getQName()));
-        }
-
-        @Override
-        public InstanceIdToNodes<?> getChild(final PathArgument child) throws DataNormalizationException {
-            return null;
-        }
-
-        @Override
-        public NormalizedNode<?, ?> create(final YangInstanceIdentifier legacyData, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
-            if(deepestChild.isPresent()) {
-                Preconditions.checkState(deepestChild instanceof AnyXmlNode);
-                final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> anyXmlBuilder =
-                        Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
-                addModifyOpIfPresent(operation, anyXmlBuilder);
-                return anyXmlBuilder.build();
-            }
-
-            final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder =
-                    Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
-            addModifyOpIfPresent(operation, builder);
-            return builder.build();
-        }
-
-    }
-
-    private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
-        DataSchemaNode potential = parent.getDataChildByName(child);
-        if (potential == null) {
-            final Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
-            potential = findChoice(choices, child);
-        }
-        return Optional.fromNullable(potential);
-    }
-
-    private static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) throws DataNormalizationException {
-        final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
-        if (!potential.isPresent()) {
-            throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes()));
-        }
-
-        final DataSchemaNode result = potential.get();
-        // We try to look up if this node was added by augmentation
-        if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
-            return fromAugmentation(schema, (AugmentationTarget) schema, result);
-        }
-        return fromDataSchemaNode(result);
-    }
-
-    private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
-        org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode foundChoice = null;
-        choiceLoop:
-        for (final ChoiceSchemaNode choice : choices) {
-            for (final ChoiceCaseNode caze : choice.getCases()) {
-                if (findChildSchemaNode(caze, child).isPresent()) {
-                    foundChoice = choice;
-                    break choiceLoop;
-                }
-            }
-        }
-        return foundChoice;
-    }
-
-    private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
-        final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
-        for (final DataSchemaNode child : augmentation.getChildNodes()) {
-            potentialChildren.add(child.getQName());
-        }
-        return new AugmentationIdentifier(potentialChildren.build());
-    }
-
-    private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
-        final Set<DataSchemaNode> children = new HashSet<>();
-        for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
-            children.add(schema.getDataChildByName(augNode.getQName()));
-        }
-        return new NodeContainerProxy(null, children);
-    }
-
-    /**
-     * Returns a SchemaPathUtil for provided child node
-     * <p/>
-     * If supplied child is added by Augmentation this operation returns
-     * a SchemaPathUtil for augmentation,
-     * otherwise returns a SchemaPathUtil for child as
-     * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
-     */
-    private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
-                                                          final AugmentationTarget parentAug, final DataSchemaNode child) {
-        AugmentationSchema augmentation = null;
-        for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
-            final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
-            if (potential != null) {
-                augmentation = aug;
-                break;
-            }
-
-        }
-        if (augmentation != null) {
-            return new AugmentationNormalization(augmentation, parent);
-        } else {
-            return fromDataSchemaNode(child);
-        }
-    }
-
-    private static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
-        if (potential instanceof ContainerSchemaNode) {
-            return new ContainerTransformation((ContainerSchemaNode) potential);
-        } else if (potential instanceof ListSchemaNode) {
-            return fromListSchemaNode((ListSchemaNode) potential);
-        } else if (potential instanceof LeafSchemaNode) {
-            return new LeafNormalization((LeafSchemaNode) potential);
-        } else if (potential instanceof ChoiceSchemaNode) {
-            return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
-        } else if (potential instanceof LeafListSchemaNode) {
-            return fromLeafListSchemaNode((LeafListSchemaNode) potential);
-        } else if (potential instanceof AnyXmlSchemaNode) {
-            return new AnyXmlNormalization((AnyXmlSchemaNode) potential);
-        }
-        return null;
-    }
-
-    private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
-        final List<QName> keyDefinition = potential.getKeyDefinition();
-        if (keyDefinition == null || keyDefinition.isEmpty()) {
-            return new UnkeyedListMixinNormalization(potential);
-        }
-        if (potential.isUserOrdered()) {
-            return new OrderedMapMixinNormalization(potential);
-        }
-        return new UnorderedMapMixinNormalization(potential);
-    }
-
-    private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
-        if (potential.isUserOrdered()) {
-            return new OrderedLeafListMixinNormalization(potential);
-        }
-        return new UnorderedLeafListMixinNormalization(potential);
-    }
-
-    public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id) {
-        return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>absent(), Optional.<ModifyAction>absent());
-    }
-
-    public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode<?, ?> deepestElement) {
-        return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>of(deepestElement), Optional.<ModifyAction>absent());
-    }
-
-    public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestElement, final Optional<ModifyAction> operation) {
-        Preconditions.checkNotNull(ctx);
-        Preconditions.checkNotNull(id);
-        final PathArgument topLevelElement = id.getPathArguments().iterator().next();
-        final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType());
-        Preconditions.checkNotNull(dataChildByName, "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement);
-        try {
-            final InstanceIdToNodes<?> instanceIdToNodes = fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType());
-            return instanceIdToNodes.create(id, deepestElement, operation);
-        } catch (final DataNormalizationException e) {
-            throw new IllegalArgumentException("Unable to serialize: " + id, e);
-        }
-    }
-}
index c19bb484082ab391eb18d9bd622e5f8881c11279..bf432d3860e4ee5a050125bbfe423e3951452482 100644 (file)
@@ -235,7 +235,8 @@ public final class NetconfBaseOps {
 
         // Default operation
         if(defaultOperation.isPresent()) {
-            editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_DEFAULT_OPERATION_QNAME)).withValue(NetconfMessageTransformUtil.modifyOperationToXmlString(defaultOperation.get())).build());
+            final String opString = defaultOperation.get().name().toLowerCase();
+            editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_DEFAULT_OPERATION_QNAME)).withValue(opString).build());
         }
 
         // Error option
index dc1c8c7c4075187f57fbd2e4eafac6b984e52dfd..1da39cf751cba27b999642c20da9d7659a45a728 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.net.URI;
+import java.util.AbstractMap;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -44,6 +45,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStre
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
@@ -145,7 +147,7 @@ public class NetconfMessageTransformUtil {
         final NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, DOMSource, AnyXmlNode> anyXmlBuilder = Builders.anyXmlBuilder().withNodeIdentifier(toId(NETCONF_FILTER_QNAME));
         anyXmlBuilder.withAttributes(Collections.singletonMap(NETCONF_TYPE_QNAME, SUBTREE));
 
-        final NormalizedNode<?, ?> filterContent = InstanceIdToNodes.serialize(ctx, identifier);
+        final NormalizedNode<?, ?> filterContent = ImmutableNodes.fromInstanceId(ctx, identifier);
 
         final Element element = XmlUtil.createElement(BLANK_DOCUMENT, NETCONF_FILTER_QNAME.getLocalName(), Optional.of(NETCONF_FILTER_QNAME.getNamespace().toString()));
         element.setAttributeNS(NETCONF_FILTER_QNAME.getNamespace().toString(), NETCONF_TYPE_QNAME.getLocalName(), "subtree");
@@ -268,7 +270,9 @@ public class NetconfMessageTransformUtil {
                     "Data has to be either container or a list node when creating structure for top level element, but was: %s", lastChildOverride.get());
             configContent = lastChildOverride.get();
         } else {
-            configContent = InstanceIdToNodes.serialize(ctx, dataPath, lastChildOverride, operation);
+            final Entry<QName, ModifyAction> modifyOperation =
+                    operation.isPresent() ? new AbstractMap.SimpleEntry<>(NETCONF_OPERATION_QNAME, operation.get()) : null;
+            configContent = ImmutableNodes.fromInstanceId(ctx, dataPath, lastChildOverride, Optional.fromNullable(modifyOperation));
         }
 
         final Element element = XmlUtil.createElement(BLANK_DOCUMENT, NETCONF_CONFIG_QNAME.getLocalName(), Optional.of(NETCONF_CONFIG_QNAME.getNamespace().toString()));
@@ -287,10 +291,6 @@ public class NetconfMessageTransformUtil {
         return SchemaPath.create(true, rpc);
     }
 
-    public static String modifyOperationToXmlString(final ModifyAction operation) {
-        return operation.name().toLowerCase();
-    }
-
     // FIXME similar code is in netconf-notifications-impl , DRY
     public static void writeNormalizedNode(final NormalizedNode<?, ?> normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext context)
             throws IOException, XMLStreamException {
index b499bd8f6fc2ea2491243648b4c9fead2973d30d..23e21370260e8fbc40dd1a37998fc8b574bff9f8 100644 (file)
@@ -28,7 +28,10 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
 
-class NodeContainerProxy implements ContainerSchemaNode {
+/**
+ * Simple proxy for container like schema nodes, where user provides a collection of children schema nodes
+ */
+public final class NodeContainerProxy implements ContainerSchemaNode {
 
     private final Map<QName, DataSchemaNode> childNodes;
     private final QName qName;
@@ -146,4 +149,4 @@ class NodeContainerProxy implements ContainerSchemaNode {
     public List<UnknownSchemaNode> getUnknownSchemaNodes() {
         return Collections.emptyList();
     }
-}
+}
\ No newline at end of file
index e4f7fab6f062ae254c2fe6e0ceefb9645960f4d8..7f867a8997e475ecd941570c184c495adc991994 100644 (file)
@@ -27,7 +27,7 @@ public class NetconfStateSchemasTest {
         final DataSchemaNode schemasNode = ((ContainerSchemaNode) NetconfDevice.INIT_SCHEMA_CTX.getDataChildByName("netconf-state")).getDataChildByName("schemas");
 
         final Document schemasXml = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-state.schemas.payload.xml"));
-        final ToNormalizedNodeParser<Element, ContainerNode, ContainerSchemaNode> containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, NetconfDevice.INIT_SCHEMA_CTX).getContainerNodeParser();
+        final ToNormalizedNodeParser<Element, ContainerNode, ContainerSchemaNode> containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, NetconfDevice.INIT_SCHEMA_CTX, false).getContainerNodeParser();
         final ContainerNode compositeNodeSchemas = containerNodeParser.parse(Collections.singleton(schemasXml.getDocumentElement()), (ContainerSchemaNode) schemasNode);
         final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device", new InetSocketAddress(99)), compositeNodeSchemas);
 
index 157a3b719ebf5ab183ffaf47be76d9b6dd8557fc..294efadc6f9506cb32bdaa7e8404b33c6a034e89 100644 (file)
@@ -36,7 +36,7 @@ public class NetconfToNotificationTest {
     public void setup() throws Exception {
         final SchemaContext schemaContext = getNotificationSchemaContext(getClass());
 
-        messageTransformer = new NetconfMessageTransformer(schemaContext);
+        messageTransformer = new NetconfMessageTransformer(schemaContext, true);
 
         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
index 49f0abafc7360b25b9a4266d7002ca64c5066031..aa5f59be8d8702b1a0101ef66ae80e4adc6747ee 100644 (file)
@@ -69,7 +69,7 @@ public class NetconfToRpcRequestTest {
         cfgCtx = parser.resolveSchemaContext(Sets.union(configModules, notifModules));
         assertNotNull(cfgCtx);
 
-        messageTransformer = new NetconfMessageTransformer(cfgCtx);
+        messageTransformer = new NetconfMessageTransformer(cfgCtx, true);
     }
 
     private LeafNode<Object> buildLeaf(final QName running, final Object value) {
index 86385c3e913f62b3a28470fa98568408e73923bd..a43f807e0f8bfd95d527d0ba4cf0151dd63d510c 100644 (file)
@@ -285,7 +285,7 @@ public class NetconfMessageTransformerTest {
     }
 
     private NetconfMessageTransformer getTransformer(final SchemaContext schema) {
-        return new NetconfMessageTransformer(schema);
+        return new NetconfMessageTransformer(schema, true);
     }
 
     @Test
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java
deleted file mode 100644 (file)
index 39a8f42..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.controller.sal.connect.netconf.util;
-
-import static org.junit.Assert.assertEquals;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Collections2;
-import com.google.common.io.ByteSource;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-
-public class InstanceIdToNodesTest {
-
-    private static final String NS = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
-    private static final String REVISION = "2014-03-13";
-    private static final QName ID = QName.create(NS, REVISION, "id");
-    private SchemaContext ctx;
-
-    private final YangInstanceIdentifier.NodeIdentifier rootContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "test"));
-    private final YangInstanceIdentifier.NodeIdentifier outerContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-container"));
-    private final YangInstanceIdentifier.NodeIdentifier augmentedLeaf = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "augmented-leaf"));
-    private final YangInstanceIdentifier.AugmentationIdentifier augmentation = new YangInstanceIdentifier.AugmentationIdentifier(Collections.singleton(augmentedLeaf.getNodeType()));
-
-    private final YangInstanceIdentifier.NodeIdentifier outerList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-list"));
-    private final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListWithKey = new YangInstanceIdentifier.NodeIdentifierWithPredicates(QName.create(NS, REVISION, "outer-list"), ID, 1);
-    private final YangInstanceIdentifier.NodeIdentifier choice = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-choice"));
-    private final YangInstanceIdentifier.NodeIdentifier leafFromCase = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "one"));
-
-    private final YangInstanceIdentifier.NodeIdentifier leafList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "ordered-leaf-list"));
-    private final YangInstanceIdentifier.NodeWithValue leafListWithValue = new YangInstanceIdentifier.NodeWithValue(leafList.getNodeType(), "abcd");
-
-    static SchemaContext createTestContext() throws IOException, YangSyntaxErrorException {
-        final YangParserImpl parser = new YangParserImpl();
-        return parser.parseSources(Collections2.transform(Collections.singletonList("/schemas/filter-test.yang"), new Function<String, ByteSource>() {
-            @Override
-            public ByteSource apply(final String input) {
-                return new ByteSource() {
-                    @Override
-                    public InputStream openStream() throws IOException {
-                        return InstanceIdToNodesTest.class.getResourceAsStream(input);
-                    }
-                };
-            }
-        }));
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        ctx = createTestContext();
-
-    }
-
-    @Test
-    public void testInAugment() throws Exception {
-        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
-                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
-                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
-                                Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).build()
-                        ).build()
-                ).build()
-        ).build();
-
-        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf));
-        assertEquals(expectedFilter, filter);
-    }
-
-    @Test
-    public void testInAugmentLeafOverride() throws Exception {
-        final LeafNode<Object> lastLeaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).withValue("randomValue").build();
-
-        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
-                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
-                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
-                                lastLeaf
-                        ).build()
-                ).build()
-        ).build();
-
-        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), lastLeaf);
-        assertEquals(expectedFilter, filter);
-    }
-
-    @Test
-    public void testListChoice() throws Exception {
-        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
-                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
-                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
-                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
-                        ).withChild(
-                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
-                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
-                                ).build()
-                        ).build()
-                ).build()
-        ).build();
-
-        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey, choice, leafFromCase));
-        assertEquals(expectedFilter, filter);
-    }
-
-    @Test
-    public void testTopContainerLastChildOverride() throws Exception {
-        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
-                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
-                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
-                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
-                        ).withChild(
-                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
-                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
-                                ).build()
-                        ).build()
-                ).build()
-        ).build();
-
-        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer), expectedStructure);
-        assertEquals(expectedStructure, filter);
-    }
-
-    @Test
-    public void testListLastChildOverride() throws Exception {
-        final MapEntryNode outerListEntry = Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
-                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
-        ).build();
-        final MapNode lastChild = Builders.mapBuilder().withNodeIdentifier(this.outerList).withChild(
-                outerListEntry
-        ).build();
-        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
-                lastChild
-        ).build();
-
-        NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey), outerListEntry);
-        assertEquals(expectedStructure, filter);
-        filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey));
-        assertEquals(expectedStructure, filter);
-    }
-
-    @Test
-    public void testLeafList() throws Exception {
-        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
-                Builders.orderedLeafSetBuilder().withNodeIdentifier(leafList).withChild(
-                        Builders.leafSetEntryBuilder().withNodeIdentifier(leafListWithValue).withValue(leafListWithValue.getValue()).build()
-                ).build()
-        ).build();
-
-        final NormalizedNode<?, ?> filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, leafList, leafListWithValue));
-        assertEquals(expectedFilter, filter);
-    }
-}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang
deleted file mode 100644 (file)
index 6df5306..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-module normalization-test {
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
-    prefix "norm-test";
-
-    revision "2014-03-13" {
-        description "Initial revision.";
-    }
-
-    grouping outer-grouping {
-    }
-
-    container test {
-        list outer-list {
-            key id;
-            leaf id {
-                type uint16;
-            }
-            choice outer-choice {
-                case one {
-                    leaf one {
-                        type string;
-                    }
-                }
-                case two-three {
-                    leaf two {
-                        type string;
-                    }
-                    leaf three {
-                        type string;
-                    }
-               }
-           }
-           list inner-list {
-                key name;
-                ordered-by user;
-
-                leaf name {
-                    type string;
-                }
-                leaf value {
-                    type string;
-                }
-            }
-        }
-
-        list unkeyed-list {
-            leaf name {
-                type string;
-            }
-        }
-
-        leaf-list unordered-leaf-list {
-            type string;
-        }
-
-        leaf-list ordered-leaf-list {
-            ordered-by user;
-            type string;
-        }
-
-        container outer-container {
-        }
-
-        anyxml any-xml-data;
-    }
-
-    augment /norm-test:test/norm-test:outer-container {
-
-        leaf augmented-leaf {
-           type string;
-        }
-    }
-}
\ No newline at end of file
index e11cac2eb3fb0bc2e5a18abc219c239ce1587cad..10399ffeffa85a20b7bb0c3c8de77aff069a86ac 100644 (file)
@@ -27,6 +27,9 @@ import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
@@ -84,9 +87,18 @@ public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPr
             final JsonReader reader = new JsonReader(new InputStreamReader(entityStream));
             jsonParser.parse(reader);
 
-            final NormalizedNode<?, ?> partialResult = resultHolder.getResult();
+            NormalizedNode<?, ?> partialResult = resultHolder.getResult();
             final NormalizedNode<?, ?> result;
-            if(partialResult instanceof MapNode) {
+
+            // unwrap result from augmentation and choice nodes on PUT
+            if (!isPost()) {
+                while (partialResult instanceof AugmentationNode || partialResult instanceof ChoiceNode) {
+                    final Object childNode = ((DataContainerNode) partialResult).getValue().iterator().next();
+                    partialResult = (NormalizedNode<?, ?>) childNode;
+                }
+            }
+
+            if (partialResult instanceof MapNode) {
                 result = Iterables.getOnlyElement(((MapNode) partialResult).getValue());
             } else {
                 result = partialResult;
index 4257e172b4a5c22847b1f0469e810ab86dca828b..74a9bd2d313618be1b090e49c472bd7894457c50 100644 (file)
@@ -34,6 +34,8 @@ import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -126,18 +128,25 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
         final String docRootElm = doc.getDocumentElement().getLocalName();
         final String schemaNodeName = pathContext.getSchemaNode().getQName().getLocalName();
 
+        // FIXME the factory instance should be cached if the schema context is the same
+        final DomToNormalizedNodeParserFactory parserFactory =
+                DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, pathContext.getSchemaContext());
+
         if (!schemaNodeName.equalsIgnoreCase(docRootElm)) {
             final DataSchemaNode foundSchemaNode = findSchemaNodeOrParentChoiceByName(schemaNode, docRootElm);
             if (foundSchemaNode != null) {
+                if (schemaNode instanceof AugmentationTarget) {
+                    final AugmentationSchema augmentSchemaNode = findCorrespondingAugment(schemaNode, foundSchemaNode);
+                    if (augmentSchemaNode != null) {
+                        return parserFactory.getAugmentationNodeParser().parse(elements, augmentSchemaNode);
+                    }
+                }
                 schemaNode = foundSchemaNode;
             }
         }
 
-        // FIXME the factory instance should be cached if the schema context is the same
-        final DomToNormalizedNodeParserFactory parserFactory =
-                DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, pathContext.getSchemaContext());
-
         NormalizedNode<?, ?> parsed = null;
+
         if(schemaNode instanceof ContainerSchemaNode) {
             return parserFactory.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode);
         } else if(schemaNode instanceof ListSchemaNode) {
@@ -147,7 +156,6 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
             final ChoiceSchemaNode casted = (ChoiceSchemaNode) schemaNode;
             return parserFactory.getChoiceNodeParser().parse(elements, casted);
         }
-
         // FIXME : add another DataSchemaNode extensions e.g. LeafSchemaNode
 
         return parsed;
@@ -175,5 +183,17 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
         }
         return null;
     }
+
+    private static AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
+        if (parent instanceof AugmentationTarget && !((parent instanceof ChoiceCaseNode) || (parent instanceof ChoiceSchemaNode))) {
+            for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+                DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+                if (childInAugmentation != null) {
+                    return augmentation;
+                }
+            }
+        }
+        return null;
+    }
 }
 
index 346d54a77382f210b36e22cd1a64a8fe6fa21012..0378ae40ee39aa09a2c2a40199b4a5161964bd50 100644 (file)
@@ -11,6 +11,7 @@ import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastor
 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.ArrayList;
@@ -20,11 +21,7 @@ import java.util.concurrent.ExecutionException;
 import javax.ws.rs.core.Response.Status;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
@@ -43,6 +40,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -106,38 +105,34 @@ public class BrokerFacade {
 
     // PUT configuration
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         checkPreconditions();
-        final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
-        return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
+        return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
     }
 
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
-            final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
             return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
-                    payload, rootOp);
+                    payload, mountPoint.getSchemaContext());
         }
         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
     }
 
     // POST configuration
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         checkPreconditions();
-        final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
-        return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
+        return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
     }
 
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
-            final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
-                    payload, rootOp);
+                    payload, mountPoint.getSchemaContext());
         }
         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
     }
@@ -206,7 +201,7 @@ public class BrokerFacade {
 
     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier parentPath, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
+            final YangInstanceIdentifier parentPath, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         // FIXME: This is doing correct post for container and list children
         //        not sure if this will work for choice case
         final YangInstanceIdentifier path;
@@ -230,7 +225,7 @@ public class BrokerFacade {
             LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
         }
 
-        ensureParentsByMerge(datastore, path, rWTransaction, root);
+        ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
         rWTransaction.merge(datastore, path, payload);
         LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
         return rWTransaction.submit();
@@ -238,9 +233,9 @@ public class BrokerFacade {
 
     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
-        ensureParentsByMerge(datastore, path, writeTransaction, root);
+        ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
         writeTransaction.put(datastore, path, payload);
         return writeTransaction.submit();
     }
@@ -257,39 +252,34 @@ public class BrokerFacade {
         this.domDataBroker = domDataBroker;
     }
 
-    private final void ensureParentsByMerge(final LogicalDatastoreType store,
-            final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx,
-            final DataNormalizationOperation<?> root) {
-        final List<PathArgument> currentArguments = new ArrayList<>();
-        final Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
-        DataNormalizationOperation<?> currentOp = root;
-        while (iterator.hasNext()) {
-            final PathArgument currentArg = iterator.next();
-            try {
-                currentOp = currentOp.getChild(currentArg);
-            } catch (final DataNormalizationException e) {
-                rwTx.cancel();
-                throw new IllegalArgumentException(
-                        String.format("Invalid child encountered in path %s", normalizedPath), e);
-            }
-            currentArguments.add(currentArg);
-            final YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
+    private void ensureParentsByMerge(final LogicalDatastoreType store,
+                                      final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx, final SchemaContext schemaContext) {
+        final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
+        YangInstanceIdentifier rootNormalizedPath = null;
 
-            final Boolean exists;
+        final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
 
-            try {
-
-                final CheckedFuture<Boolean, ReadFailedException> future = rwTx.exists(store, currentPath);
-                exists = future.checkedGet();
-            } catch (final ReadFailedException e) {
-                LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
-                rwTx.cancel();
-                throw new IllegalStateException("Failed to read pre-existing data", e);
+        while(it.hasNext()) {
+            final PathArgument pathArgument = it.next();
+            if(rootNormalizedPath == null) {
+                rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
             }
 
-            if (!exists && iterator.hasNext()) {
-                rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
+            // Skip last element, its not a parent
+            if(it.hasNext()) {
+                normalizedPathWithoutChildArgs.add(pathArgument);
             }
         }
+
+        // No parent structure involved, no need to ensure parents
+        if(normalizedPathWithoutChildArgs.isEmpty()) {
+            return;
+        }
+
+        Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
+
+        final NormalizedNode<?, ?> parentStructure =
+                ImmutableNodes.fromInstanceId(schemaContext, YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
+        rwTx.merge(store, rootNormalizedPath, parentStructure);
     }
 }
index 33795889a122536c14f9e6c61f368b8b7c949ece..1514a15d1132417849a0efb29e53b0ea26113b8f 100644 (file)
@@ -697,7 +697,7 @@ public class RestconfImpl implements RestconfService {
                 if (mountPoint != null) {
                     broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
                 } else {
-                    broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
+                    broker.commitConfigurationDataPut(controllerContext.getGlobalSchema(), normalizedII, payload.getData()).checkedGet();
                 }
 
                 break;
@@ -828,12 +828,13 @@ public class RestconfImpl implements RestconfService {
             throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
 
-        final URI payloadNS = payload.getData().getNodeType().getNamespace();
-        if (payloadNS == null) {
-            throw new RestconfDocumentedException(
-                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
-                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
-        }
+        // FIXME: move this to parsing stage (we can have augmentation nodes here which do not have namespace)
+//        final URI payloadNS = payload.getData().getNodeType().getNamespace();
+//        if (payloadNS == null) {
+//            throw new RestconfDocumentedException(
+//                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
+//                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
+//        }
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
@@ -842,7 +843,7 @@ public class RestconfImpl implements RestconfService {
             if (mountPoint != null) {
                 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
             } else {
-                broker.commitConfigurationDataPost(normalizedII, payload.getData()).checkedGet();
+                broker.commitConfigurationDataPost(controllerContext.getGlobalSchema(), normalizedII, payload.getData()).checkedGet();
             }
         } catch(final RestconfDocumentedException e) {
             throw e;
index b696854dbb8b2e9d97655270f520dda8e95c1158..8ccd4a1f4166c71d3efaa475acd7a348db9b271e 100644 (file)
@@ -71,7 +71,7 @@ public class RestPutListDataTest {
         restconfImpl = RestconfImpl.getInstance();
         restconfImpl.setBroker(brokerFacade);
         restconfImpl.setControllerContext(controllerContext);
-        when(brokerFacade.commitConfigurationDataPut(any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
+        when(brokerFacade.commitConfigurationDataPut(any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
                 .thenReturn(mock(CheckedFuture.class));
     }
 
index dc1f968805a1f97d032d3d1b5ded964ab67c7b42..6542396612f6a2c4d57bc0226567e4ac507681c3 100644 (file)
@@ -55,6 +55,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 /**
@@ -181,7 +182,7 @@ public class BrokerFacadeTest {
 
         when(wTransaction.submit()).thenReturn(expFuture);
 
-        final Future<Void> actualFuture = brokerFacade.commitConfigurationDataPut(instanceID, dummyNode);
+        final Future<Void> actualFuture = brokerFacade.commitConfigurationDataPut((SchemaContext)null, instanceID, dummyNode);
 
         assertSame("commitConfigurationDataPut", expFuture, actualFuture);
 
@@ -208,7 +209,7 @@ public class BrokerFacadeTest {
         when(rwTransaction.submit()).thenReturn(expFuture);
 
         final CheckedFuture<Void, TransactionCommitFailedException> actualFuture = brokerFacade.commitConfigurationDataPost(
-                YangInstanceIdentifier.builder().build(), dummyNode);
+                (SchemaContext)null, YangInstanceIdentifier.builder().build(), dummyNode);
 
         assertSame("commitConfigurationDataPost", expFuture, actualFuture);
 
@@ -223,7 +224,8 @@ public class BrokerFacadeTest {
         when(rwTransaction.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(
                 dummyNodeInFuture);
         try {
-            brokerFacade.commitConfigurationDataPost(instanceID, dummyNode);
+            // Schema context is only necessary for ensuring parent structure
+            brokerFacade.commitConfigurationDataPost((SchemaContext)null, instanceID, dummyNode);
         } catch (final RestconfDocumentedException e) {
             assertEquals("getErrorTag", RestconfError.ErrorTag.DATA_EXISTS, e.getErrors().get(0).getErrorTag());
             throw e;
index 62a37ed1473c9e2fa74aac1b763ab8d130d6d99e..bb731a32d099955edf4a93a7b51d5afc3dab3b67 100644 (file)
@@ -137,7 +137,7 @@ public class RestPostOperationTest extends JerseyTest {
         final RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
                 TransactionStatus.COMMITED).build();
 
-        when(brokerFacade.commitConfigurationDataPost(any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
+        when(brokerFacade.commitConfigurationDataPost((SchemaContext)null, any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
                 .thenReturn(mock(CheckedFuture.class));
 
         final ArgumentCaptor<YangInstanceIdentifier> instanceIdCaptor = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
@@ -157,7 +157,7 @@ public class RestPostOperationTest extends JerseyTest {
         // FIXME : NEVER test a nr. of call some service in complex test suite
 //        verify(brokerFacade, times(2))
         verify(brokerFacade, times(1))
-                .commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture());
+                .commitConfigurationDataPost((SchemaContext)null, instanceIdCaptor.capture(), compNodeCaptor.capture());
 //        identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces, (urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)block]";
         assertEquals(identifier, ImmutableList.copyOf(instanceIdCaptor.getValue().getPathArguments()).toString());
     }
@@ -166,7 +166,7 @@ public class RestPostOperationTest extends JerseyTest {
     public void createConfigurationDataNullTest() throws UnsupportedEncodingException {
         initMocking();
 
-        when(brokerFacade.commitConfigurationDataPost(any(YangInstanceIdentifier.class),any(NormalizedNode.class)))
+        when(brokerFacade.commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),any(NormalizedNode.class)))
                 .thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
 
         //FIXME : find who is set schemaContext
index 39646f3ffd5197d26e212139da5607221190923a..f70af4e4f6193f5ee43f8f8dbe8e3cdffe8e6d45 100644 (file)
@@ -171,13 +171,13 @@ public class RestPutOperationTest extends JerseyTest {
 
         doThrow(OptimisticLockFailedException.class).
             when(brokerFacade).commitConfigurationDataPut(
-                any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
 
         assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
 
         doThrow(OptimisticLockFailedException.class).doReturn(mock(CheckedFuture.class)).
             when(brokerFacade).commitConfigurationDataPut(
-                any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
 
         assertEquals(200, put(uri, MediaType.APPLICATION_XML, xmlData));
     }
@@ -190,7 +190,7 @@ public class RestPutOperationTest extends JerseyTest {
 
         doThrow(TransactionCommitFailedException.class).
             when(brokerFacade).commitConfigurationDataPut(
-                any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                (SchemaContext)null, any(YangInstanceIdentifier.class), any(NormalizedNode.class));
 
         assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
     }
@@ -202,10 +202,10 @@ public class RestPutOperationTest extends JerseyTest {
     private void mockCommitConfigurationDataPutMethod(final boolean noErrors) {
         if (noErrors) {
             doReturn(mock(CheckedFuture.class)).when(brokerFacade).commitConfigurationDataPut(
-                    any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                    any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
         } else {
             doThrow(RestconfDocumentedException.class).when(brokerFacade).commitConfigurationDataPut(
-                    any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                    any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
         }
     }
 
index cb035161b4b2fb21388edcdaa2a857d257d52aca..29616620920111b394dbe450284a9443b362c8ac 100644 (file)
@@ -137,12 +137,15 @@ public class YangStoreService implements YangStoreContext {
     }
 
     public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
-        if(ref.get() == null || ref.get().get() == null) {
-            getYangStoreSnapshot();
+        final SoftReference<YangStoreSnapshot> yangStoreSnapshotSoftReference = ref.get();
+
+        YangStoreContext ret = yangStoreSnapshotSoftReference != null ? yangStoreSnapshotSoftReference.get() : null;
+        if(ret == null) {
+            ret = getYangStoreSnapshot();
         }
 
         this.listeners.add(listener);
-        listener.onCapabilitiesAdded(NetconfOperationServiceFactoryImpl.setupCapabilities(ref.get().get()));
+        listener.onCapabilitiesAdded(NetconfOperationServiceFactoryImpl.setupCapabilities(ret));
 
         return new AutoCloseable() {
             @Override
index 878c37846bcb4e9d828b40aaa37ed8cc35a532cd..d58bd54ae55d58a5fc1d565468cbb77bc2840886 100644 (file)
           <artifactId>xmlunit</artifactId>
           <scope>test</scope>
       </dependency>
-      <dependency>
-          <!--TODO remove this dependency on sal-netconf-connector - move InstanceIdToNodes to netconf-util,
-          remove odl-yangtools-models, sal-netconf-connector, model-inventory dependency from the odl-netconf-mdsal feature in mdsal features with this-->
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-netconf-connector</artifactId>
-      </dependency>
-
   </dependencies>
 
   <build>
index 9a66ceb5bcd0d8dc66fc77cfa2f215a37e77d223..d028a89e36d6d34c8d2535988739e5807908224f 100644 (file)
@@ -31,7 +31,6 @@ import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext;
 import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore;
 import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.sal.connect.netconf.util.InstanceIdToNodes;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
@@ -42,6 +41,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -151,7 +151,7 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
     protected Element serializeNodeWithParentStructure(Document document, YangInstanceIdentifier dataRoot, NormalizedNode node) {
         if (!dataRoot.equals(ROOT)) {
             return (Element) transformNormalizedNode(document,
-                    InstanceIdToNodes.serialize(schemaContext.getCurrentContext(), dataRoot, node),
+                    ImmutableNodes.fromInstanceId(schemaContext.getCurrentContext(), dataRoot, node),
                     ROOT);
         }
         return  (Element) transformNormalizedNode(document, node, ROOT);
index 2e7accad45e8950b7be2c3fb11a0a1ad1e74b93d..566dfde7c3f3117f7c01af10655f4cf4c18bc453 100644 (file)
@@ -31,14 +31,6 @@ public class SubtreeFilter {
     private static final Logger LOG = LoggerFactory.getLogger(SubtreeFilter.class);
 
     static Document applySubtreeFilter(Document requestDocument, Document rpcReply) throws NetconfDocumentedException {
-        // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
-        try {
-            rpcReply = XmlUtil.readXmlToDocument(XmlUtil.toString(rpcReply, true));
-        } catch (SAXException | IOException e) {
-            LOG.error("Cannot transform document", e);
-            throw new NetconfDocumentedException("Cannot transform document");
-        }
-
         OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(requestDocument);
         if (XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0.equals(operationNameAndNamespace.getNamespace()) &&
                 XmlNetconfConstants.GET.equals(operationNameAndNamespace.getOperationName()) ||
@@ -47,16 +39,26 @@ public class SubtreeFilter {
             // not implement filtering.
             Optional<XmlElement> maybeFilter = operationNameAndNamespace.getOperationElement().getOnlyChildElementOptionally(
                     XmlNetconfConstants.FILTER, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
-            if (maybeFilter.isPresent() && (
-                    "subtree".equals(maybeFilter.get().getAttribute("type"))||
-                            "subtree".equals(maybeFilter.get().getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0)))
-            ) {
+            if (!maybeFilter.isPresent()) {
+                return rpcReply;
+            }
 
+            // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
+            try {
+                rpcReply = XmlUtil.readXmlToDocument(XmlUtil.toString(rpcReply, true));
+            } catch (SAXException | IOException e) {
+                LOG.error("Cannot transform document", e);
+                throw new NetconfDocumentedException("Cannot transform document" + e);
+            }
+            XmlElement filter = maybeFilter.get();
+            if ("subtree".equals(filter.getAttribute("type"))||
+                    "subtree".equals(filter.getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0))) {
 
                 // do
                 return filtered(maybeFilter.get(), rpcReply);
             }
         }
+
         return rpcReply; // return identical document
     }
 
index 4529f81e575e9245013319ab75fd43a9974c5a16..3d46bf6ab43f8cb353fa1a33009581a16af32f51 100644 (file)
@@ -143,8 +143,9 @@ public final class XmlElement {
     }
 
     public String getName() {
-        if (element.getLocalName()!=null && !element.getLocalName().equals(DEFAULT_NAMESPACE_PREFIX)){
-            return element.getLocalName();
+        final String localName = element.getLocalName();
+        if (!Strings.isNullOrEmpty(localName)){
+            return localName;
         }
         return element.getTagName();
     }