Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java b/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java
index 134d995..d8c795a 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/FlowRuleStatusCompleter.java
@@ -5,7 +5,7 @@
 
 import org.apache.karaf.shell.console.Completer;
 import org.apache.karaf.shell.console.completer.StringsCompleter;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 
 /**
  * Device ID completer.
@@ -16,7 +16,7 @@
         // Delegate string completer
         StringsCompleter delegate = new StringsCompleter();
 
-        FlowRuleState[] states = FlowRuleState.values();
+        FlowEntryState[] states = FlowEntryState.values();
         SortedSet<String> strings = delegate.getStrings();
         for (int i = 0; i < states.length; i++) {
             strings.add(states[i].toString().toLowerCase());
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
index d49ae6e..8b6cefc 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/FlowsListCommand.java
@@ -13,8 +13,8 @@
 import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.DeviceService;
-import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 import org.onlab.onos.net.flow.FlowRuleService;
 
 import com.google.common.collect.Maps;
@@ -45,7 +45,7 @@
     protected void execute() {
         DeviceService deviceService = get(DeviceService.class);
         FlowRuleService service = get(FlowRuleService.class);
-        Map<Device, List<FlowRule>> flows = getSortedFlows(deviceService, service);
+        Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
         for (Device d : flows.keySet()) {
             printFlows(d, flows.get(d));
         }
@@ -57,12 +57,12 @@
      * @param service device service
      * @return sorted device list
      */
