Initial implementation of the ClusteredDataStore 70/2470/4
authorMoiz Raja <moraja@cisco.com>
Wed, 6 Nov 2013 23:38:13 +0000 (15:38 -0800)
committerMoiz Raja <moraja@cisco.com>
Thu, 7 Nov 2013 19:04:26 +0000 (11:04 -0800)
Please review and see if this implementation is headed in the right direction.
At this point this is mostly unit testing. This should give an idea of what the code intends to do.

The ClusteredDataStore is dependent on IClusterGlobalServices and provides a simple pass through mechanism.

Change-Id: I2ab628a40fa8a44ae85be3ad678717ae139faa59
Signed-off-by: Moiz Raja <moraja@cisco.com>
opendaylight/md-sal/clustered-data-store/implementation/pom.xml
opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/ClusteredDataStore.java [new file with mode: 0644]
opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/Activator.java
opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStore.java [deleted file]
opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreImpl.java [new file with mode: 0644]
opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreManager.java [new file with mode: 0644]
opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ActivatorTest.java
opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreImplTest.java [new file with mode: 0644]
opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreTest.java [deleted file]

index f822ac5..bb43809 100644 (file)
     </plugins>
   </build>
   <dependencies>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
     <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>sal-common-api</artifactId>
         <version>1.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal</artifactId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+       <groupId>org.mockito</groupId>
+       <artifactId>mockito-all</artifactId>
+       <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>equinoxSDK381</groupId>
       <artifactId>org.eclipse.osgi</artifactId>
