HA cache sync for switch manager 76/576/3
authorPramila Singh <pramisin@cisco.com>
Mon, 8 Jul 2013 23:48:04 +0000 (16:48 -0700)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 9 Jul 2013 23:41:52 +0000 (23:41 +0000)
Change-Id: I00ab53d62f2c7622311ee07fd934c9ce8c000064
Signed-off-by: Pramila Singh <pramisin@cisco.com>
opendaylight/clustering/test/src/main/java/org/opendaylight/controller/clustering/test/internal/SimpleClient.java
opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/Activator.java
opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SpanConfig.java
opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java
opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SubnetConfig.java
opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SwitchConfig.java
opendaylight/switchmanager/implementation/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java
opendaylight/switchmanager/implementation/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImplTest.java
opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java
opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java

index b6ab95db33fff81260879bb97065b2fafbf840bd..0a0a9d8828ea3ec6de32739d13e85f99e2dd08bf 100644 (file)
@@ -273,7 +273,7 @@ public class SimpleClient implements CommandProvider {
             ci.println("\nNo Clustering services available");
             return;
         }
-        String containerName = ci.nextArgument();
+        String containerName = ci.nextArgument().toLowerCase();
         if (containerName == null) {
             ci.println("containerName not supplied");
             return;
@@ -372,7 +372,7 @@ public class SimpleClient implements CommandProvider {
 
     public void _dumper(CommandInterpreter ci) {
         ConcurrentMap<Object, Object> c;
-        String containerName = ci.nextArgument();
+        String containerName = ci.nextArgument().toLowerCase();
         if (containerName == null) {
             ci.println("containerName not supplied");
             return;
@@ -490,6 +490,7 @@ public class SimpleClient implements CommandProvider {
     }
 
     class DoListenRoleChanged implements IListenRoleChange {
+        @Override
         public void newActiveAvailable() {
             logger.debug("New Active is available");
         }
index 6cab6b9b4f8b0cf8c774f021e8487fd13365aedc..1a9675a0d445dc953030f10875d2ec95ae2b06aa 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.sal.implementation.internal;
 
+import org.apache.felix.dm.Component;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
@@ -29,7 +30,6 @@ import org.opendaylight.controller.sal.topology.IPluginOutTopologyService;
 import org.opendaylight.controller.sal.topology.ITopologyService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.felix.dm.Component;
 
 public class Activator extends ComponentActivatorAbstractBase {
     protected static final Logger logger = LoggerFactory
@@ -125,7 +125,7 @@ public class Activator extends ComponentActivatorAbstractBase {
             c.add(createContainerServiceDependency(containerName)
                     .setService(IPluginInInventoryService.class)
                     .setCallbacks("setPluginService", "unsetPluginService")
-                    .setRequired(true));
+                    .setRequired(false));
         }
 
         if (imp.equals(FlowProgrammerService.class)) {
index 83cb8b1cc9786f42757e1f609dedf724943c0f19..522f45946d98fbdc170db4ec9531d9128df53f97 100644 (file)
@@ -153,6 +153,6 @@ public class SpanConfig implements Serializable {
 
     @Override
     public String toString() {
-        return ("Span Config [nodeId=" + nodeId + " spanPort=" + spanPort + "]");
+        return ("SpanConfig [nodeId=" + nodeId + ", spanPort=" + spanPort + "]");
     }
 }
index e15303581f9ad32be958dd3971cd0cf92dc8d8bd..6a8eea2d0c7e3f75322dbf946bd90d4e190cfed2 100644 (file)
@@ -21,14 +21,14 @@ import org.opendaylight.controller.sal.core.NodeConnector;
  * The class describes subnet information including L3 address, vlan and set of
  * ports associated with the subnet.
  */
-public class Subnet implements Serializable {
+public class Subnet implements Cloneable, Serializable {
     private static final long serialVersionUID = 1L;
     // Key fields
     private InetAddress networkAddress;
     private short subnetMaskLength;
     // Property fields
     private short vlan;
-    private Set<NodeConnector> nodeConnectors;
+    private final Set<NodeConnector> nodeConnectors;
 
     public Subnet(InetAddress ip, short maskLen, short vlan) {
         this.networkAddress = ip;
@@ -43,6 +43,13 @@ public class Subnet implements Serializable {
         nodeConnectors = conf.getSubnetNodeConnectors();
     }
 
+    public Subnet(Subnet subnet) {
+        networkAddress = subnet.networkAddress;
+        subnetMaskLength = subnet.subnetMaskLength;
+        vlan = subnet.vlan;
+        nodeConnectors = new HashSet<NodeConnector>(subnet.nodeConnectors);
+    }
+
     /**
      * Add NodeConnectors to a subnet
      *
@@ -220,7 +227,7 @@ public class Subnet implements Serializable {
     public String toString() {
         return ("Subnet [networkAddress=" + networkAddress.getHostAddress()
                 + "/" + subnetMaskLength
-                + ((vlan == 0) ? "" : (" vlan=" + vlan)) + " "
+                + ((vlan == 0) ? "" : (", vlan=" + vlan)) + ", "
                 + ((isFlatLayer2()) ? "{[*, *]}" : nodeConnectors.toString()) + "]");
     }
 
@@ -246,4 +253,13 @@ public class Subnet implements Serializable {
         }
         return true;
     }
+
+    /**
+     * Implement clonable interface
+     */
+    @Override
+    public Subnet clone() {
+        return new Subnet(this);
+    }
+
 }
index 895f117321a6c61645742e425bcf0ad2d0af6cbd..52b5f5255d3e5c13f088a38c19819c8730fdf266 100644 (file)
@@ -34,7 +34,7 @@ import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
  */
 @XmlRootElement
 @XmlAccessorType(XmlAccessType.NONE)
-public class SubnetConfig implements Serializable {
+public class SubnetConfig implements Cloneable, Serializable {
     //static fields are by default excluded by Gson parser
     private static final long serialVersionUID = 1L;
     private static final String prettyFields[] = { GUIField.NAME.toString(),
@@ -60,6 +60,12 @@ public class SubnetConfig implements Serializable {
         nodePorts = sp;
     }
 
+    public SubnetConfig(SubnetConfig subnetConfig) {
+        name = subnetConfig.name;
+        subnet = subnetConfig.subnet;
+        nodePorts = new ArrayList<String>(subnetConfig.nodePorts);
+    }
+
     public String getName() {
         return name;
     }
@@ -233,7 +239,16 @@ public class SubnetConfig implements Serializable {
 
     @Override
     public String toString() {
-        return ("Subnet Config [Description=" + name + " Subnet=" + subnet
-                + " NodeConnectors=" + nodePorts + "]");
+        return ("SubnetConfig [Description=" + name + ", Subnet=" + subnet
+                + ", NodeConnectors=" + nodePorts + "]");
+    }
+
+    /**
+     * Implement clonable interface
+     */
+    @Override
+    public SubnetConfig clone() {
+        return new SubnetConfig(this);
     }
+
 }
index 61b2f0a3a8edf0a00a7a783d088b4751e379859a..c595c43e5bc48cf05d5f8b26e38d6289c915a092 100644 (file)
@@ -100,7 +100,7 @@ public class SwitchConfig implements Serializable {
 
     @Override
     public String toString() {
-        return ("Switch Config [Node=" + nodeId + " Description=" + description +
-                " Tier=" + tier + " Mode=" + mode + "]");
+        return ("SwitchConfig [Node=" + nodeId + ", Description=" + description +
+                ", Tier=" + tier + ", Mode=" + mode + "]");
     }
 }
index d32f98650b8b549ce8235fd3c14d22c55205ac56..8c95c46d72abd8e09e068da837288a4d31ad00bd 100644 (file)
@@ -40,6 +40,7 @@ import org.opendaylight.controller.clustering.services.IClusterServices;
 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
 import org.opendaylight.controller.sal.core.Bandwidth;
 import org.opendaylight.controller.sal.core.Config;
+import org.opendaylight.controller.sal.core.ConstructionException;
 import org.opendaylight.controller.sal.core.Description;
 import org.opendaylight.controller.sal.core.MacAddress;
 import org.opendaylight.controller.sal.core.Name;
@@ -95,7 +96,7 @@ CommandProvider {
     private final List<NodeConnector> spanNodeConnectors = new CopyOnWriteArrayList<NodeConnector>();
     private ConcurrentMap<InetAddress, Subnet> subnets; // set of Subnets keyed by the InetAddress
     private ConcurrentMap<String, SubnetConfig> subnetsConfigList;
-    private ConcurrentMap<Integer, SpanConfig> spanConfigList;
+    private ConcurrentMap<SpanConfig, SpanConfig> spanConfigList;
     private ConcurrentMap<String, SwitchConfig> nodeConfigList; // manually configured parameters for the node like name and tier
     private ConcurrentMap<Long, String> configSaveEvent;
     private ConcurrentMap<Node, Map<String, Property>> nodeProps; // properties are maintained in global container only
@@ -114,6 +115,7 @@ CommandProvider {
     private IClusterContainerServices clusterContainerService = null;
     private String containerName = null;
     private boolean isDefaultContainer = true;
+    private static final int REPLACE_RETRY = 1;
 
     public enum ReasonCode {
         SUCCESS("Success"), FAILURE("Failure"), INVALID_CONF(
@@ -206,6 +208,7 @@ CommandProvider {
     @SuppressWarnings("deprecation")
     private void allocateCaches() {
         if (this.clusterContainerService == null) {
+            this.nonClusterObjectCreate();
             log.warn("un-initialized clusterContainerService, can't create cache");
             return;
         }
@@ -251,7 +254,7 @@ CommandProvider {
             log.error("\nFailed to get cache for subnetsConfigList");
         }
 
-        spanConfigList = (ConcurrentMap<Integer, SpanConfig>) clusterContainerService
+        spanConfigList = (ConcurrentMap<SpanConfig, SpanConfig>) clusterContainerService
                 .getCache("switchmanager.spanConfigList");
         if (spanConfigList == null) {
             log.error("\nFailed to get cache for spanConfigList");
@@ -294,9 +297,9 @@ CommandProvider {
         }
     }
 
-    void nonClusterObjectCreate() {
+    private void nonClusterObjectCreate() {
         subnetsConfigList = new ConcurrentHashMap<String, SubnetConfig>();
-        spanConfigList = new ConcurrentHashMap<Integer, SpanConfig>();
+        spanConfigList = new ConcurrentHashMap<SpanConfig, SpanConfig>();
         nodeConfigList = new ConcurrentHashMap<String, SwitchConfig>();
         subnets = new ConcurrentHashMap<InetAddress, Subnet>();
         configSaveEvent = new ConcurrentHashMap<Long, String>();
@@ -305,26 +308,6 @@ CommandProvider {
         nodeConnectorNames = new ConcurrentHashMap<Node, Map<String, NodeConnector>>();
     }
 
-    @SuppressWarnings("deprecation")
-    private void destroyCaches(String container) {
-        if (this.clusterContainerService == null) {
-            log.info("un-initialized clusterContainerService, can't create cache");
-            return;
-        }
-
-        clusterContainerService.destroyCache("switchmanager.subnetsConfigList");
-        clusterContainerService.destroyCache("switchmanager.spanConfigList");
-        clusterContainerService.destroyCache("switchmanager.nodeConfigList");
-        clusterContainerService.destroyCache("switchmanager.subnets");
-        clusterContainerService.destroyCache("switchmanager.configSaveEvent");
-        clusterContainerService.destroyCache("switchmanager.nodeProps");
-        clusterContainerService
-        .destroyCache("switchmanager.nodeConnectorProps");
-        clusterContainerService
-        .destroyCache("switchmanager.nodeConnectorNames");
-        nonClusterObjectCreate();
-    }
-
     @Override
     public List<SubnetConfig> getSubnetsConfigList() {
         return new ArrayList<SubnetConfig>(subnetsConfigList.values());
@@ -390,19 +373,26 @@ CommandProvider {
         return swList;
     }
 
-    private void updateConfig(SubnetConfig conf, boolean add) {
+    private Status updateConfig(SubnetConfig conf, boolean add) {
         if (add) {
-            subnetsConfigList.put(conf.getName(), conf);
+            if(subnetsConfigList.putIfAbsent(conf.getName(), conf) != null) {
+                String msg = "Cluster conflict: Subnet with name " + conf.getName() + "already exists.";
+                return new Status(StatusCode.CONFLICT, msg);
+            }
         } else {
             subnetsConfigList.remove(conf.getName());
         }
+        return new Status(StatusCode.SUCCESS);
     }
 
-    private void updateDatabase(SubnetConfig conf, boolean add) {
-        Subnet subnet = subnets.get(conf.getIPnum());
+    private Status updateDatabase(SubnetConfig conf, boolean add) {
         if (add) {
-            if (subnet == null) {
+            Subnet subnetCurr = subnets.get(conf.getIPnum());
+            Subnet subnet;
+            if (subnetCurr == null) {
                 subnet = new Subnet(conf);
+            } else {
+                subnet = subnetCurr.clone();
             }
             // In case of API3 call we may receive the ports along with the
             // subnet creation
@@ -410,29 +400,38 @@ CommandProvider {
                 Set<NodeConnector> sp = conf.getSubnetNodeConnectors();
                 subnet.addNodeConnectors(sp);
             }
-            subnets.put(conf.getIPnum(), subnet);
-        } else { // This is the deletion of the whole subnet
-            if (subnet == null) {
-                return;
+            boolean result = false;
+            if(subnetCurr == null) {
+                if(subnets.putIfAbsent(conf.getIPnum(), subnet) == null) {
+                    result = true;
+                }
+            } else {
+                result = subnets.replace(conf.getIPnum(), subnetCurr, subnet);
+            }
+            if(!result) {
+                String msg = "Cluster conflict: Conflict while adding the subnet " + conf.getIPnum();
+                return new Status(StatusCode.CONFLICT, msg);
             }
+        } else { // This is the deletion of the whole subnet
             subnets.remove(conf.getIPnum());
         }
+        return new Status(StatusCode.SUCCESS);
     }
 
     private Status semanticCheck(SubnetConfig conf) {
         Subnet newSubnet = new Subnet(conf);
         Set<InetAddress> IPs = subnets.keySet();
         if (IPs == null) {
-            return new Status(StatusCode.SUCCESS, null);
+            return new Status(StatusCode.SUCCESS);
         }
         for (InetAddress i : IPs) {
             Subnet existingSubnet = subnets.get(i);
             if ((existingSubnet != null)
                     && !existingSubnet.isMutualExclusive(newSubnet)) {
-                return new Status(StatusCode.CONFLICT, null);
+                return new Status(StatusCode.CONFLICT);
             }
         }
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     private Status addRemoveSubnet(SubnetConfig conf, boolean add) {
@@ -455,13 +454,19 @@ CommandProvider {
                 return rc;
             }
         }
-        // Update Configuration
-        updateConfig(conf, add);
 
         // Update Database
-        updateDatabase(conf, add);
+        Status rc = updateDatabase(conf, add);
+
+        if (rc.isSuccess()) {
+            // Update Configuration
+            rc = updateConfig(conf, add);
+            if(!rc.isSuccess()) {
+                updateDatabase(conf, (!add));
+            }
+        }
 
-        return new Status(StatusCode.SUCCESS, null);
+        return rc;
     }
 
     /**
@@ -488,42 +493,77 @@ CommandProvider {
 
     @Override
     public Status addPortsToSubnet(String name, String switchPorts) {
-        // Update Configuration
-        SubnetConfig conf = subnetsConfigList.get(name);
-        if (conf == null) {
+        SubnetConfig confCurr = subnetsConfigList.get(name);
+        if (confCurr == null) {
             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
         }
-        if (!conf.isValidSwitchPort(switchPorts)) {
+        if (!confCurr.isValidSwitchPort(switchPorts)) {
             return new Status(StatusCode.BADREQUEST, "Invalid switchports");
         }
 
-        conf.addNodeConnectors(switchPorts);
-        subnetsConfigList.put(name, conf);
+        Subnet subCurr = subnets.get(confCurr.getIPnum());
+        if (subCurr == null) {
+            log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPnum());
+            return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
+        }
 
         // Update Database
-        Subnet sub = subnets.get(conf.getIPnum());
-        Set<NodeConnector> sp = conf.getNodeConnectors(switchPorts);
+        Subnet sub = subCurr.clone();
+        Set<NodeConnector> sp = confCurr.getNodeConnectors(switchPorts);
         sub.addNodeConnectors(sp);
-        subnets.put(conf.getIPnum(), sub);
-        return new Status(StatusCode.SUCCESS, null);
+        boolean subnetsReplace = subnets.replace(confCurr.getIPnum(), subCurr, sub);
+        if (!subnetsReplace) {
+            String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name;
+            return new Status(StatusCode.CONFLICT, msg);
+        }
+
+        // Update Configuration
+        SubnetConfig conf = confCurr.clone();
+        conf.addNodeConnectors(switchPorts);
+        boolean result = subnetsConfigList.replace(name, confCurr, conf);
+        if (!result) {
+            // TODO: recovery using Transactionality
+            String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name;
+            return new Status(StatusCode.CONFLICT, msg);
+        }
+
+        return new Status(StatusCode.SUCCESS);
     }
 
     @Override
     public Status removePortsFromSubnet(String name, String switchPorts) {
-        // Update Configuration
-        SubnetConfig conf = subnetsConfigList.get(name);
-        if (conf == null) {
+        SubnetConfig confCurr = subnetsConfigList.get(name);
+        if (confCurr == null) {
+            return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
+        }
+
+        Subnet subCurr = subnets.get(confCurr.getIPnum());
+        if (subCurr == null) {
+            log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPnum());
             return new Status(StatusCode.NOTFOUND, "Subnet does not exist");
         }
-        conf.removeNodeConnectors(switchPorts);
-        subnetsConfigList.put(name, conf);
 
         // Update Database
-        Subnet sub = subnets.get(conf.getIPnum());
-        Set<NodeConnector> sp = conf.getNodeConnectors(switchPorts);
+        Subnet sub = subCurr.clone();
+        Set<NodeConnector> sp = confCurr.getNodeConnectors(switchPorts);
         sub.deleteNodeConnectors(sp);
-        subnets.put(conf.getIPnum(), sub);
-        return new Status(StatusCode.SUCCESS, null);
+        boolean subnetsReplace = subnets.replace(confCurr.getIPnum(), subCurr, sub);
+        if (!subnetsReplace) {
+            String msg = "Cluster conflict: Conflict while removing ports from the subnet " + name;
+            return new Status(StatusCode.CONFLICT, msg);
+        }
+
+        // Update Configuration
+        SubnetConfig conf = confCurr.clone();
+        conf.removeNodeConnectors(switchPorts);
+        boolean result = subnetsConfigList.replace(name, confCurr, conf);
+        if (!result) {
+            // TODO: recovery using Transactionality
+            String msg = "Cluster conflict: Conflict while removing ports from " + conf;
+            return new Status(StatusCode.CONFLICT, msg);
+        }
+
+        return new Status(StatusCode.SUCCESS);
     }
 
     public String getContainerName() {
@@ -606,31 +646,51 @@ CommandProvider {
             return;
         }
 
+        SwitchConfig sc = nodeConfigList.get(cfgObject.getNodeId());
+        if (sc == null) {
+            if (nodeConfigList.putIfAbsent(cfgObject.getNodeId(), cfgObject) != null) {
+                return;
+            }
+        } else {
+            if (!nodeConfigList.replace(cfgObject.getNodeId(), sc, cfgObject)) {
+                return;
+            }
+        }
+
         boolean modeChange = false;
 
-        SwitchConfig sc = nodeConfigList.get(cfgObject.getNodeId());
         if ((sc == null) || !cfgObject.getMode().equals(sc.getMode())) {
             modeChange = true;
         }
 
-        nodeConfigList.put(cfgObject.getNodeId(), cfgObject);
         try {
             String nodeId = cfgObject.getNodeId();
             Node node = Node.fromString(nodeId);
-            Map<String, Property> propMap;
-            if (nodeProps.get(node) != null) {
-                propMap = nodeProps.get(node);
-            } else {
-                propMap = new HashMap<String, Property>();
+            Map<String, Property> propMapCurr = nodeProps.get(node);
+            Map<String, Property> propMap = new HashMap<String, Property>();
+            if (propMapCurr != null) {
+                for (String s : propMapCurr.keySet()) {
+                    propMap.put(s, propMapCurr.get(s).clone());
+                }
             }
             Property desc = new Description(cfgObject.getNodeDescription());
             propMap.put(desc.getName(), desc);
             Property tier = new Tier(Integer.parseInt(cfgObject.getTier()));
             propMap.put(tier.getName(), tier);
-            addNodeProps(node, propMap);
 
-            log.info("Set Node {}'s Mode to {}", nodeId,
-                    cfgObject.getMode());
+            if (propMapCurr == null) {
+                if (nodeProps.putIfAbsent(node, propMap) != null) {
+                    // TODO rollback using Transactionality
+                    return;
+                }
+            } else {
+                if (!nodeProps.replace(node, propMapCurr, propMap)) {
+                    // TODO rollback using Transactionality
+                    return;
+                }
+            }
+
+            log.info("Set Node {}'s Mode to {}", nodeId, cfgObject.getMode());
 
             if (modeChange) {
                 notifyModeChange(node, cfgObject.isProactive());
@@ -653,7 +713,7 @@ CommandProvider {
 
         retS = objWriter.write(new ConcurrentHashMap<String, SubnetConfig>(
                 subnetsConfigList), subnetFileName);
-        retP = objWriter.write(new ConcurrentHashMap<Integer, SpanConfig>(
+        retP = objWriter.write(new ConcurrentHashMap<SpanConfig, SpanConfig>(
                 spanConfigList), spanFileName);
         retS = objWriter.write(new ConcurrentHashMap<String, SwitchConfig>(
                 nodeConfigList), switchConfigFileName);
@@ -684,17 +744,17 @@ CommandProvider {
         }
 
         // Presence check
-        if (spanConfigList.containsKey(conf.hashCode())) {
+        if (spanConfigList.containsKey(conf)) {
             return new Status(StatusCode.CONFLICT, "Same span config exists");
         }
 
-        // Update database and notify clients
-        addSpanPorts(conf.getNode(), conf.getPortArrayList());
-
         // Update configuration
-        spanConfigList.put(conf.hashCode(), conf);
+        if (spanConfigList.putIfAbsent(conf, conf) == null) {
+            // Update database and notify clients
+            addSpanPorts(conf.getNode(), conf.getPortArrayList());
+        }
 
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     @Override
@@ -702,9 +762,9 @@ CommandProvider {
         removeSpanPorts(conf.getNode(), conf.getPortArrayList());
 
         // Update configuration
-        spanConfigList.remove(conf.hashCode());
+        spanConfigList.remove(conf);
 
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     @Override
@@ -739,11 +799,12 @@ CommandProvider {
             return;
         }
 
-        Map<String, Property> propMap;
-        if (nodeProps.get(node) != null) {
-            propMap = nodeProps.get(node);
-        } else {
-            propMap = new HashMap<String, Property>();
+        Map<String, Property> propMapCurr = nodeProps.get(node);
+        Map<String, Property> propMap = new HashMap<String, Property>();
+        if (propMapCurr != null) {
+            for (String s : propMapCurr.keySet()) {
+                propMap.put(s, propMapCurr.get(s).clone());
+            }
         }
 
         // copy node properties from plugin
@@ -759,8 +820,7 @@ CommandProvider {
             String nodeId = node.toString();
             for (SwitchConfig conf : nodeConfigList.values()) {
                 if (conf.getNodeId().equals(nodeId)) {
-                    Property description = new Description(
-                            conf.getNodeDescription());
+                    Property description = new Description(conf.getNodeDescription());
                     propMap.put(description.getName(), description);
                     Property tier = new Tier(Integer.parseInt(conf.getTier()));
                     propMap.put(tier.getName(), tier);
@@ -769,7 +829,22 @@ CommandProvider {
                 }
             }
         }
-        addNodeProps(node, propMap);
+
+        boolean result = false;
+        if (propMapCurr == null) {
+            if (nodeProps.putIfAbsent(node, propMap) == null) {
+                result = true;
+            }
+        } else {
+            result = nodeProps.replace(node, propMapCurr, propMap);
+        }
+
+        if (!result) {
+            log.debug(
+                    "Cluster conflict: Conflict while adding the node properties. Node: {}  Properties: {}",
+                    node.getID(), props);
+            addNodeProps(node, propMap);
+        }
 
         // check if span ports are configed
         addSpanPorts(node);
@@ -804,11 +879,12 @@ CommandProvider {
             return;
         }
 
-        Map<String, Property> propMap;
-        if (nodeProps.get(node) != null) {
-            propMap = nodeProps.get(node);
-        } else {
-            propMap = new HashMap<String, Property>();
+        Map<String, Property> propMapCurr = nodeProps.get(node);
+        Map<String, Property> propMap = new HashMap<String, Property>();
+        if (propMapCurr != null) {
+            for (String s : propMapCurr.keySet()) {
+                propMap.put(s, propMapCurr.get(s).clone());
+            }
         }
 
         // copy node properties from plugin
@@ -817,7 +893,20 @@ CommandProvider {
                 propMap.put(prop.getName(), prop);
             }
         }
-        addNodeProps(node, propMap);
+
+        if (propMapCurr == null) {
+            if (nodeProps.putIfAbsent(node, propMap) != null) {
+                log.debug("Cluster conflict: Conflict while updating the node. Node: {}  Properties: {}",
+                        node.getID(), props);
+                addNodeProps(node, propMap);
+            }
+        } else {
+            if (!nodeProps.replace(node, propMapCurr, propMap)) {
+                log.debug("Cluster conflict: Conflict while updating the node. Node: {}  Properties: {}",
+                        node.getID(), props);
+                addNodeProps(node, propMap);
+            }
+        }
 
         /* notify node listeners */
         notifyNode(node, UpdateType.CHANGED, propMap);
@@ -928,30 +1017,67 @@ CommandProvider {
 
     @Override
     public void setNodeProp(Node node, Property prop) {
-        /* Get a copy of the property map */
-        Map<String, Property> propMap = getNodeProps(node);
-        if (propMap == null) {
-            return;
-        }
 
-        propMap.put(prop.getName(), prop);
-        this.nodeProps.put(node, propMap);
+        for (int i = 0; i <= REPLACE_RETRY; i++) {
+            /* Get a copy of the property map */
+            Map<String, Property> propMapCurr = getNodeProps(node);
+            if (propMapCurr == null) {
+                return;
+            }
+
+            Map<String, Property> propMap = new HashMap<String, Property>();
+            for (String s : propMapCurr.keySet()) {
+                propMap.put(s, propMapCurr.get(s).clone());
+            }
+
+            propMap.put(prop.getName(), prop);
+
+            if (nodeProps.replace(node, propMapCurr, propMap)) {
+                return;
+            }
+            if (!propMapCurr.get(prop.getName()).equals(nodeProps.get(node).get(prop.getName()))) {
+                log.warn("Cluster conflict: Unable to add property {} to node {}.", prop.getName(), node.getID());
+                return;
+            }
+        }
+        log.warn("Cluster conflict: Unable to add property {} to node {}.", prop.getName(), node.getID());
     }
 
     @Override
     public Status removeNodeProp(Node node, String propName) {
-        Map<String, Property> propMap = getNodeProps(node);
-        if (propMap != null) {
-            propMap.remove(propName);
-            this.nodeProps.put(node, propMap);
+        for (int i = 0; i <= REPLACE_RETRY; i++) {
+            Map<String, Property> propMapCurr = getNodeProps(node);
+            if (propMapCurr != null) {
+                if (!propMapCurr.containsKey(propName)) {
+                    return new Status(StatusCode.SUCCESS);
+                }
+                Map<String, Property> propMap = new HashMap<String, Property>();
+                for (String s : propMapCurr.keySet()) {
+                    propMap.put(s, propMapCurr.get(s).clone());
+                }
+
+                propMap.remove(propName);
+                if (nodeProps.replace(node, propMapCurr, propMap)) {
+                    return new Status(StatusCode.SUCCESS);
+                }
+                if (!propMapCurr.get(propName).equals(nodeProps.get(node).get(propName))) {
+                    String msg = "Cluster conflict: Unable to remove property " + propName + " for node "
+                            + node.getID();
+                    return new Status(StatusCode.CONFLICT, msg);
+                }
+
+            } else {
+                return new Status(StatusCode.SUCCESS);
+            }
         }
-        return new Status(StatusCode.SUCCESS, null);
+        String msg = "Cluster conflict: Unable to remove property " + propName + " for node " + node.getID();
+        return new Status(StatusCode.CONFLICT, msg);
     }
 
     @Override
     public Status removeNodeAllProps(Node node) {
         this.nodeProps.remove(node);
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     @Override
@@ -1100,36 +1226,73 @@ CommandProvider {
     @Override
     public Status addNodeConnectorProp(NodeConnector nodeConnector,
             Property prop) {
-        Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+        Map<String, Property> propMapCurr = getNodeConnectorProps(nodeConnector);
+        Map<String, Property> propMap = new HashMap<String, Property>();
 
-        if (propMap == null) {
-            propMap = new HashMap<String, Property>();
+        if (propMapCurr != null) {
+            for (String s : propMapCurr.keySet()) {
+                propMap.put(s, propMapCurr.get(s).clone());
+            }
         }
 
+        String msg = "Cluster conflict: Unable to add NodeConnector Property.";
         // Just add the nodeConnector if prop is not available (in a non-default
         // container)
         if (prop == null) {
-            nodeConnectorProps.put(nodeConnector, propMap);
-            return new Status(StatusCode.SUCCESS, null);
+            if (propMapCurr == null) {
+                if (nodeConnectorProps.putIfAbsent(nodeConnector, propMap) != null) {
+                    return new Status(StatusCode.CONFLICT, msg);
+                }
+            } else {
+                if (!nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap)) {
+                    return new Status(StatusCode.CONFLICT, msg);
+                }
+            }
+            return new Status(StatusCode.SUCCESS);
         }
 
         propMap.put(prop.getName(), prop);
-        nodeConnectorProps.put(nodeConnector, propMap);
+        if (propMapCurr == null) {
+            if (nodeConnectorProps.putIfAbsent(nodeConnector, propMap) != null) {
+                return new Status(StatusCode.CONFLICT, msg);
+            }
+        } else {
+            if (!nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap)) {
+                return new Status(StatusCode.CONFLICT, msg);
+            }
+        }
 
         if (prop.getName().equals(Name.NamePropName)) {
             if (nodeConnectorNames != null) {
                 Node node = nodeConnector.getNode();
-                Map<String, NodeConnector> map = nodeConnectorNames.get(node);
-                if (map == null) {
-                    map = new HashMap<String, NodeConnector>();
+                Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
+                Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
+                if (mapCurr != null) {
+                    for (String s : mapCurr.keySet()) {
+                        try {
+                            map.put(s, new NodeConnector(mapCurr.get(s)));
+                        } catch (ConstructionException e) {
+                            e.printStackTrace();
+                        }
+                    }
                 }
 
                 map.put(((Name) prop).getValue(), nodeConnector);
-                nodeConnectorNames.put(node, map);
+                if (mapCurr == null) {
+                    if (nodeConnectorNames.putIfAbsent(node, map) != null) {
+                        // TODO: recovery using Transactionality
+                        return new Status(StatusCode.CONFLICT, msg);
+                    }
+                } else {
+                    if (!nodeConnectorNames.replace(node, mapCurr, map)) {
+                        // TODO: recovery using Transactionality
+                        return new Status(StatusCode.CONFLICT, msg);
+                    }
+                }
             }
         }
 
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     /**
@@ -1142,32 +1305,53 @@ CommandProvider {
      * @return success or failed reason
      */
     @Override
-    public Status removeNodeConnectorProp(NodeConnector nodeConnector,
-            String propName) {
-        Map<String, Property> propMap = getNodeConnectorProps(nodeConnector);
+    public Status removeNodeConnectorProp(NodeConnector nodeConnector, String propName) {
+        Map<String, Property> propMapCurr = getNodeConnectorProps(nodeConnector);
 
-        if (propMap == null) {
+        if (propMapCurr == null) {
             /* Nothing to remove */
-            return new Status(StatusCode.SUCCESS, null);
+            return new Status(StatusCode.SUCCESS);
+        }
+
+        Map<String, Property> propMap = new HashMap<String, Property>();
+
+        for (String s : propMapCurr.keySet()) {
+            propMap.put(s, propMapCurr.get(s).clone());
         }
 
         propMap.remove(propName);
-        nodeConnectorProps.put(nodeConnector, propMap);
+        boolean result = nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap);
+        String msg = "Cluster conflict: Unable to remove NodeConnector property.";
+        if (!result) {
+            return new Status(StatusCode.CONFLICT, msg);
+        }
 
-        if (nodeConnectorNames != null) {
-            Name name = ((Name) getNodeConnectorProp(nodeConnector,
-                    Name.NamePropName));
-            if (name != null) {
-                Node node = nodeConnector.getNode();
-                Map<String, NodeConnector> map = nodeConnectorNames.get(node);
-                if (map != null) {
-                    map.remove(name.getValue());
-                    nodeConnectorNames.put(node, map);
+        if (propName.equals(Name.NamePropName)) {
+            if (nodeConnectorNames != null) {
+                Name name = ((Name) getNodeConnectorProp(nodeConnector, Name.NamePropName));
+                if (name != null) {
+                    Node node = nodeConnector.getNode();
+                    Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
+                    if (mapCurr != null) {
+                        Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
+                        for (String s : mapCurr.keySet()) {
+                            try {
+                                map.put(s, new NodeConnector(mapCurr.get(s)));
+                            } catch (ConstructionException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                        map.remove(name.getValue());
+                        if (!nodeConnectorNames.replace(node, mapCurr, map)) {
+                            // TODO: recovery using Transactionality
+                            return new Status(StatusCode.CONFLICT, msg);
+                        }
+                    }
                 }
             }
         }
 
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     /**
@@ -1180,20 +1364,31 @@ CommandProvider {
     @Override
     public Status removeNodeConnectorAllProps(NodeConnector nodeConnector) {
         if (nodeConnectorNames != null) {
-            Name name = ((Name) getNodeConnectorProp(nodeConnector,
-                    Name.NamePropName));
+            Name name = ((Name) getNodeConnectorProp(nodeConnector, Name.NamePropName));
             if (name != null) {
                 Node node = nodeConnector.getNode();
-                Map<String, NodeConnector> map = nodeConnectorNames.get(node);
-                if (map != null) {
+                Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
+                if (mapCurr != null) {
+                    Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
+                    for (String s : mapCurr.keySet()) {
+                        try {
+                            map.put(s, new NodeConnector(mapCurr.get(s)));
+                        } catch (ConstructionException e) {
+                            e.printStackTrace();
+                        }
+                    }
                     map.remove(name.getValue());
-                    nodeConnectorNames.put(node, map);
+                    if (!nodeConnectorNames.replace(node, mapCurr, map)) {
+                        log.warn("Cluster conflict: Unable remove Name property of nodeconnector {}, skip.",
+                                nodeConnector.getID());
+                    }
                 }
+
             }
         }
         nodeConnectorProps.remove(nodeConnector);
 
-        return new Status(StatusCode.SUCCESS, null);
+        return new Status(StatusCode.SUCCESS);
     }
 
     /**
@@ -1339,22 +1534,21 @@ CommandProvider {
         }
 
         Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
-        for(Map.Entry<Node, Map<String, Property>> entry : nodeProp.entrySet()) {
+        for (Map.Entry<Node, Map<String, Property>> entry : nodeProp.entrySet()) {
             Node node = entry.getKey();
-            log.debug("getInventories: {} added for container {}",
-                    new Object[] { node, containerName });
+            log.debug("getInventories: {} added for container {}", new Object[] { node, containerName });
             Map<String, Property> propMap = entry.getValue();
             Set<Property> props = new HashSet<Property>();
-            for(Property property : propMap.values()) {
+            for (Property property : propMap.values()) {
                 props.add(property);
             }
             addNode(node, props);
         }
 
         Map<NodeConnector, Map<String, Property>> nodeConnectorProp = this.inventoryService.getNodeConnectorProps();
-        for(Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp.entrySet()) {
+        for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp.entrySet()) {
             Map<String, Property> propMap = entry.getValue();
-            for(Property property : propMap.values()) {
+            for (Property property : propMap.values()) {
                 addNodeConnectorProp(entry.getKey(), property);
             }
         }
index 9bb231fb0398b1bcbf1fcaae96f606d596374676..25d54906edc74fda57bfb331af1c52f193101bbe 100644 (file)
@@ -31,7 +31,7 @@ public class SwitchManagerImplTest {
     @Test
     public void testSwitchManagerAddRemoveSubnet() {
         SwitchManagerImpl switchmgr = new SwitchManagerImpl();
-        switchmgr.nonClusterObjectCreate();
+        switchmgr.startUp();
 
         ArrayList<String> portList = new ArrayList<String>();
         portList.add("1/1");
@@ -56,7 +56,7 @@ public class SwitchManagerImplTest {
     @Test
     public void testSwitchManagerNodeConnectors() {
         SwitchManagerImpl switchmgr = new SwitchManagerImpl();
-        switchmgr.nonClusterObjectCreate();
+        switchmgr.startUp();
 
         State state;
         Bandwidth bw;
index ea24ffcf88b0e6d287367ee60748e3c87baba2f3..39a9cceb085fc68cade5c53d961b06a8a9c8d6b2 100644 (file)
@@ -8,65 +8,48 @@
 
 package org.opendaylight.controller.switchmanager.internal;
 
-import java.net.InetAddress;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
 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.Map.Entry;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.Bundle;
 import javax.inject.Inject;
 
-import org.eclipse.osgi.framework.console.CommandProvider;
 import org.junit.Assert;
-import org.junit.Test;
 import org.junit.Before;
-import org.junit.After;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.opendaylight.controller.sal.core.Actions;
 import org.opendaylight.controller.sal.core.Bandwidth;
 import org.opendaylight.controller.sal.core.Buffers;
 import org.opendaylight.controller.sal.core.Capabilities;
+import org.opendaylight.controller.sal.core.Capabilities.CapabilitiesType;
 import org.opendaylight.controller.sal.core.ConstructionException;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
 import org.opendaylight.controller.sal.core.State;
 import org.opendaylight.controller.sal.core.TimeStamp;
-import org.opendaylight.controller.sal.core.UpdateType;
-import org.opendaylight.controller.sal.core.Property;
-import org.opendaylight.controller.sal.core.Capabilities.CapabilitiesType;
-import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
-import org.opendaylight.controller.sal.utils.NodeCreator;
-import org.opendaylight.controller.sal.utils.Status;
-import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
-import org.opendaylight.controller.switchmanager.*;
-import org.opendaylight.controller.configuration.IConfigurationContainerAware;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
-
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.util.Filter;
-import org.osgi.framework.BundleContext;
-import static org.junit.Assert.*;
-import org.ops4j.pax.exam.junit.Configuration;
-import static org.ops4j.pax.exam.CoreOptions.*;
-
 import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.PaxExam;
 import org.ops4j.pax.exam.util.PathUtils;
-import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
-import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @RunWith(PaxExam.class)
 public class SwitchManagerIT {
-    private Logger log = LoggerFactory.getLogger(SwitchManagerIT.class);
+    private final Logger log = LoggerFactory.getLogger(SwitchManagerIT.class);
     // get the OSGI bundle context
     @Inject
     private BundleContext bc;
@@ -107,29 +90,28 @@ public class SwitchManagerIT {
                         .versionAsInProject(),
                 mavenBundle("ch.qos.logback", "logback-classic")
                         .versionAsInProject(),
-
-                mavenBundle("org.opendaylight.controller", "switchmanager")
-                        .versionAsInProject(),
-                mavenBundle("org.opendaylight.controller",
-                        "switchmanager.implementation").versionAsInProject(),
-                mavenBundle("org.opendaylight.controller", "sal")
+                mavenBundle("org.opendaylight.controller", "clustering.stub")
                         .versionAsInProject(),
-                mavenBundle("org.opendaylight.controller", "sal.implementation")
+                mavenBundle("org.opendaylight.controller", "configuration")
                         .versionAsInProject(),
+                mavenBundle("org.opendaylight.controller",
+                        "configuration.implementation").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller", "containermanager")
                         .versionAsInProject(),
                 mavenBundle("org.opendaylight.controller",
                         "containermanager.implementation").versionAsInProject(),
                 mavenBundle("org.opendaylight.controller",
                         "clustering.services").versionAsInProject(),
-                mavenBundle("org.opendaylight.controller", "clustering.stub")
+                mavenBundle("org.opendaylight.controller", "sal")
                         .versionAsInProject(),
-                mavenBundle("org.opendaylight.controller", "configuration")
+                mavenBundle("org.opendaylight.controller", "sal.implementation")
                         .versionAsInProject(),
                 mavenBundle("org.opendaylight.controller",
-                        "configuration.implementation").versionAsInProject(),
+                                "protocol_plugins.stub").versionAsInProject(),
+                mavenBundle("org.opendaylight.controller", "switchmanager")
+                        .versionAsInProject(),
                 mavenBundle("org.opendaylight.controller",
-                        "protocol_plugins.stub").versionAsInProject(),
+                        "switchmanager.implementation").versionAsInProject(),
                 mavenBundle("org.jboss.spec.javax.transaction",
                         "jboss-transaction-api_1.1_spec").versionAsInProject(),
                 mavenBundle("org.apache.commons", "commons-lang3")
@@ -203,11 +185,11 @@ public class SwitchManagerIT {
 
         Assert.assertTrue(this.switchManager.getNodeProp(node,
                 Capabilities.CapabilitiesPropName).equals(
-                new Capabilities((int) 3)));
+                new Capabilities(3)));
         Assert.assertTrue(this.switchManager.getNodeProp(node,
-                Actions.ActionsPropName).equals(new Actions((int) 2)));
+                Actions.ActionsPropName).equals(new Actions(2)));
         Assert.assertTrue(this.switchManager.getNodeProp(node,
-                Buffers.BuffersPropName).equals(new Buffers((int) 1)));
+                Buffers.BuffersPropName).equals(new Buffers(1)));
         Assert.assertTrue(this.switchManager.getNodeProp(node,
                 TimeStamp.TimeStampPropName).equals(
                 new TimeStamp(100000L, "connectedSince")));
index 534c2c293c9c9fecc9bf8467fc74d0e237261ac9..04f9863312891978502efedb34410a334da02fd1 100644 (file)
@@ -16,17 +16,11 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentMap;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.codehaus.jackson.map.ObjectMapper;
-import org.opendaylight.controller.web.DaylightWebUtil;
-import org.opendaylight.controller.web.IDaylightWeb;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
 import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
 import org.opendaylight.controller.sal.authorization.Privilege;
@@ -46,6 +40,13 @@ import org.opendaylight.controller.switchmanager.SpanConfig;
 import org.opendaylight.controller.switchmanager.SubnetConfig;
 import org.opendaylight.controller.switchmanager.Switch;
 import org.opendaylight.controller.switchmanager.SwitchConfig;
+import org.opendaylight.controller.web.DaylightWebUtil;
+import org.opendaylight.controller.web.IDaylightWeb;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
 
 import com.google.gson.Gson;