-    protected Map<Device, List<FlowRule>> getSortedFlows(DeviceService deviceService, FlowRuleService service) {
-        Map<Device, List<FlowRule>> flows = Maps.newHashMap();
-        List<FlowRule> rules;
-        FlowRuleState s = null;
+    protected Map<Device, List<FlowEntry>> getSortedFlows(DeviceService deviceService, FlowRuleService service) {
+        Map<Device, List<FlowEntry>> flows = Maps.newHashMap();
+        List<FlowEntry> rules;
+        FlowEntryState s = null;
         if (state != null && !state.equals("any")) {
-            s = FlowRuleState.valueOf(state.toUpperCase());
+            s = FlowEntryState.valueOf(state.toUpperCase());
         }
         Iterable<Device> devices = uri == null ?  deviceService.getDevices() :
             Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
@@ -71,7 +71,7 @@
                 rules = newArrayList(service.getFlowEntries(d.id()));
             } else {
                 rules = newArrayList();
-                for (FlowRule f : service.getFlowEntries(d.id())) {
+                for (FlowEntry f : service.getFlowEntries(d.id())) {
                     if (f.state().equals(s)) {
                         rules.add(f);
                     }
@@ -88,13 +88,13 @@
      * @param d the device
      * @param flows the set of flows for that device.
      */
-    protected void printFlows(Device d, List<FlowRule> flows) {
+    protected void printFlows(Device d, List<FlowEntry> flows) {
         print("Device: " + d.id());
         if (flows == null | flows.isEmpty()) {
             print(" %s", "No flows.");
             return;
         }
-        for (FlowRule f : flows) {
+        for (FlowEntry f : flows) {
             print(FMT, Long.toHexString(f.id().value()), f.state(), f.bytes(),
                     f.packets(), f.life(), f.priority());
             print(SFMT, f.selector().criteria());
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
new file mode 100644
index 0000000..5a0f55b
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowEntry.java
@@ -0,0 +1,111 @@
+package org.onlab.onos.net.flow;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.onlab.onos.net.DeviceId;
+import org.slf4j.Logger;
+
+public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
+
+    private final Logger log = getLogger(getClass());
+
+    private long life;
+    private long packets;
+    private long bytes;
+    private FlowEntryState state;
+
+    private long lastSeen = -1;
+
+
+    public DefaultFlowEntry(DeviceId deviceId, TrafficSelector selector,
+            TrafficTreatment treatment, int priority, FlowEntryState state,
+            long life, long packets, long bytes, long flowId,
+            int timeout) {
+        super(deviceId, selector, treatment, priority, flowId, timeout);
+        this.state = state;
+        this.life = life;
+        this.packets = packets;
+        this.bytes = bytes;
+        this.lastSeen = System.currentTimeMillis();
+    }
+
+    public DefaultFlowEntry(FlowRule rule, FlowEntryState state,
+            long life, long packets, long bytes) {
+        super(rule);
+        this.state = state;
+        this.life = life;
+        this.packets = packets;
+        this.bytes = bytes;
+        this.lastSeen = System.currentTimeMillis();
+    }
+
+    public DefaultFlowEntry(FlowRule rule) {
+        super(rule);
+        this.state = FlowEntryState.PENDING_ADD;
+        this.life = 0;
+        this.packets = 0;
+        this.bytes = 0;
+        this.lastSeen = System.currentTimeMillis();
+    }
+
+    @Override
+    public long life() {
+        return life;
+    }
+
+    @Override
+    public long packets() {
+        return packets;
+    }
+
+    @Override
+    public long bytes() {
+        return bytes;
+    }
+
+    @Override
+    public FlowEntryState state() {
+        return this.state;
+    }
+
+    @Override
+    public long lastSeen() {
+        return lastSeen;
+    }
+
+    @Override
+    public void setLastSeen() {
+        this.lastSeen = System.currentTimeMillis();
+    }
+
+    @Override
+    public void setState(FlowEntryState newState) {
+        this.state = newState;
+    }
+
+    @Override
+    public void setLife(long life) {
+        this.life = life;
+    }
+
+    @Override
+    public void setPackets(long packets) {
+        this.packets = packets;
+    }
+
+    @Override
+    public void setBytes(long bytes) {
+        this.bytes = bytes;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("rule", super.toString())
+                .add("state", state)
+                .toString();
+    }
+
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index 9b44fc2..9011a93 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -18,10 +18,6 @@
     private final TrafficSelector selector;
     private final TrafficTreatment treatment;
     private final long created;
-    private final long life;
-    private final long packets;
-    private final long bytes;
-    private final FlowRuleState state;
 
     private final FlowId id;
 
@@ -29,90 +25,50 @@
 
     private final int timeout;
 
-    /**
-     * Creates a flow rule given the following paremeters.
-     * @param deviceId the device where the rule should be installed
-     * @param selector the traffic selection
-     * @param treatment how the seleted traffic should be handled
-     * @param priority the rule priority cannot be less than FlowRule.MIN_PRIORITY
-     * @param state the state in which the rule is
-     * @param life how long it has existed for (ms)
-     * @param packets number of packets it has seen
-     * @param bytes number of bytes  it has seen
-     * @param flowId the identifier
-     * @param timeout the rule's timeout (idle) not to exceed
-     *  FlowRule.MAX_TIMEOUT of idle time
-     */
+
     public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
-            TrafficTreatment treatment, int priority, FlowRuleState state,
-            long life, long packets, long bytes, long flowId,
+            TrafficTreatment treatment, int priority, long flowId,
             int timeout) {
         this.deviceId = deviceId;
         this.priority = priority;
         this.selector = selector;
         this.treatment = treatment;
-        this.state = state;
+        this.timeout = timeout;
+        this.created = System.currentTimeMillis();
+
         this.appId = ApplicationId.valueOf((int) (flowId >> 32));
         this.id = FlowId.valueOf(flowId);
-        this.life = life;
-        this.packets = packets;
-        this.bytes = bytes;
-        this.created = System.currentTimeMillis();
-        this.timeout = timeout;
     }
 
     public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
             TrafficTreatment treatement, int priority, ApplicationId appId,
             int timeout) {
-        this(deviceId, selector, treatement, priority,
-                FlowRuleState.CREATED, appId, timeout);
-    }
 
-    public DefaultFlowRule(FlowRule rule, FlowRuleState state) {
-        this(rule.deviceId(), rule.selector(), rule.treatment(),
-                rule.priority(), state, rule.id(), rule.appId(),
-                rule.timeout());
-    }
-
-    private DefaultFlowRule(DeviceId deviceId,
-            TrafficSelector selector, TrafficTreatment treatment,
-            int priority, FlowRuleState state, ApplicationId appId,
-            int timeout) {
-        if (priority < MIN_PRIORITY) {
+        if (priority < FlowRule.MIN_PRIORITY) {
             throw new IllegalArgumentException("Priority cannot be less than " + MIN_PRIORITY);
         }
+
         this.deviceId = deviceId;
         this.priority = priority;
         this.selector = selector;
-        this.treatment = treatment;
-        this.state = state;
-        this.life = 0;
-        this.packets = 0;
-        this.bytes = 0;
+        this.treatment = treatement;
         this.appId = appId;
-
         this.timeout = timeout;
+        this.created = System.currentTimeMillis();
 
         this.id = FlowId.valueOf((((long) appId().id()) << 32) | (this.hash() & 0xffffffffL));
-        this.created = System.currentTimeMillis();
     }
 
-    private DefaultFlowRule(DeviceId deviceId,
-            TrafficSelector selector, TrafficTreatment treatment,
-            int priority, FlowRuleState state, FlowId flowId, ApplicationId appId,
-            int timeout) {
-        this.deviceId = deviceId;
-        this.priority = priority;
-        this.selector = selector;
-        this.treatment = treatment;
-        this.state = state;
-        this.life = 0;
-        this.packets = 0;
-        this.bytes = 0;
-        this.appId = appId;
-        this.id = flowId;
-        this.timeout = timeout;
+    public DefaultFlowRule(FlowRule rule) {
+        this.deviceId = rule.deviceId();
+        this.priority = rule.priority();
+        this.selector = rule.selector();
+        this.treatment = rule.treatment();
+        this.appId = rule.appId();
+        this.id = rule.id();
+        this.timeout = rule.timeout();
         this.created = System.currentTimeMillis();
+
     }
 
 
@@ -146,26 +102,6 @@
         return treatment;
     }
 
-    @Override
-    public long life() {
-        return life;
-    }
-
-    @Override
-    public long packets() {
-        return packets;
-    }
-
-    @Override
-    public long bytes() {
-        return bytes;
-    }
-
-    @Override
-    public FlowRuleState state() {
-        return this.state;
-    }
-
 
     @Override
     /*
@@ -213,7 +149,6 @@
                 .add("selector", selector.criteria())
                 .add("treatment", treatment == null ? "N/A" : treatment.instructions())
                 .add("created", created)
-                .add("state", state)
                 .toString();
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
new file mode 100644
index 0000000..5b5f89b
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowEntry.java
@@ -0,0 +1,98 @@
+package org.onlab.onos.net.flow;
+
+
+/**
+ * Represents a generalized match &amp; action pair to be applied to
+ * an infrastucture device.
+ */
+public interface FlowEntry extends FlowRule {
+
+
+    public enum FlowEntryState {
+
+        /**
+         * Indicates that this rule has been submitted for addition.
+         * Not necessarily in  the flow table.
+         */
+        PENDING_ADD,
+
+        /**
+         * Rule has been added which means it is in the flow table.
+         */
+        ADDED,
+
+        /**
+         * Flow has been marked for removal, might still be in flow table.
+         */
+        PENDING_REMOVE,
+
+        /**
+         * Flow has been removed from flow table and can be purged.
+         */
+        REMOVED
+    }
+
+    /**
+     * Returns the flow entry state.
+     *
+     * @return flow entry state
+     */
+    FlowEntryState state();
+
+    /**
+     * Returns the number of milliseconds this flow rule has been applied.
+     *
+     * @return number of millis
+     */
+    long life();
+
+    /**
+     * Returns the number of packets this flow rule has matched.
+     *
+     * @return number of packets
+     */
+    long packets();
+
+    /**
+     * Returns the number of bytes this flow rule has matched.
+     *
+     * @return number of bytes
+     */
+    long bytes();
+
+    /**
+     * When this flow entry was last deemed active.
+     * @return epoch time of last activity
+     */
+    long lastSeen();
+
+    /**
+     * Sets the last active epoch time.
+     */
+    void setLastSeen();
+
+    /**
+     * Sets the new state for this entry.
+     * @param newState new flow entry state.
+     */
+    void setState(FlowEntryState newState);
+
+    /**
+     * Sets how long this entry has been entered in the system.
+     * @param life epoch time
+     */
+    void setLife(long life);
+
+    /**
+     * Number of packets seen by this entry.
+     * @param packets a long value
+     */
+    void setPackets(long packets);
+
+    /**
+     * Number of bytes seen by this rule.
+     * @param bytes a long value
+     */
+    void setBytes(long bytes);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
index 9b76f32..8b30c74 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
@@ -12,41 +12,6 @@
     static final int MAX_TIMEOUT = 60;
     static final int MIN_PRIORITY = 0;
 
-    public enum FlowRuleState {
-        /**
-         * Indicates that this rule has been created.
-         */
-        CREATED,
-
-        /**
-         * Indicates that this rule has been submitted for addition.
-         * Not necessarily in  the flow table.
-         */
-        PENDING_ADD,
-
-        /**
-         * Rule has been added which means it is in the flow table.
-         */
-        ADDED,
-
-        /**
-         * Flow has been marked for removal, might still be in flow table.
-         */
-        PENDING_REMOVE,
-
-        /**
-         * Flow has been removed from flow table and can be purged.
-         */
-        REMOVED
-    }
-
-    /**
-     * Returns the flow rule state.
-     *
-     * @return flow rule state
-     */
-    FlowRuleState state();
-
     //TODO: build cookie value
     /**
      * Returns the ID of this flow.
@@ -93,27 +58,6 @@
     TrafficTreatment treatment();
 
     /**
-     * Returns the number of milliseconds this flow rule has been applied.
-     *
-     * @return number of millis
-     */
-    long life();
-
-    /**
-     * Returns the number of packets this flow rule has matched.
-     *
-     * @return number of packets
-     */
-    long packets();
-
-    /**
-     * Returns the number of bytes this flow rule has matched.
-     *
-     * @return number of bytes
-     */
-    long bytes();
-
-    /**
      * Returns the timeout for this flow requested by an application.
      * @return integer value of the timeout
      */
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
index 2076103..8164579 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
@@ -14,7 +14,7 @@
      *
      * @param flowRule information about the removed flow
      */
-    void flowRemoved(FlowRule flowRule);
+    void flowRemoved(FlowEntry flowEntry);
 
     /**
      * Pushes the collection of flow entries currently applied on the given
@@ -22,7 +22,7 @@
      *
      * @param flowRules collection of flow rules
      */
-    void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowRules);
+    void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
 
 
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
index c09a56d..2ebc5a25 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleService.java
@@ -20,7 +20,7 @@
      * @param deviceId device identifier
      * @return collection of flow rules
      */
-    Iterable<FlowRule> getFlowEntries(DeviceId deviceId);
+    Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
 
     // TODO: add createFlowRule factory method and execute operations method
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java
index c6093384..4d68e74 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleStore.java
@@ -14,7 +14,7 @@
      * @param rule the rule to look for
      * @return a flow rule
      */
-    FlowRule getFlowRule(FlowRule rule);
+    FlowEntry getFlowEntry(FlowRule rule);
 
     /**
      * Returns the flow entries associated with a device.
@@ -22,7 +22,7 @@
      * @param deviceId the device ID
      * @return the flow entries
      */
-    Iterable<FlowRule> getFlowEntries(DeviceId deviceId);
+    Iterable<FlowEntry> getFlowEntries(DeviceId deviceId);
 
     /**
      * Returns the flow entries associated with an application.
@@ -30,7 +30,7 @@
      * @param appId the application id
      * @return the flow entries
      */
-    Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId);
+    Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId);
 
     /**
      * Stores a new flow rule without generating events.
@@ -40,7 +40,8 @@
     void storeFlowRule(FlowRule rule);
 
     /**
-     * Deletes a flow rule without generating events.
+     * Marks a flow rule for deletion. Actual deletion will occur
+     * when the provider indicates that the flow has been removed.
      *
      * @param rule the flow rule to delete
      */
@@ -52,12 +53,12 @@
      * @param rule the flow rule to add or update
      * @return flow_added event, or null if just an update
      */
-    FlowRuleEvent addOrUpdateFlowRule(FlowRule rule);
+    FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule);
 
     /**
-     * @param rule the flow rule to remove
+     * @param rule the flow entry to remove
      * @return flow_removed event, or null if nothing removed
      */
-    FlowRuleEvent removeFlowRule(FlowRule rule);
+    FlowRuleEvent removeFlowRule(FlowEntry rule);
 
 }
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 182e210..385e300 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -5,8 +5,6 @@
 
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -20,6 +18,7 @@
 import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.FlowEntry;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleListener;
@@ -61,8 +60,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
-    private final Map<FlowRule, Long> idleTime = new ConcurrentHashMap<>();
-
     @Activate
     public void activate() {
         store.setDelegate(delegate);
@@ -78,7 +75,7 @@
     }
 
     @Override
-    public Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
+    public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
         return store.getFlowEntries(deviceId);
     }
 
@@ -88,7 +85,6 @@
             FlowRule f = flowRules[i];
             final Device device = deviceService.getDevice(f.deviceId());
             final FlowRuleProvider frp = getProvider(device.providerId());
-            idleTime.put(f, System.currentTimeMillis());
             store.storeFlowRule(f);
             frp.applyFlowRule(f);
         }
@@ -103,7 +99,6 @@
             f = flowRules[i];
             device = deviceService.getDevice(f.deviceId());
             frp = getProvider(device.providerId());
-            idleTime.remove(f);
             store.deleteFlowRule(f);
             frp.removeFlowRule(f);
         }
