Fixes for GossipIntentStore
 * State checking to prevent state updates outrunning.
 * Copy IntentData on the way in and out of the store.

Change-Id: Id18164d15c896c5a62376aac17b7c8c2cac420c2
diff --git a/core/store/dist/src/main/java/org/onosproject/store/impl/EventuallyConsistentMapImpl.java b/core/store/dist/src/main/java/org/onosproject/store/impl/EventuallyConsistentMapImpl.java
index 0e6a590..66e85bd 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/impl/EventuallyConsistentMapImpl.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/impl/EventuallyConsistentMapImpl.java
@@ -242,11 +242,13 @@
         synchronized (this) {
             Timestamp removed = removedItems.get(key);
             if (removed != null && removed.compareTo(timestamp) > 0) {
+                log.debug("ecmap - removed was newer {}", value);
                 return false;
             }
 
             Timestamped<V> existing = items.get(key);
             if (existing != null && existing.isNewer(timestamp)) {
+                log.debug("ecmap - existing was newer {}", value);
                 return false;
             } else {
                 items.put(key, new Timestamped<>(value, timestamp));
diff --git a/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java b/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
index 17439ef..d81435f 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
@@ -45,13 +45,18 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static org.onosproject.net.intent.IntentState.FAILED;
+import static org.onosproject.net.intent.IntentState.INSTALLED;
+import static org.onosproject.net.intent.IntentState.INSTALLING;
+import static org.onosproject.net.intent.IntentState.WITHDRAWING;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Manages inventory of Intents in a distributed data store that uses optimistic
  * replication and gossip based techniques.
  */
-@Component(immediate = false, enabled = false)
+@Component(immediate = false, enabled = true)
 @Service
 public class GossipIntentStore
         extends AbstractStore<IntentEvent, IntentStoreDelegate>
@@ -144,17 +149,104 @@
         return null;
     }
 
+    private IntentData copyData(IntentData original) {
+        if (original == null) {
+            return null;
+        }
+        IntentData result =
+                new IntentData(original.intent(), original.state(), original.version());
+
+        if (original.installables() != null) {
+            result.setInstallables(original.installables());
+        }
+        return result;
+    }
+
+    /**
+     * TODO.
+     * @param currentData
+     * @param newData
+     * @return
+     */
+    private boolean isUpdateAcceptable(IntentData currentData, IntentData newData) {
+
+        if (currentData == null) {
+            return true;
+        } else if (currentData.version().compareTo(newData.version()) < 0) {
+            return true;
+        } else if (currentData.version().compareTo(newData.version()) > 0) {
+            return false;
+        }
+
+        // current and new data versions are the same
+        IntentState currentState = currentData.state();
+        IntentState newState = newData.state();
+
+        switch (newState) {
+        case INSTALLING:
+            if (currentState == INSTALLING) {
+                return false;
+            }
+            // FALLTHROUGH
+        case INSTALLED:
+            if (currentState == INSTALLED) {
+                return false;
+            } else if (currentState == WITHDRAWING || currentState == WITHDRAWN) {
+                log.warn("Invalid state transition from {} to {} for intent {}",
+                         currentState, newState, newData.key());
+                return false;
+            }
+            return true;
+
+        case WITHDRAWING:
+            if (currentState == WITHDRAWING) {
+                return false;
+            }
+            // FALLTHROUGH
+        case WITHDRAWN:
+            if (currentState == WITHDRAWN) {
+                return false;
+            } else if (currentState == INSTALLING || currentState == INSTALLED) {
+                log.warn("Invalid state transition from {} to {} for intent {}",
+                         currentState, newState, newData.key());
+                return false;
+            }
+            return true;
+
+
+        case FAILED:
+            if (currentState == FAILED) {
+                return false;
+            }
+            return true;
+
+
+        case COMPILING:
+        case RECOMPILING:
+        case INSTALL_REQ:
+        case WITHDRAW_REQ:
+        default:
+            log.warn("Invalid state {} for intent {}", newState, newData.key());
+            return false;
+        }
+    }
+
     @Override
     public void write(IntentData newData) {
-        log.debug("writing intent {}", newData);
+        //log.debug("writing intent {}", newData);
 
-        // Only the master is modifying the current state. Therefore assume
-        // this always succeeds
-        currentState.put(newData.key(), newData);
+        IntentData currentData = currentState.get(newData.key());
 
-        // if current.put succeeded
-        pending.remove(newData.key(), newData);
+        if (isUpdateAcceptable(currentData, newData)) {
+            // Only the master is modifying the current state. Therefore assume
+            // this always succeeds
+            currentState.put(newData.key(), copyData(newData));
 
+            // if current.put succeeded
+            pending.remove(newData.key(), newData);
+        } else {
+            log.debug("not writing update: {}", newData);
+        }
         /*try {
             notifyDelegate(IntentEvent.getEvent(newData));
         } catch (IllegalArgumentException e) {
@@ -179,7 +271,7 @@
 
     @Override
     public IntentData getIntentData(Key key) {
-        return currentState.get(key);
+        return copyData(currentState.get(key));
     }
 
     @Override
@@ -189,7 +281,7 @@
             log.debug("updating timestamp");
             data.setVersion(new SystemClockTimestamp());
         }
-        pending.put(data.key(), data);
+        pending.put(data.key(), copyData(data));
     }
 
     @Override
@@ -234,7 +326,7 @@
                 // some work.
                 if (isMaster(event.value().intent())) {
                     if (delegate != null) {
-                        delegate.process(event.value());
+                        delegate.process(copyData(event.value()));
                     }
                 }
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/intent/impl/SimpleIntentStore.java b/core/store/dist/src/main/java/org/onosproject/store/intent/impl/SimpleIntentStore.java
index 59b1d74..89952ce 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/intent/impl/SimpleIntentStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/intent/impl/SimpleIntentStore.java
@@ -43,7 +43,7 @@
 
 //TODO Note: this store will be removed once the GossipIntentStore is stable
 
-@Component(immediate = true)
+@Component(immediate = true, enabled = false)
 @Service
 //FIXME remove this
 public class SimpleIntentStore