public boolean remove(DeviceId deviceId, FlowEntry rule) {
   try {
     return getFlowEntriesInternal(deviceId, rule.id()).remove(rule);
   } finally {
     lastUpdateTimes.put(deviceId, System.currentTimeMillis());
   }
 }
  /**
   * add or update typed flow entry from flow entry into the internal flow table.
   *
   * @param flowEntries the flow entries
   */
  public synchronized void addOrUpdateFlows(FlowEntry... flowEntries) {
    for (FlowEntry fe : flowEntries) {
      // check if this new rule is an update to an existing entry
      TypedStoredFlowEntry stored = deviceFlowTable.getFlowEntry(fe);

      if (stored != null) {
        // duplicated flow entry is collected!, just skip
        if (fe.bytes() == stored.bytes()
            && fe.packets() == stored.packets()
            && fe.life() == stored.life()) {
          log.debug(
              "addOrUpdateFlows:, FlowId="
                  + Long.toHexString(fe.id().value())
                  + ",is DUPLICATED stats collection, just skip."
                  + " AdaptiveStats collection thread for {}",
              sw.getStringId());

          // FIXME modification of "stored" flow entry outside of store
          stored.setLastSeen();
          continue;
        } else if (fe.life() < stored.life()) {
          // Invalid updates the stats values, i.e., bytes, packets, durations ...
          log.debug(
              "addOrUpdateFlows():"
                  + " Invalid Flow Update! The new life is SMALLER than the previous one, jus skip."
                  + " new flowId="
                  + Long.toHexString(fe.id().value())
                  + ", old flowId="
                  + Long.toHexString(stored.id().value())
                  + ", new bytes="
                  + fe.bytes()
                  + ", old bytes="
                  + stored.bytes()
                  + ", new life="
                  + fe.life()
                  + ", old life="
                  + stored.life()
                  + ", new lastSeen="
                  + fe.lastSeen()
                  + ", old lastSeen="
                  + stored.lastSeen());
          // go next
          // FIXME modification of "stored" flow entry outside of store
          stored.setLastSeen();
          continue;
        }

        // update now
        // FIXME modification of "stored" flow entry outside of store
        stored.setLife(fe.life());
        stored.setPackets(fe.packets());
        stored.setBytes(fe.bytes());
        stored.setLastSeen();
        if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {
          // flow is really RULE_ADDED
          stored.setState(FlowEntry.FlowEntryState.ADDED);
        }
        // flow is RULE_UPDATED, skip adding and just updating flow live table
        // deviceFlowTable.calAndSetFlowLiveType(stored);
        continue;
      }

      // add new flow entry, we suppose IMMEDIATE_FLOW
      TypedStoredFlowEntry newFlowEntry =
          new DefaultTypedFlowEntry(fe, FlowLiveType.IMMEDIATE_FLOW);
      deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
    }
  }
  @Override
  public synchronized void updateFlowStatistic(FlowEntry rule) {
    ConnectPoint cp = buildConnectPoint(rule);
    if (cp == null) {
      return;
    }

    Set<FlowEntry> curr = current.get(cp);
    if (curr == null) {
      addFlowStatistic(rule);
    } else {
      Optional<FlowEntry> f = curr.stream().filter(c -> rule.equals(c)).findAny();
      if (f.isPresent() && rule.bytes() < f.get().bytes()) {
        log.debug(
            "DistributedFlowStatisticStore:updateFlowStatistic():"
                + " Invalid Flow Update! Will be removed!!"
                + " curr flowId="
                + Long.toHexString(rule.id().value())
                + ", prev flowId="
                + Long.toHexString(f.get().id().value())
                + ", curr bytes="
                + rule.bytes()
                + ", prev bytes="
                + f.get().bytes()
                + ", curr life="
                + rule.life()
                + ", prev life="
                + f.get().life()
                + ", curr lastSeen="
                + rule.lastSeen()
                + ", prev lastSeen="
                + f.get().lastSeen());
        // something is wrong! invalid flow entry, so delete it
        removeFlowStatistic(rule);
        return;
      }
      Set<FlowEntry> prev = previous.get(cp);
      if (prev == null) {
        prev = new HashSet<>();
        previous.put(cp, prev);
      }

      // previous one is exist
      if (f.isPresent()) {
        // remove old one and add new one
        prev.remove(rule);
        if (!prev.add(f.get())) {
          log.debug(
              "DistributedFlowStatisticStore:updateFlowStatistic():"
                  + " flowId={}, add failed into previous.",
              Long.toHexString(rule.id().value()));
        }
      }

      // remove old one and add new one
      curr.remove(rule);
      if (!curr.add(rule)) {
        log.debug(
            "DistributedFlowStatisticStore:updateFlowStatistic():"
                + " flowId={}, add failed into current.",
            Long.toHexString(rule.id().value()));
      }
    }
  }
 public void add(FlowEntry rule) {
   getFlowEntriesInternal(rule.deviceId(), rule.id()).add((StoredFlowEntry) rule);
   lastUpdateTimes.put(rule.deviceId(), System.currentTimeMillis());
 }