@@ -125,7 +120,7 @@
 
     @Override
     public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
-        return store.getFlowEntriesByAppId(id);
+        return store.getFlowRulesByAppId(id);
     }
 
     @Override
@@ -153,15 +148,15 @@
         }
 
         @Override
-        public void flowRemoved(FlowRule flowRule) {
-            checkNotNull(flowRule, FLOW_RULE_NULL);
+        public void flowRemoved(FlowEntry flowEntry) {
+            checkNotNull(flowEntry, FLOW_RULE_NULL);
             checkValidity();
-            FlowRule stored = store.getFlowRule(flowRule);
+            FlowEntry stored = store.getFlowEntry(flowEntry);
             if (stored == null) {
-                log.info("Rule already evicted from store: {}", flowRule);
+                log.info("Rule already evicted from store: {}", flowEntry);
                 return;
             }
-            Device device = deviceService.getDevice(flowRule.deviceId());
+            Device device = deviceService.getDevice(flowEntry.deviceId());
             FlowRuleProvider frp = getProvider(device.providerId());
             FlowRuleEvent event = null;
             switch (stored.state()) {
@@ -171,20 +166,20 @@
                 break;
             case PENDING_REMOVE:
             case REMOVED:
-                event = store.removeFlowRule(flowRule);
+                event = store.removeFlowRule(stored);
                 break;
             default:
                 break;
 
             }
             if (event != null) {
-                log.debug("Flow {} removed", flowRule);
+                log.debug("Flow {} removed", flowEntry);
                 post(event);
             }
         }
 
 
-        private void flowMissing(FlowRule flowRule) {
+        private void flowMissing(FlowEntry flowRule) {
             checkNotNull(flowRule, FLOW_RULE_NULL);
             checkValidity();
             Device device = deviceService.getDevice(flowRule.deviceId());
@@ -220,36 +215,37 @@
         }
 
 
-        private void flowAdded(FlowRule flowRule) {
-            checkNotNull(flowRule, FLOW_RULE_NULL);
+        private void flowAdded(FlowEntry flowEntry) {
+            checkNotNull(flowEntry, FLOW_RULE_NULL);
             checkValidity();
 
-            if (idleTime.containsKey(flowRule) &&
-                    checkRuleLiveness(flowRule, store.getFlowRule(flowRule))) {
+            if (checkRuleLiveness(flowEntry, store.getFlowEntry(flowEntry))) {
 
-                FlowRuleEvent event = store.addOrUpdateFlowRule(flowRule);
+                FlowRuleEvent event = store.addOrUpdateFlowRule(flowEntry);
                 if (event == null) {
                     log.debug("No flow store event generated.");
                 } else {
-                    log.debug("Flow {} {}", flowRule, event.type());
+                    log.debug("Flow {} {}", flowEntry, event.type());
                     post(event);
                 }
             } else {
-                removeFlowRules(flowRule);
+                removeFlowRules(flowEntry);
             }
 
         }
 
-        private boolean checkRuleLiveness(FlowRule swRule, FlowRule storedRule) {
+        private boolean checkRuleLiveness(FlowEntry swRule, FlowEntry storedRule) {
+            if (storedRule == null) {
+                return false;
+            }
             long timeout = storedRule.timeout() * 1000;
             Long currentTime = System.currentTimeMillis();
             if (storedRule.packets() != swRule.packets()) {
-                idleTime.put(swRule, currentTime);
+                storedRule.setLastSeen();
                 return true;
             }
 
-            if ((currentTime - idleTime.get(swRule)) <= timeout) {
-                idleTime.put(swRule, currentTime);
+            if ((currentTime - storedRule.lastSeen()) <= timeout) {
                 return true;
             }
             return false;
@@ -263,13 +259,13 @@
         }
 
         @Override
-        public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowEntries) {
-            List<FlowRule> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
+        public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+            List<FlowEntry> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
 
-            Iterator<FlowRule> switchRulesIterator = flowEntries.iterator();
+            Iterator<FlowEntry> switchRulesIterator = flowEntries.iterator();
 
             while (switchRulesIterator.hasNext()) {
-                FlowRule rule = switchRulesIterator.next();
+                FlowEntry rule = switchRulesIterator.next();
                 if (storedRules.remove(rule)) {
                     // we both have the rule, let's update some info then.
                     flowAdded(rule);
@@ -278,7 +274,7 @@
                     extraneousFlow(rule);
                 }
             }
-            for (FlowRule rule : storedRules) {
+            for (FlowEntry rule : storedRules) {
                 // there are rules in the store that aren't on the switch
                 flowMissing(rule);
 
diff --git a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
index 8922fd9..7463671 100644
--- a/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/flow/impl/FlowRuleManagerTest.java
@@ -27,9 +27,11 @@
 import org.onlab.onos.net.PortNumber;
 import org.onlab.onos.net.device.DeviceListener;
 import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
 import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleListener;
 import org.onlab.onos.net.flow.FlowRuleProvider;
@@ -103,9 +105,6 @@
         return new DefaultFlowRule(DID, ts, tr, 10, appId, TIMEOUT);
     }
 
-    private FlowRule flowRule(FlowRule rule, FlowRuleState state) {
-        return new DefaultFlowRule(rule, state);
-    }
 
     private FlowRule addFlowRule(int hval) {
         FlowRule rule = flowRule(hval, hval);
@@ -143,24 +142,26 @@
         FlowRule f1 = addFlowRule(1);
         FlowRule f2 = addFlowRule(2);
 
+        FlowEntry fe1 = new DefaultFlowEntry(f1);
+        FlowEntry fe2 = new DefaultFlowEntry(f2);
         assertEquals("2 rules should exist", 2, flowCount());
 
-        providerService.pushFlowMetrics(DID, ImmutableList.of(f1, f2));
+        providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
         validateEvents(RULE_ADDED, RULE_ADDED);
 
         addFlowRule(1);
         assertEquals("should still be 2 rules", 2, flowCount());
 
-        providerService.pushFlowMetrics(DID, ImmutableList.of(f1));
+        providerService.pushFlowMetrics(DID, ImmutableList.of(fe1));
         validateEvents(RULE_UPDATED);
     }
 
 
     //backing store is sensitive to the order of additions/removals
-    private boolean validateState(FlowRuleState... state) {
-        Iterable<FlowRule> rules = service.getFlowEntries(DID);
+    private boolean validateState(FlowEntryState... state) {
+        Iterable<FlowEntry> rules = service.getFlowEntries(DID);
         int i = 0;
-        for (FlowRule f : rules) {
+        for (FlowEntry f : rules) {
             if (f.state() != state[i]) {
                 return false;
             }
@@ -181,8 +182,8 @@
         mgr.applyFlowRules(r1, r2, r3);
         assertEquals("3 rules should exist", 3, flowCount());
         assertTrue("Entries should be pending add.",
-                validateState(FlowRuleState.PENDING_ADD, FlowRuleState.PENDING_ADD,
-                        FlowRuleState.PENDING_ADD));
+                validateState(FlowEntryState.PENDING_ADD, FlowEntryState.PENDING_ADD,
+                        FlowEntryState.PENDING_ADD));
     }
 
     @Test
@@ -192,20 +193,21 @@
         FlowRule f3 = addFlowRule(3);
         assertEquals("3 rules should exist", 3, flowCount());
 
-        providerService.pushFlowMetrics(DID, ImmutableList.of(f1, f2, f3));
+        FlowEntry fe1 = new DefaultFlowEntry(f1);
+        FlowEntry fe2 = new DefaultFlowEntry(f2);
+        FlowEntry fe3 = new DefaultFlowEntry(f3);
+        providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3));
         validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED);
 
-        FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED);
-        FlowRule rem2 = flowRule(f2, FlowRuleState.REMOVED);
-        mgr.removeFlowRules(rem1, rem2);
+        mgr.removeFlowRules(f1, f2);
         //removing from north, so no events generated
         validateEvents();
         assertEquals("3 rule should exist", 3, flowCount());
         assertTrue("Entries should be pending remove.",
-                validateState(FlowRuleState.CREATED, FlowRuleState.PENDING_REMOVE,
-                        FlowRuleState.PENDING_REMOVE));
+                validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE,
+                        FlowEntryState.ADDED));
 
-        mgr.removeFlowRules(rem1);
+        mgr.removeFlowRules(f1);
         assertEquals("3 rule should still exist", 3, flowCount());
     }
 
@@ -213,21 +215,24 @@
     public void flowRemoved() {
         FlowRule f1 = addFlowRule(1);
         FlowRule f2 = addFlowRule(2);
-        providerService.pushFlowMetrics(f1.deviceId(), ImmutableList.of(f1, f2));
+        FlowEntry fe1 = new DefaultFlowEntry(f1);
+        FlowEntry fe2 = new DefaultFlowEntry(f2);
+        providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
         service.removeFlowRules(f1);
-        FlowRule rem1 = flowRule(f1, FlowRuleState.REMOVED);
-        providerService.flowRemoved(rem1);
+        fe1.setState(FlowEntryState.REMOVED);
+        providerService.flowRemoved(fe1);
         validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
 
-        providerService.flowRemoved(rem1);
+        providerService.flowRemoved(fe1);
         validateEvents();
 
         FlowRule f3 = flowRule(3, 3);
+        FlowEntry fe3 = new DefaultFlowEntry(f3);
         service.applyFlowRules(f3);
-        providerService.pushFlowMetrics(f3.deviceId(), Collections.singletonList(f3));
+        providerService.pushFlowMetrics(DID, Collections.singletonList(fe3));
         validateEvents(RULE_ADDED);
 
-        providerService.flowRemoved(f3);
+        providerService.flowRemoved(fe3);
         validateEvents();
     }
 
@@ -237,17 +242,20 @@
         FlowRule f2 = flowRule(2, 2);
         FlowRule f3 = flowRule(3, 3);
 
-
-
         mgr.applyFlowRules(f1, f2, f3);
-        FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
-        FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
 
-        providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2));
+        FlowEntry fe1 = new DefaultFlowEntry(f1);
+        FlowEntry fe2 = new DefaultFlowEntry(f2);
+
+
+        //FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
+        //FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+
+        providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
 
         assertTrue("Entries should be added.",
-                validateState(FlowRuleState.PENDING_ADD, FlowRuleState.ADDED,
-                        FlowRuleState.ADDED));
+                validateState(FlowEntryState.ADDED, FlowEntryState.ADDED,
+                        FlowEntryState.PENDING_ADD));
 
         validateEvents(RULE_ADDED, RULE_ADDED);
     }