diff --git a/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/ClusteredDataStore.java b/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/ClusteredDataStore.java
new file mode 100644 (file)
index 0000000..0a577ad
--- /dev/null
@@ -0,0 +1,18 @@
+
+/*
+ * 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.datastore;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface ClusteredDataStore extends DataReader<InstanceIdentifier<? extends Object>, Object>, DataCommitHandler<InstanceIdentifier<? extends Object>,Object> {
+}
index 101da7f..c94355d 100644 (file)
@@ -9,14 +9,46 @@
 
 package org.opendaylight.controller.datastore.internal;
 
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.datastore.ClusteredDataStore;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Set;
+
 public class Activator extends ComponentActivatorAbstractBase {
     protected static final Logger logger = LoggerFactory
             .getLogger(Activator.class);
 
 
+    @Override
+    protected Object[] getGlobalImplementations(){
+       logger.debug("Calling getGlobalImplementations to return:", ClusteredDataStoreManager.class);
+        return new Object[] {
+            ClusteredDataStoreManager.class
+        };
+    }
+
+
+    @Override
+    protected void configureGlobalInstance(Component c, Object imp){
+        if (imp.equals(ClusteredDataStoreManager.class)) {
+            Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
+
+            c.setInterface(new String[] { ClusteredDataStore.class.getName() }, props);
+            logger.debug("configureGlobalInstance adding dependency:", IClusterGlobalServices.class);
+            
+            c.add(createServiceDependency().setService(
+                    IClusterGlobalServices.class).setCallbacks(
+                    "setClusterGlobalServices",
+                    "unsetClusterGlobalServices").setRequired(true));
+
+        }
+    }
+
 
 }
diff --git a/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStore.java b/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStore.java
deleted file mode 100644 (file)
index 7c25b14..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.opendaylight.controller.datastore.internal;
-
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
-import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.md.sal.common.api.data.DataReader;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-public class ClusteredDataStore implements DataReader<InstanceIdentifier<? extends Object>, Object>, DataCommitHandler<InstanceIdentifier<? extends Object>,Object> {
-    @Override
-    public DataCommitTransaction<InstanceIdentifier<? extends Object>, Object> requestCommit(DataModification<InstanceIdentifier<? extends Object>, Object> modification) {
-        return null;
-    }
-
-    @Override
-    public Object readOperationalData(InstanceIdentifier<? extends Object> path) {
-        return null;
-    }
-
-    @Override
-    public Object readConfigurationData(InstanceIdentifier<? extends Object> path) {
-        return null;
-    }
-}
diff --git a/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreImpl.java b/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreImpl.java
new file mode 100644 (file)
index 0000000..f2e7773
--- /dev/null
@@ -0,0 +1,122 @@
+
+/*
+ * 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.datastore.internal;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.datastore.ClusteredDataStore;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * The ClusteredDataStoreImpl stores global data to be shared across a controller cluster. It uses Clustering Services.
+ */
+public class ClusteredDataStoreImpl implements ClusteredDataStore {
+
+
+    public static final String OPERATIONAL_DATA_CACHE = "clustered_data_store.operational_data_cache";
+    public static final String CONFIGURATION_DATA_CACHE = "clustered_data_store.configuration_data_cache";
+
+    private ConcurrentMap operationalDataCache;
+    private ConcurrentMap configurationDataCache;
+
+    public ClusteredDataStoreImpl(IClusterGlobalServices clusterGlobalServices) throws CacheExistException, CacheConfigException {
+        Preconditions.checkNotNull(clusterGlobalServices, "clusterGlobalServices cannot be null");
+
+        operationalDataCache = clusterGlobalServices.createCache(OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+        if(operationalDataCache == null){
+            Preconditions.checkNotNull(operationalDataCache, "operationalDataCache cannot be null");
+        }
+
+        configurationDataCache = clusterGlobalServices.createCache(CONFIGURATION_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+
+        if(configurationDataCache == null){
+            Preconditions.checkNotNull(configurationDataCache, "configurationDataCache cannot be null");
+        }
+
+    }
+
+    @Override
+    public DataCommitTransaction<InstanceIdentifier<? extends Object>, Object> requestCommit(DataModification<InstanceIdentifier<? extends Object>, Object> modification) {
+        return new ClusteredDataStoreTransaction(modification);
+    }
+
+    @Override
+    public Object readOperationalData(InstanceIdentifier<? extends Object> path) {
+        Preconditions.checkNotNull(path, "path cannot be null");
+        return operationalDataCache.get(path);
+    }
+
+    @Override
+    public Object readConfigurationData(InstanceIdentifier<? extends Object> path) {
+        Preconditions.checkNotNull(path, "path cannot be null");
+        return configurationDataCache.get(path);
+    }
+
+    private RpcResult<Void> finish(final ClusteredDataStoreTransaction transaction) {
+      final DataModification<InstanceIdentifier<? extends Object>,Object> modification = transaction.getModification();
+
+      this.configurationDataCache.putAll(modification.getUpdatedConfigurationData());
+      this.operationalDataCache.putAll(modification.getUpdatedOperationalData());
+
+      for (final InstanceIdentifier<? extends Object> removal : modification.getRemovedConfigurationData()) {
+        this.configurationDataCache.remove(removal);
+      }
+
+      for (final InstanceIdentifier<? extends Object> removal : modification.getRemovedOperationalData()) {
+        this.operationalDataCache.remove(removal  );
+      }
+
+      Set<RpcError> _emptySet = Collections.<RpcError>emptySet();
+      return Rpcs.<Void>getRpcResult(true, null, _emptySet);
+    }
+
+    private RpcResult<Void> rollback(final ClusteredDataStoreTransaction transaction) {
+      Set<RpcError> _emptySet = Collections.<RpcError>emptySet();
+      return Rpcs.<Void>getRpcResult(true, null, _emptySet);
+    }
+
+    private class ClusteredDataStoreTransaction implements DataCommitTransaction<InstanceIdentifier<? extends Object>, Object> {
+        private final DataModification<InstanceIdentifier<? extends Object>,Object> modification;
+
+        public ClusteredDataStoreTransaction(DataModification<InstanceIdentifier<? extends Object>,Object> modification){
+            Preconditions.checkNotNull(modification, "modification cannot be null");
+
+            this.modification = modification;
+        }
+
+        @Override
+        public DataModification<InstanceIdentifier<? extends Object>, Object> getModification() {
+            return this.modification;
+        }
+
+        @Override
+        public RpcResult<Void> finish() throws IllegalStateException {
+            return ClusteredDataStoreImpl.this.finish(this);
+        }
+
+        @Override
+        public RpcResult<Void> rollback() throws IllegalStateException {
+            return ClusteredDataStoreImpl.this.rollback(this);
+        }
+    }
+}
diff --git a/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreManager.java b/opendaylight/md-sal/clustered-data-store/implementation/src/main/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreManager.java
new file mode 100644 (file)
index 0000000..42ed2bb
--- /dev/null
@@ -0,0 +1,71 @@
+
+/*
+ * 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.datastore.internal;
+
+import com.google.common.base.Preconditions;
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.datastore.ClusteredDataStore;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class ClusteredDataStoreManager implements ClusteredDataStore {
+
+    private ClusteredDataStoreImpl clusteredDataStore = null;
+    private IClusterGlobalServices clusterGlobalServices = null;
+
+    @Override
+    public DataCommitTransaction<InstanceIdentifier<? extends Object>, Object> requestCommit(DataModification<InstanceIdentifier<? extends Object>, Object> modification) {
+        Preconditions.checkState(clusteredDataStore != null, "clusteredDataStore cannot be null");
+        return clusteredDataStore.requestCommit(modification);
+    }
+
+    @Override
+    public Object readOperationalData(InstanceIdentifier<? extends Object> path) {
+        Preconditions.checkState(clusteredDataStore != null, "clusteredDataStore cannot be null");
+        return clusteredDataStore.readOperationalData(path);
+    }
+
+    @Override
+    public Object readConfigurationData(InstanceIdentifier<? extends Object> path) {
+        Preconditions.checkState(clusteredDataStore != null, "clusteredDataStore cannot be null");
+        return clusteredDataStore.readConfigurationData(path);
+    }
+
+
+    public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices){
+        this.clusterGlobalServices = clusterGlobalServices;
+    }
+
+    public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices){
+        this.clusterGlobalServices = null;
+        this.clusteredDataStore = null;
+    }
+
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     *
+     */
+    void init(Component c) {
+        try {
+            clusteredDataStore = new ClusteredDataStoreImpl(clusterGlobalServices);
+        } catch (CacheExistException e) {
+            throw new IllegalStateException("could not construct clusteredDataStore");
+        } catch (CacheConfigException e) {
+            throw new IllegalStateException("could not construct clusteredDataStore");
+        }
+    }
+}
index d36e9ba..5ced9d9 100644 (file)
@@ -1,13 +1,70 @@
+
+/*
+ * 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.datastore.internal;
 
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ServiceDependency;
+
+import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
 
 import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class ActivatorTest {
 
+       private static  ServiceDependency serviceDependency;
+       
+       @BeforeClass 
+       public static void initialize(){
+               serviceDependency = mock(ServiceDependency.class);
+       }
+       
+       private class ActivatorTestImpl extends Activator{
+                protected ServiceDependency createServiceDependency() {
+                       return ActivatorTest.serviceDependency;
+                   }
+       }
+       
     @Test
     public void construct(){
         assertNotNull(new Activator());
     }
+    
+    @Test
+    public void construct_OnInvokeOfGlobalImpl_ShouldReturnNotNullObject(){
+        Activator activator = new Activator();
+        
+        assertNotNull(activator.getGlobalImplementations());
+        assertEquals(ClusteredDataStoreManager.class,activator.getGlobalImplementations()[0]);
+    }
+    
+    @Test
+    public void construct_OnInvokeOfConfigGlobalInstance_ShouldNotThrowAnyExceptions(){
+       Activator activator = new ActivatorTestImpl();
+       
+       Component c = mock(Component.class);
+       Object clusterDataStoreMgr = ClusteredDataStoreManager.class;
+       
+       when(serviceDependency.setService(IClusterGlobalServices.class)).thenReturn(serviceDependency);
+       when(serviceDependency.setCallbacks("setClusterGlobalServices",
+                    "unsetClusterGlobalServices")).thenReturn(serviceDependency);
+       when(serviceDependency.setRequired(true)).thenReturn(serviceDependency);
+       
+       
+       activator.configureGlobalInstance(c, clusterDataStoreMgr);
+       
+       
+    }
+    
 }
diff --git a/opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreImplTest.java b/opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreImplTest.java
new file mode 100644 (file)
index 0000000..8049bae
--- /dev/null
@@ -0,0 +1,248 @@
+package org.opendaylight.controller.datastore.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ClusteredDataStoreImplTest {
+    @Before
+    public void setUp(){
+
+    }
+
+    @Test
+    public void constructor_WhenPassedANullClusteringServices_ShouldThrowANullPointerException() throws CacheExistException, CacheConfigException {
+        try {
+            new ClusteredDataStoreImpl(null);
+        } catch(NullPointerException npe){
+            assertEquals("clusterGlobalServices cannot be null", npe.getMessage());
+        }
+    }
+
+    @Test
+    public void constructor_WhenClusteringServicesReturnsANullOperationalDataCache_ShouldThrowANullPointerException() throws CacheExistException, CacheConfigException {
+        try {
+            new ClusteredDataStoreImpl(mock(IClusterGlobalServices.class));
+        } catch(NullPointerException npe){
+            assertEquals("operationalDataCache cannot be null", npe.getMessage());
+        }
+    }
+
+    @Test
+    public void constructor_WhenClusteringServicesReturnsANullOConfigurationDataCache_ShouldThrowANullPointerException() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = mock(IClusterGlobalServices.class);
+
+        // Confused about the following line?
+        // See this http://stackoverflow.com/questions/10952629/a-strange-generics-edge-case-with-mockito-when-and-generic-type-inference
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(new ConcurrentHashMap<Object, Object>());
+
+
+        try {
+            new ClusteredDataStoreImpl(mockClusterGlobalServices);
+        } catch(NullPointerException npe){
+            assertEquals("configurationDataCache cannot be null", npe.getMessage());
+        }
+    }
+
+    @Test
+    public void constructor_WhenPassedAValidClusteringServices_ShouldNotThrowAnyExceptions() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        new ClusteredDataStoreImpl(mockClusterGlobalServices);
+    }
+
+
+    @Test
+    public void readOperationalData_WhenPassedANullPath_ShouldThrowANullPointerException() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        try {
+            store.readOperationalData(null);
+        } catch(NullPointerException npe){
+            assertEquals("path cannot be null", npe.getMessage());
+        }
+    }
+
+    @Test
+    public void readOperationalData_WhenPassedAKeyThatDoesNotExistInTheCache_ShouldReturnNull() throws CacheExistException, CacheConfigException {
+        InstanceIdentifier path = InstanceIdentifier.builder().toInstance();
+
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        assertNull(store.readOperationalData(path));
+    }
+
+    @Test
+    public void readOperationalData_WhenPassedAKeyThatDoesExistInTheCache_ShouldReturnTheValueObject() throws CacheExistException, CacheConfigException {
+        InstanceIdentifier path = InstanceIdentifier.builder().toInstance();
+
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ConcurrentMap mockOperationalDataCache = mock(ConcurrentMap.class);
+
+        Object valueObject = mock(Object.class);
+
+        when(mockOperationalDataCache.get(path)).thenReturn(valueObject);
+
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mockOperationalDataCache);
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.CONFIGURATION_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(new ConcurrentHashMap<Object, Object>());
+
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        assertEquals(valueObject, store.readOperationalData(path));
+    }
+
+
+
+    @Test
+    public void readConfigurationData_WhenPassedANullPath_ShouldThrowANullPointerException() throws CacheExistException, CacheConfigException {
+
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        try {
+            store.readConfigurationData(null);
+        } catch(NullPointerException npe){
+            assertEquals("path cannot be null", npe.getMessage());
+        }
+    }
+
+
+    @Test
+    public void readConfigurationData_WhenPassedAKeyThatDoesNotExistInTheCache_ShouldReturnNull() throws CacheExistException, CacheConfigException {
+        InstanceIdentifier path = InstanceIdentifier.builder().toInstance();
+
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        assertNull(store.readConfigurationData(path));
+    }
+
+    @Test
+    public void readConfigurationData_WhenPassedAKeyThatDoesExistInTheCache_ShouldReturnTheValueObject() throws CacheExistException, CacheConfigException {
+        InstanceIdentifier path = InstanceIdentifier.builder().toInstance();
+
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ConcurrentMap mockConfigurationDataCache = mock(ConcurrentMap.class);
+
+        Object valueObject = mock(Object.class);
+
+        when(mockConfigurationDataCache.get(path)).thenReturn(valueObject);
+
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mock(ConcurrentMap.class));
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.CONFIGURATION_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mockConfigurationDataCache);
+
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        assertEquals(valueObject, store.readConfigurationData(path));
+    }
+
+
+    @Test
+    public void requestCommit_ShouldReturnADataTransaction() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = createClusterGlobalServices();
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        assertNotNull(store.requestCommit(mock(DataModification.class)));
+
+
+    }
+
+    @Test
+    public void finishingADataTransaction_ShouldUpdateTheUnderlyingCache() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = mock(IClusterGlobalServices.class);
+
+        ConcurrentMap mockConfigurationDataCache = mock(ConcurrentMap.class);
+        ConcurrentMap mockOperationalDataCache = mock(ConcurrentMap.class);
+
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mockOperationalDataCache);
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.CONFIGURATION_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mockConfigurationDataCache);
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        DataModification mockModification = mock(DataModification.class);
+
+        Map configurationData = mock(Map.class);
+        Map operationalData = mock(Map.class);
+
+        when(mockModification.getUpdatedConfigurationData()).thenReturn(configurationData);
+        when(mockModification.getUpdatedOperationalData()).thenReturn(operationalData);
+
+        DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends Object>, Object> transaction = store.requestCommit(mockModification);
+
+        transaction.finish();
+
+        verify(mockConfigurationDataCache).putAll(mockModification.getUpdatedConfigurationData());
+        verify(mockOperationalDataCache).putAll(mockModification.getUpdatedOperationalData());
+    }
+
+
+    @Test
+    public void rollingBackADataTransaction_ShouldDoNothing() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = mock(IClusterGlobalServices.class);
+
+        ConcurrentMap mockConfigurationDataCache = mock(ConcurrentMap.class);
+        ConcurrentMap mockOperationalDataCache = mock(ConcurrentMap.class);
+
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mockOperationalDataCache);
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.CONFIGURATION_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mockConfigurationDataCache);
+
+        ClusteredDataStoreImpl store = new ClusteredDataStoreImpl(mockClusterGlobalServices);
+
+        DataModification mockModification = mock(DataModification.class);
+
+        Map configurationData = mock(Map.class);
+        Map operationalData = mock(Map.class);
+
+        when(mockModification.getUpdatedConfigurationData()).thenReturn(configurationData);
+        when(mockModification.getUpdatedOperationalData()).thenReturn(operationalData);
+
+        DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends Object>, Object> transaction = store.requestCommit(mockModification);
+
+        transaction.rollback();
+
+        verify(mockConfigurationDataCache, never()).putAll(mockModification.getUpdatedConfigurationData());
+        verify(mockOperationalDataCache, never()).putAll(mockModification.getUpdatedOperationalData());
+
+    }
+
+
+    private IClusterGlobalServices createClusterGlobalServices() throws CacheExistException, CacheConfigException {
+        IClusterGlobalServices mockClusterGlobalServices = mock(IClusterGlobalServices.class);
+
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.OPERATIONAL_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mock(ConcurrentMap.class));
+        Mockito.<ConcurrentMap<?,?>>when(mockClusterGlobalServices.createCache(ClusteredDataStoreImpl.CONFIGURATION_DATA_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(mock(ConcurrentMap.class));
+
+        return mockClusterGlobalServices;
+    }
+}
diff --git a/opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreTest.java b/opendaylight/md-sal/clustered-data-store/implementation/src/test/java/org/opendaylight/controller/datastore/internal/ClusteredDataStoreTest.java
deleted file mode 100644 (file)
index 7a8e8e8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.opendaylight.controller.datastore.internal;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertNotNull;
-
-public class ClusteredDataStoreTest {
-    @Test
-    public void construct(){
-        assertNotNull(new ClusteredDataStore());
-    }
-}