adding device specific counters for meter ids in
the meter service.

Change-Id: I38d38a0a85024927f5a74013b2b4d9efa9b32d22
diff --git a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
index 94cada4..51394c3 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
@@ -43,7 +43,8 @@
 
     private DefaultMeterRequest(DeviceId deviceId, ApplicationId appId,
                                 Meter.Unit unit, boolean burst,
-                                Collection<Band> bands, MeterContext context, Type op) {
+                                Collection<Band> bands, MeterContext context,
+                                Type op) {
         this.deviceId = deviceId;
         this.appId = appId;
         this.unit = unit;
@@ -58,6 +59,7 @@
         return deviceId;
     }
 
+
     @Override
     public ApplicationId appId() {
         return appId;
@@ -107,6 +109,7 @@
         private Collection<Band> bands;
         private DeviceId deviceId;
         private MeterContext context;
+        private Optional<MeterId> desiredId = Optional.empty();
 
 
         @Override
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java b/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java
new file mode 100644
index 0000000..086d4d6
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.meter;
+
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+/**
+ * A meter key represents a meter uniquely.
+ */
+public final class MeterKey {
+
+    private final DeviceId deviceId;
+    private final MeterId id;
+
+    private MeterKey(DeviceId deviceId, MeterId id) {
+        this.deviceId = deviceId;
+        this.id = id;
+    }
+
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    public MeterId meterId() {
+        return id;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MeterKey meterKey = (MeterKey) o;
+        return Objects.equal(deviceId, meterKey.deviceId) &&
+                Objects.equal(id, meterKey.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(deviceId, id);
+    }
+
+    public static MeterKey key(DeviceId deviceId, MeterId id) {
+        return new MeterKey(deviceId, id);
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterService.java b/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
index bdc90eb..2e07cb6 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.meter;
 
 import org.onosproject.event.ListenerService;
+import org.onosproject.net.DeviceId;
 
 import java.util.Collection;
 
@@ -46,10 +47,11 @@
     /**
      * Fetch the meter by the meter id.
      *
+     * @param deviceId a device id
      * @param id a meter id
      * @return a meter
      */
-    Meter getMeter(MeterId id);
+    Meter getMeter(DeviceId deviceId, MeterId id);
 
     /**
      * Fetches all the meters.
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java b/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
index 5112a4a..f429e95 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
@@ -57,12 +57,12 @@
     void updateMeterState(Meter meter);
 
     /**
-     * Obtains a meter matching the given meter id.
+     * Obtains a meter matching the given meter key.
      *
-     * @param meterId a meter id
+     * @param key a meter key
      * @return a meter
      */
-    Meter getMeter(MeterId meterId);
+    Meter getMeter(MeterKey key);
 
     /**
      * Returns all meters stored in the store.
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java
index 575a715..8c86e01 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.incubator.net.meter.impl;
 
+import com.google.common.collect.Maps;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -27,6 +28,7 @@
 import org.onosproject.net.meter.MeterEvent;
 import org.onosproject.net.meter.MeterFailReason;
 import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterKey;
 import org.onosproject.net.meter.MeterListener;
 import org.onosproject.net.meter.MeterOperation;
 import org.onosproject.net.meter.MeterProvider;
@@ -61,7 +63,7 @@
         MeterProvider, MeterProviderService>
         implements MeterService, MeterProviderRegistry {
 
-    private final String meterIdentifier = "meter-id-counter";
+    private static final String METERCOUNTERIDENTIFIER = "meter-id-counter-%s";
     private final Logger log = getLogger(getClass());
     private final MeterStoreDelegate delegate = new InternalMeterStoreDelegate();
 
@@ -71,15 +73,16 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MeterStore store;
 
-    private AtomicCounter meterIdCounter;
+    private Map<DeviceId, AtomicCounter> meterIdCounters
+            = Maps.newConcurrentMap();
 
     private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
 
     @Activate
     public void activate() {
-        meterIdCounter = storageService.atomicCounterBuilder()
-                .withName(meterIdentifier)
-                .build();
+        /*meterIdCounter = storageService.atomicCounterBuilder()
+                .withName(METERCOUNTERIDENTIFIER)
+                .build();*/
 
         store.setDelegate(delegate);
 
@@ -115,11 +118,13 @@
     @Override
     public Meter submit(MeterRequest request) {
 
+        MeterId id = allocateMeterId(request.deviceId());
+
         Meter.Builder mBuilder = DefaultMeter.builder()
                 .forDevice(request.deviceId())
                 .fromApp(request.appId())
                 .withBands(request.bands())
-                .withId(allocateMeterId())
+                .withId(id)
                 .withUnit(request.unit());
 
         if (request.isBurst()) {
@@ -152,8 +157,9 @@
     }
 
     @Override
-    public Meter getMeter(MeterId id) {
-        return store.getMeter(id);
+    public Meter getMeter(DeviceId deviceId, MeterId id) {
+        MeterKey key = MeterKey.key(deviceId, id);
+        return store.getMeter(key);
     }
 
     @Override
@@ -161,9 +167,21 @@
         return store.getAllMeters();
     }
 
-    private MeterId allocateMeterId() {
-        // FIXME: This will break one day.
-        return MeterId.meterId((int) meterIdCounter.incrementAndGet());
+    private MeterId allocateMeterId(DeviceId deviceId) {
+        long id = meterIdCounters.compute(deviceId, (k, v) -> {
+            if (v == null) {
+                return allocateCounter(k);
+            }
+            return v;
+        }).incrementAndGet();
+
+        return MeterId.meterId((int) id);
+    }
+
+    private AtomicCounter allocateCounter(DeviceId deviceId) {
+        return storageService.atomicCounterBuilder()
+                .withName(String.format(METERCOUNTERIDENTIFIER, deviceId))
+                .build();
     }
 
     private class InternalMeterProviderService
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java
index e0c0c86..76caebc 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java
@@ -130,7 +130,7 @@
         m2 = DefaultMeter.builder()
                 .forDevice(did("2"))
                 .fromApp(APP_ID)
-                .withId(MeterId.meterId(2))
+                .withId(MeterId.meterId(1))
                 .withUnit(Meter.Unit.KB_PER_SEC)
                 .withBands(Collections.singletonList(band))
                 .build();
@@ -167,7 +167,7 @@
 
         assertTrue("The meter was not added", manager.getAllMeters().size() == 1);
 
-        assertThat(manager.getMeter(MeterId.meterId(1)), is(m1));
+        assertThat(manager.getMeter(did("1"), MeterId.meterId(1)), is(m1));
     }
 
     @Test
@@ -175,7 +175,7 @@
         manager.submit(m1Request.add());
         manager.withdraw(m1Request.remove(), m1.id());
 
-        assertThat(manager.getMeter(MeterId.meterId(1)).state(),
+        assertThat(manager.getMeter(did("1"), MeterId.meterId(1)).state(),
                    is(MeterState.PENDING_REMOVE));
 
         providerService.pushMeterMetrics(m1.deviceId(), Collections.emptyList());
@@ -184,7 +184,16 @@
 
     }
 
+    @Test
+    public void testMultipleDevice() {
+        manager.submit(m1Request.add());
+        manager.submit(m2Request.add());
 
+        assertTrue("The meters were not added", manager.getAllMeters().size() == 2);
+
+        assertThat(manager.getMeter(did("1"), MeterId.meterId(1)), is(m1));
+        assertThat(manager.getMeter(did("2"), MeterId.meterId(1)), is(m2));
+    }
 
     public class TestApplicationId extends DefaultApplicationId {
         public TestApplicationId(int id, String name) {
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
index 32890cb..c09b1ac 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
@@ -33,6 +33,7 @@
 import org.onosproject.net.meter.MeterEvent;
 import org.onosproject.net.meter.MeterFailReason;
 import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterKey;
 import org.onosproject.net.meter.MeterOperation;
 import org.onosproject.net.meter.MeterState;
 import org.onosproject.net.meter.MeterStore;
@@ -78,12 +79,12 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private ClusterService clusterService;
 
-    private ConsistentMap<MeterId, MeterData> meters;
+    private ConsistentMap<MeterKey, MeterData> meters;
     private NodeId local;
 
     private MapEventListener mapListener = new InternalMapEventListener();
 
-    private Map<MeterId, CompletableFuture<MeterStoreResult>> futures =
+    private Map<MeterKey, CompletableFuture<MeterStoreResult>> futures =
             Maps.newConcurrentMap();
 
     @Activate
@@ -92,9 +93,10 @@
         local = clusterService.getLocalNode().id();
 
 
-        meters = storageService.<MeterId, MeterData>consistentMapBuilder()
+        meters = storageService.<MeterKey, MeterData>consistentMapBuilder()
                     .withName(METERSTORE)
                     .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+                                                     MeterKey.class,
                                                      MeterData.class,
                                                      DefaultMeter.class,
                                                      DefaultBand.class,
@@ -120,11 +122,12 @@
     @Override
     public CompletableFuture<MeterStoreResult> storeMeter(Meter meter) {
         CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
-        futures.put(meter.id(), future);
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        futures.put(key, future);
         MeterData data = new MeterData(meter, null, local);
 
         try {
-            meters.put(meter.id(), data);
+            meters.put(key, data);
         } catch (StorageException e) {
             future.completeExceptionally(e);
         }
@@ -136,14 +139,15 @@
     @Override
     public CompletableFuture<MeterStoreResult> deleteMeter(Meter meter) {
         CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
-        futures.put(meter.id(), future);
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        futures.put(key, future);
 
         MeterData data = new MeterData(meter, null, local);
 
         // update the state of the meter. It will be pruned by observing
         // that it has been removed from the dataplane.
         try {
-            if (meters.computeIfPresent(meter.id(), (k, v) -> data) == null) {
+            if (meters.computeIfPresent(key, (k, v) -> data) == null) {
                 future.complete(MeterStoreResult.success());
             }
         } catch (StorageException e) {
@@ -157,11 +161,12 @@
     @Override
     public CompletableFuture<MeterStoreResult> updateMeter(Meter meter) {
         CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
-        futures.put(meter.id(), future);
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        futures.put(key, future);
 
         MeterData data = new MeterData(meter, null, local);
         try {
-            if (meters.computeIfPresent(meter.id(), (k, v) -> data) == null) {
+            if (meters.computeIfPresent(key, (k, v) -> data) == null) {
                 future.complete(MeterStoreResult.fail(MeterFailReason.INVALID_METER));
             }
         } catch (StorageException e) {
@@ -172,7 +177,8 @@
 
     @Override
     public void updateMeterState(Meter meter) {
-        meters.computeIfPresent(meter.id(), (id, v) -> {
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        meters.computeIfPresent(key, (k, v) -> {
             DefaultMeter m = (DefaultMeter) v.meter();
             m.setState(meter.state());
             m.setProcessedPackets(meter.packetsSeen());
@@ -185,8 +191,8 @@
     }
 
     @Override
-    public Meter getMeter(MeterId meterId) {
-        MeterData data = Versioned.valueOrElse(meters.get(meterId), null);
+    public Meter getMeter(MeterKey key) {
+        MeterData data = Versioned.valueOrElse(meters.get(key), null);
         return data == null ? null : data.meter();
     }
 
@@ -198,14 +204,16 @@
 
     @Override
     public void failedMeter(MeterOperation op, MeterFailReason reason) {
-        meters.computeIfPresent(op.meter().id(), (k, v) ->
+        MeterKey key = MeterKey.key(op.meter().deviceId(), op.meter().id());
+        meters.computeIfPresent(key, (k, v) ->
                 new MeterData(v.meter(), reason, v.origin()));
     }
 
     @Override
     public void deleteMeterNow(Meter m) {
-        futures.remove(m.id());
-        meters.remove(m.id());
+        MeterKey key = MeterKey.key(m.deviceId(), m.id());
+        futures.remove(key);
+        meters.remove(key);
     }
 
     private class InternalMapEventListener implements MapEventListener<MeterId, MeterData> {