@@ -259,11 +267,15 @@
         FlowRule f3 = flowRule(3, 3);
         mgr.applyFlowRules(f1, f2);
 
-        FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
-        FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
-        FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED);
+//        FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
+//        FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+//        FlowRule updatedF3 = flowRule(f3, FlowRuleState.ADDED);
+        FlowEntry fe1 = new DefaultFlowEntry(f1);
+        FlowEntry fe2 = new DefaultFlowEntry(f2);
+        FlowEntry fe3 = new DefaultFlowEntry(f3);
 
-        providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2, updatedF3));
+
+        providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3));
 
         validateEvents(RULE_ADDED, RULE_ADDED);
 
@@ -279,13 +291,16 @@
         FlowRule f2 = flowRule(2, 2);
         FlowRule f3 = flowRule(3, 3);
 
-        FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
-        FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+//        FlowRule updatedF1 = flowRule(f1, FlowRuleState.ADDED);
+//        FlowRule updatedF2 = flowRule(f2, FlowRuleState.ADDED);
+
+        FlowEntry fe1 = new DefaultFlowEntry(f1);
+        FlowEntry fe2 = new DefaultFlowEntry(f2);
         mgr.applyFlowRules(f1, f2, f3);
 
         mgr.removeFlowRules(f3);
 
