ONOS-4154 generates consistent hash for flow ID across multiple instances

Change-Id: I8de850dcc5d6d40ed563f7d476c6e13a09060093
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
index b8605f1..313cbfc 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
@@ -16,6 +16,11 @@
 package org.onosproject.net.flow;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Charsets;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
@@ -393,9 +398,20 @@
         }
 
         private int hash() {
-            return Objects.hash(deviceId, priority, selector, tableId);
-        }
+            Funnel<TrafficSelector> selectorFunnel = (from, into) -> from.criteria()
+                    .stream()
+                    .forEach(c -> into.putString(c.toString(), Charsets.UTF_8));
 
+            HashFunction hashFunction = Hashing.murmur3_32();
+            HashCode hashCode = hashFunction.newHasher()
+                    .putString(deviceId.toString(), Charsets.UTF_8)
+                    .putObject(selector, selectorFunnel)
+                    .putInt(priority)
+                    .putInt(tableId)
+                    .hash();
+
+            return hashCode.asInt();
+        }
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onosproject/net/flow/DefaultFlowRuleTest.java b/core/api/src/test/java/org/onosproject/net/flow/DefaultFlowRuleTest.java
index e8230f7..c0f11d6 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/DefaultFlowRuleTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/DefaultFlowRuleTest.java
@@ -158,4 +158,34 @@
         assertThat(rule.treatment(), is(TREATMENT));
         assertThat(rule.timeout(), is(44));
     }
+
+    /**
+     * Tests flow ID is consistent.
+     */
+    @Test
+    public void testCreationWithConsistentFlowId() {
+        final FlowRule rule1 =
+                DefaultFlowRule.builder()
+                        .forDevice(did("1"))
+                        .withSelector(SELECTOR)
+                        .withTreatment(TREATMENT)
+                        .withPriority(22)
+                        .forTable(1)
+                        .fromApp(APP_ID)
+                        .makeTemporary(44)
+                        .build();
+
+        final FlowRule rule2 =
+                DefaultFlowRule.builder()
+                        .forDevice(did("1"))
+                        .withSelector(SELECTOR)
+                        .withTreatment(TREATMENT)
+                        .withPriority(22)
+                        .forTable(1)
+                        .fromApp(APP_ID)
+                        .makeTemporary(44)
+                        .build();
+
+        new EqualsTester().addEqualityGroup(rule1.id(), rule2.id()).testEquals();
+    }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java b/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
index 923de26..aaa3db5 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
@@ -15,13 +15,16 @@
  */
 package org.onosproject.net.flow;
 
+import java.util.List;
 import java.util.Set;
 
+import com.google.common.collect.Lists;
 import org.hamcrest.Description;
 import org.hamcrest.Factory;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.Test;
+import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
@@ -78,6 +81,26 @@
     }
 
     /**
+     * Tests criteria order is consistent.
+     */
+    @Test
+    public void testCriteriaOrder() {
+        final TrafficSelector selector1 = DefaultTrafficSelector.builder()
+                .matchInPort(PortNumber.portNumber(11))
+                .matchEthType(Ethernet.TYPE_ARP)
+                .build();
+        final TrafficSelector selector2 = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchInPort(PortNumber.portNumber(11))
+                .build();
+
+        List<Criterion> criteria1 = Lists.newArrayList(selector1.criteria());
+        List<Criterion> criteria2 = Lists.newArrayList(selector2.criteria());
+
+        new EqualsTester().addEqualityGroup(criteria1, criteria2).testEquals();
+    }
+
+    /**
      * Hamcrest matcher to check that a selector contains a
      * Criterion with the specified type.
      */
diff --git a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
index 1e621f5..235699d 100644
--- a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
@@ -569,7 +569,7 @@
 
         @Override
         public Set<Criterion> criteria() {
-            return null;
+            return Collections.emptySet();
         }
 
         @Override