-        providerService.pushFlowMetrics(DID, Lists.newArrayList(updatedF1, updatedF2));
+        providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
 
         validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
 
@@ -312,7 +327,7 @@
 
         //only check that we are in pending remove. Events and actual remove state will
         // be set by flowRemoved call.
-        validateState(FlowRuleState.PENDING_REMOVE, FlowRuleState.PENDING_REMOVE);
+        validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE);
     }
 
     private static class TestListener implements FlowRuleListener {
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index 5a5592a..f3c8f34 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -1,6 +1,5 @@
 package org.onlab.onos.store.flow.impl;
 
-import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -13,9 +12,10 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.ApplicationId;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
 import org.onlab.onos.net.flow.FlowRuleStore;
@@ -30,18 +30,18 @@
 /**
  * Manages inventory of flow rules using trivial in-memory implementation.
  */
-//FIXME: I LIE I AM NOT DISTRIBUTED
+//FIXME I LIE. I AIN'T DISTRIBUTED
 @Component(immediate = true)
 @Service
 public class DistributedFlowRuleStore
-extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
-implements FlowRuleStore {
+        extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
+        implements FlowRuleStore {
 
     private final Logger log = getLogger(getClass());
 
     // store entries as a pile of rules, no info about device tables
-    private final Multimap<DeviceId, FlowRule> flowEntries =
-            ArrayListMultimap.<DeviceId, FlowRule>create();
+    private final Multimap<DeviceId, FlowEntry> flowEntries =
+            ArrayListMultimap.<DeviceId, FlowEntry>create();
 
     private final Multimap<ApplicationId, FlowRule> flowEntriesById =
             ArrayListMultimap.<ApplicationId, FlowRule>create();
@@ -58,8 +58,8 @@
 
 
     @Override
-    public synchronized FlowRule getFlowRule(FlowRule rule) {
-        for (FlowRule f : flowEntries.get(rule.deviceId())) {
+    public synchronized FlowEntry getFlowEntry(FlowRule rule) {
+        for (FlowEntry f : flowEntries.get(rule.deviceId())) {
             if (f.equals(rule)) {
                 return f;
             }
@@ -68,8 +68,8 @@
     }
 
     @Override
-    public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
-        Collection<FlowRule> rules = flowEntries.get(deviceId);
+    public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+        Collection<FlowEntry> rules = flowEntries.get(deviceId);
         if (rules == null) {
             return Collections.emptyList();
         }
@@ -77,7 +77,7 @@
     }
 
     @Override
-    public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) {
+    public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
         Collection<FlowRule> rules = flowEntriesById.get(appId);
         if (rules == null) {
             return Collections.emptyList();
@@ -87,7 +87,7 @@
 
     @Override
     public synchronized void storeFlowRule(FlowRule rule) {
-        FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD);
+        FlowEntry f = new DefaultFlowEntry(rule);
         DeviceId did = f.deviceId();
         if (!flowEntries.containsEntry(did, f)) {
             flowEntries.put(did, f);
@@ -97,57 +97,41 @@
 
     @Override
     public synchronized void deleteFlowRule(FlowRule rule) {
-        FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE);
-        DeviceId did = f.deviceId();
-
-        /*
-         *  find the rule and mark it for deletion.
-         *  Ultimately a flow removed will come remove it.
-         */
-
-        if (flowEntries.containsEntry(did, f)) {
-            //synchronized (flowEntries) {
-            flowEntries.remove(did, f);
-            flowEntries.put(did, f);
-            flowEntriesById.remove(rule.appId(), rule);
-            //}
+        FlowEntry entry = getFlowEntry(rule);
+        if (entry == null) {
+            return;
         }
+        entry.setState(FlowEntryState.PENDING_REMOVE);
     }
 
     @Override
-    public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+    public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
         DeviceId did = rule.deviceId();
 
         // check if this new rule is an update to an existing entry
-        if (flowEntries.containsEntry(did, rule)) {
-            //synchronized (flowEntries) {
-            // Multimaps support duplicates so we have to remove our rule
-            // and replace it with the current version.
-            flowEntries.remove(did, rule);
-            flowEntries.put(did, rule);
-            //}
+        FlowEntry stored = getFlowEntry(rule);
+        if (stored != null) {
+            stored.setBytes(rule.bytes());
+            stored.setLife(rule.life());
+            stored.setPackets(rule.packets());
+            if (stored.state() == FlowEntryState.PENDING_ADD) {
+                stored.setState(FlowEntryState.ADDED);
+                return new FlowRuleEvent(Type.RULE_ADDED, rule);
+            }
             return new FlowRuleEvent(Type.RULE_UPDATED, rule);
         }
 
         flowEntries.put(did, rule);
-        return new FlowRuleEvent(RULE_ADDED, rule);
+        return null;
     }
 
     @Override
-    public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) {
-        //synchronized (this) {
+    public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
+        // This is where one could mark a rule as removed and still keep it in the store.
         if (flowEntries.remove(rule.deviceId(), rule)) {
             return new FlowRuleEvent(RULE_REMOVED, rule);
         } else {
             return null;
         }
-        //}
     }
-
-
-
-
-
-
-
 }
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index 6ec7c51..f3c8f34 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -1,6 +1,5 @@
 package org.onlab.onos.store.flow.impl;
 
-import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -13,9 +12,10 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.ApplicationId;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
 import org.onlab.onos.net.flow.FlowRuleStore;
@@ -28,20 +28,20 @@
 import com.google.common.collect.Multimap;
 
 /**
- * TEMPORARY: Manages inventory of flow rules using distributed store implementation.
+ * Manages inventory of flow rules using trivial in-memory implementation.
  */
-//FIXME: I LIE I AM NOT DISTRIBUTED
+//FIXME I LIE. I AIN'T DISTRIBUTED
 @Component(immediate = true)
 @Service
 public class DistributedFlowRuleStore
-extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
-implements FlowRuleStore {
+        extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
+        implements FlowRuleStore {
 
     private final Logger log = getLogger(getClass());
 
     // store entries as a pile of rules, no info about device tables
-    private final Multimap<DeviceId, FlowRule> flowEntries =
-            ArrayListMultimap.<DeviceId, FlowRule>create();
+    private final Multimap<DeviceId, FlowEntry> flowEntries =
+            ArrayListMultimap.<DeviceId, FlowEntry>create();
 
     private final Multimap<ApplicationId, FlowRule> flowEntriesById =
             ArrayListMultimap.<ApplicationId, FlowRule>create();
@@ -58,8 +58,8 @@
 
 
     @Override
-    public synchronized FlowRule getFlowRule(FlowRule rule) {
-        for (FlowRule f : flowEntries.get(rule.deviceId())) {
+    public synchronized FlowEntry getFlowEntry(FlowRule rule) {
+        for (FlowEntry f : flowEntries.get(rule.deviceId())) {
             if (f.equals(rule)) {
                 return f;
             }
@@ -68,8 +68,8 @@
     }
 
     @Override
-    public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
-        Collection<FlowRule> rules = flowEntries.get(deviceId);
+    public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+        Collection<FlowEntry> rules = flowEntries.get(deviceId);
         if (rules == null) {
             return Collections.emptyList();
         }
@@ -77,7 +77,7 @@
     }
 
     @Override
-    public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) {
+    public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
         Collection<FlowRule> rules = flowEntriesById.get(appId);
         if (rules == null) {
             return Collections.emptyList();
@@ -87,7 +87,7 @@
 
     @Override
     public synchronized void storeFlowRule(FlowRule rule) {
-        FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD);
+        FlowEntry f = new DefaultFlowEntry(rule);
         DeviceId did = f.deviceId();
         if (!flowEntries.containsEntry(did, f)) {
             flowEntries.put(did, f);
@@ -97,57 +97,41 @@
 
     @Override
     public synchronized void deleteFlowRule(FlowRule rule) {
-        FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE);
-        DeviceId did = f.deviceId();
-
-        /*
-         *  find the rule and mark it for deletion.
-         *  Ultimately a flow removed will come remove it.
-         */
-
-        if (flowEntries.containsEntry(did, f)) {
-            //synchronized (flowEntries) {
-            flowEntries.remove(did, f);
-            flowEntries.put(did, f);
-            flowEntriesById.remove(rule.appId(), rule);
-            //}
+        FlowEntry entry = getFlowEntry(rule);
+        if (entry == null) {
+            return;
         }
+        entry.setState(FlowEntryState.PENDING_REMOVE);
     }
 
     @Override
-    public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+    public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
         DeviceId did = rule.deviceId();
 
         // check if this new rule is an update to an existing entry
-        if (flowEntries.containsEntry(did, rule)) {
-            //synchronized (flowEntries) {
-            // Multimaps support duplicates so we have to remove our rule
-            // and replace it with the current version.
-            flowEntries.remove(did, rule);
-            flowEntries.put(did, rule);
-            //}
+        FlowEntry stored = getFlowEntry(rule);
+        if (stored != null) {
+            stored.setBytes(rule.bytes());
+            stored.setLife(rule.life());
+            stored.setPackets(rule.packets());
+            if (stored.state() == FlowEntryState.PENDING_ADD) {
+                stored.setState(FlowEntryState.ADDED);
+                return new FlowRuleEvent(Type.RULE_ADDED, rule);
+            }
             return new FlowRuleEvent(Type.RULE_UPDATED, rule);
         }
 
         flowEntries.put(did, rule);
-        return new FlowRuleEvent(RULE_ADDED, rule);
+        return null;
     }
 
     @Override
-    public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) {
-        //synchronized (this) {
+    public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
+        // This is where one could mark a rule as removed and still keep it in the store.
         if (flowEntries.remove(rule.deviceId(), rule)) {
             return new FlowRuleEvent(RULE_REMOVED, rule);
         } else {
             return null;
         }
-        //}
     }
-
-
-
-
-
-
-
 }
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
index d12d00e..5fa92f3 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
@@ -12,9 +12,10 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.ApplicationId;
 import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
 import org.onlab.onos.net.flow.FlowRuleStore;
@@ -38,8 +39,8 @@
     private final Logger log = getLogger(getClass());
 
     // store entries as a pile of rules, no info about device tables
-    private final Multimap<DeviceId, FlowRule> flowEntries =
-            ArrayListMultimap.<DeviceId, FlowRule>create();
+    private final Multimap<DeviceId, FlowEntry> flowEntries =
+            ArrayListMultimap.<DeviceId, FlowEntry>create();
 
     private final Multimap<ApplicationId, FlowRule> flowEntriesById =
             ArrayListMultimap.<ApplicationId, FlowRule>create();
@@ -56,8 +57,8 @@
 
 
     @Override
-    public synchronized FlowRule getFlowRule(FlowRule rule) {
-        for (FlowRule f : flowEntries.get(rule.deviceId())) {
+    public synchronized FlowEntry getFlowEntry(FlowRule rule) {
+        for (FlowEntry f : flowEntries.get(rule.deviceId())) {
             if (f.equals(rule)) {
                 return f;
             }
@@ -66,8 +67,8 @@
     }
 
     @Override
-    public synchronized Iterable<FlowRule> getFlowEntries(DeviceId deviceId) {
-        Collection<FlowRule> rules = flowEntries.get(deviceId);
+    public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+        Collection<FlowEntry> rules = flowEntries.get(deviceId);
         if (rules == null) {
             return Collections.emptyList();
         }
@@ -75,7 +76,7 @@
     }
 
     @Override
-    public synchronized Iterable<FlowRule> getFlowEntriesByAppId(ApplicationId appId) {
+    public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
         Collection<FlowRule> rules = flowEntriesById.get(appId);
         if (rules == null) {
             return Collections.emptyList();
@@ -85,7 +86,7 @@
 
     @Override
     public synchronized void storeFlowRule(FlowRule rule) {
-        FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_ADD);
+        FlowEntry f = new DefaultFlowEntry(rule);
         DeviceId did = f.deviceId();
         if (!flowEntries.containsEntry(did, f)) {
             flowEntries.put(did, f);
@@ -95,34 +96,25 @@
 
     @Override
     public synchronized void deleteFlowRule(FlowRule rule) {
-        FlowRule f = new DefaultFlowRule(rule, FlowRuleState.PENDING_REMOVE);
-        DeviceId did = f.deviceId();
-
-        /*
-         *  find the rule and mark it for deletion.
-         *  Ultimately a flow removed will come remove it.
-         */
-
-        if (flowEntries.containsEntry(did, f)) {
-            flowEntries.remove(did, f);
-            flowEntries.put(did, f);
-            flowEntriesById.remove(rule.appId(), rule);
+        FlowEntry entry = getFlowEntry(rule);
+        if (entry == null) {
+            return;
         }
+        entry.setState(FlowEntryState.PENDING_REMOVE);
     }
 
     @Override
-    public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowRule rule) {
+    public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
         DeviceId did = rule.deviceId();
 
         // check if this new rule is an update to an existing entry
-        FlowRule stored = getFlowRule(rule);
+        FlowEntry stored = getFlowEntry(rule);
         if (stored != null) {
-            // Multimaps support duplicates so we have to remove our rule
-            // and replace it with the current version.
-            flowEntries.remove(did, rule);
-            flowEntries.put(did, rule);
-
-            if (stored.state() == FlowRuleState.PENDING_ADD) {
+            stored.setBytes(rule.bytes());
+            stored.setLife(rule.life());
+            stored.setPackets(rule.packets());
+            if (stored.state() == FlowEntryState.PENDING_ADD) {
+                stored.setState(FlowEntryState.ADDED);
                 return new FlowRuleEvent(Type.RULE_ADDED, rule);
             }
             return new FlowRuleEvent(Type.RULE_UPDATED, rule);
@@ -133,13 +125,12 @@
     }
 
     @Override
-    public synchronized FlowRuleEvent removeFlowRule(FlowRule rule) {
-        //synchronized (this) {
+    public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
+        // This is where one could mark a rule as removed and still keep it in the store.
         if (flowEntries.remove(rule.deviceId(), rule)) {
             return new FlowRuleEvent(RULE_REMOVED, rule);
         } else {
             return null;
         }
-        //}
     }
 }
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
similarity index 91%
rename from providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
rename to providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
index 028e857..14c2c22 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowEntryBuilder.java
@@ -6,11 +6,13 @@
 
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.DefaultFlowEntry;
 import org.onlab.onos.net.flow.DefaultFlowRule;
 import org.onlab.onos.net.flow.DefaultTrafficSelector;
 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.FlowEntry;
+import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
 import org.onlab.onos.net.flow.FlowRule;
-import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 import org.onlab.onos.openflow.controller.Dpid;
@@ -37,7 +39,7 @@
 
 import com.google.common.collect.Lists;
 
-public class FlowRuleBuilder {
+public class FlowEntryBuilder {
     private final Logger log = getLogger(getClass());
 
     private final OFFlowStatsEntry stat;
@@ -51,7 +53,7 @@
     private final boolean addedRule;
 
 
-    public FlowRuleBuilder(Dpid dpid, OFFlowStatsEntry entry) {
+    public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) {
         this.stat = entry;
         this.match = entry.getMatch();
         this.actions = getActions(entry);
@@ -60,7 +62,7 @@
         this.addedRule = true;
     }
 
-    public FlowRuleBuilder(Dpid dpid, OFFlowRemoved removed) {
+    public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed) {
         this.match = removed.getMatch();
         this.removed = removed;
 
@@ -71,20 +73,21 @@
 
     }
 
-    public FlowRule build() {
+    public FlowEntry build() {
         if (addedRule) {
-            return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
+            FlowRule rule = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
                     buildSelector(), buildTreatment(), stat.getPriority(),
-                    FlowRuleState.ADDED, stat.getDurationSec(),
-                    stat.getPacketCount().getValue(), stat.getByteCount().getValue(),
                     stat.getCookie().getValue(), stat.getIdleTimeout());
+            return new DefaultFlowEntry(rule, FlowEntryState.ADDED,
+                    stat.getDurationSec(), stat.getPacketCount().getValue(),
+                    stat.getByteCount().getValue());
+
         } else {
-            // TODO: revisit potentially.
-            return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
+            FlowRule rule = new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
                     buildSelector(), null, removed.getPriority(),
-                    FlowRuleState.REMOVED, removed.getDurationSec(),
-                    removed.getPacketCount().getValue(), removed.getByteCount().getValue(),
-                    removed.getCookie().getValue(), removed.getIdleTimeout());
+                   removed.getCookie().getValue(), removed.getIdleTimeout());
+            return new DefaultFlowEntry(rule, FlowEntryState.REMOVED, removed.getDurationSec(),
+                    removed.getPacketCount().getValue(), removed.getByteCount().getValue());
         }
     }
 
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
index c4507b4..eac3c18 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -12,6 +12,7 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.onos.ApplicationId;
 import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.flow.FlowEntry;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleProvider;
 import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
@@ -131,7 +132,7 @@
     implements OpenFlowSwitchListener, OpenFlowEventListener {
 
         private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
-        private final Multimap<DeviceId, FlowRule> completeEntries =
+        private final Multimap<DeviceId, FlowEntry> completeEntries =
                 ArrayListMultimap.create();
 
         @Override
@@ -158,7 +159,7 @@
                 //TODO: make this better
                 OFFlowRemoved removed = (OFFlowRemoved) msg;
 
-                FlowRule fr = new FlowRuleBuilder(dpid, removed).build();
+                FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
                 providerService.flowRemoved(fr);
                 break;
             case STATS_REPLY:
@@ -188,7 +189,7 @@
 
             for (OFFlowStatsEntry reply : replies.getEntries()) {
                 if (!tableMissRule(dpid, reply)) {
-                    completeEntries.put(did, new FlowRuleBuilder(dpid, reply).build());
+                    completeEntries.put(did, new FlowEntryBuilder(dpid, reply).build());
                 }
             }