Create an implementaion of IntentIdGenerator

- Define interface to allocate IdBlock in IdBlockAllocator
- Implement IdBlockAllocatorBasedIntentIdGenerator
- Refactor IdBlock class

Change-Id: I21fa21ae625e3d7e137a7f846bb5a0c1bdb8df9a
diff --git a/src/main/java/net/onrc/onos/core/newintent/IdBlockAllocatorBasedIntentIdGenerator.java b/src/main/java/net/onrc/onos/core/newintent/IdBlockAllocatorBasedIntentIdGenerator.java
new file mode 100644
index 0000000..9f2eceb
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/IdBlockAllocatorBasedIntentIdGenerator.java
@@ -0,0 +1,34 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.newintent.IntentId;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+import net.onrc.onos.core.registry.IdBlock;
+import net.onrc.onos.core.util.UnavailableIdException;
+import net.onrc.onos.core.util.IdBlockAllocator;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An implementation of {@link IntentIdGenerator},
+ * which uses {@link IdBlockAllocator#allocateUniqueIdBlock()}.
+ */
+public class IdBlockAllocatorBasedIntentIdGenerator implements IntentIdGenerator {
+
+    private final IdBlockAllocator allocator;
+    private IdBlock idBlock;
+
+    public IdBlockAllocatorBasedIntentIdGenerator(IdBlockAllocator allocator) {
+        this.allocator = checkNotNull(allocator);
+        this.idBlock = allocator.allocateUniqueIdBlock();
+    }
+
+    @Override
+    public synchronized IntentId getNewId() {
+        try {
+            return new IntentId(idBlock.getNextId());
+        } catch (UnavailableIdException e) {
+            idBlock = allocator.allocateUniqueIdBlock();
+            return new IntentId(idBlock.getNextId());
+        }
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/registry/IControllerRegistryService.java b/src/main/java/net/onrc/onos/core/registry/IControllerRegistryService.java
index ef441db..7d41a4e 100644
--- a/src/main/java/net/onrc/onos/core/registry/IControllerRegistryService.java
+++ b/src/main/java/net/onrc/onos/core/registry/IControllerRegistryService.java
@@ -5,6 +5,7 @@
 import java.util.Map;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
+import net.onrc.onos.core.util.IdBlockAllocator;
 import net.onrc.onos.core.util.OnosInstanceId;
 
 /**
@@ -21,7 +22,7 @@
  * mechanism that enables them to decide who should control a each switch. The
  * registry service provides this mechanism.
  */
-public interface IControllerRegistryService extends IFloodlightService {
+public interface IControllerRegistryService extends IFloodlightService, IdBlockAllocator {
 
     /**
      * Callback interface for control change events.
@@ -133,21 +134,6 @@
     public Collection<Long> getSwitchesControlledByController(String controllerId);
 
     /**
-     * Get a unique Id Block.
-     *
-     * @return Id Block.
-     */
-    public IdBlock allocateUniqueIdBlock();
-
-    /**
-     * Get next unique id and retrieve a new range of ids if needed.
-     *
-     * @param range range to use for the identifier
-     * @return Id Block.
-     */
-    public IdBlock allocateUniqueIdBlock(long range);
-
-    /**
      * Get a globally unique ID.
      *
      * @return a globally unique ID.
diff --git a/src/main/java/net/onrc/onos/core/registry/IdBlock.java b/src/main/java/net/onrc/onos/core/registry/IdBlock.java
index 7184239..53048e4 100644
--- a/src/main/java/net/onrc/onos/core/registry/IdBlock.java
+++ b/src/main/java/net/onrc/onos/core/registry/IdBlock.java
@@ -1,31 +1,109 @@
 package net.onrc.onos.core.registry;
 
-public class IdBlock {
+import com.google.common.base.Objects;
+import net.onrc.onos.core.util.UnavailableIdException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * A class representing an ID space. This class is not thread-safe.
+ */
+public final class IdBlock {
     private final long start;
-    private final long end;
     private final long size;
 
-    public IdBlock(long start, long end, long size) {
+    private long currentId;
+
+    /**
+     * Constructs a new ID block with the specified size and initial value.
+     *
+     * @param start initial value of the block
+     * @param size size of the block
+     * @throws IllegalArgumentException if the size is less than or equal to 0
+     */
+    public IdBlock(long start, long size) {
+        checkArgument(size > 0, "size should be more than 0, but %s", size);
+
         this.start = start;
-        this.end = end;
         this.size = size;
+
+        this.currentId = start;
     }
 
+    // TODO: consider if this method is needed or not
+    /**
+     * Returns the initial value.
+     *
+     * @return initial value
+     */
     public long getStart() {
         return start;
     }
 
+    // TODO: consider if this method is needed or not
+    /**
+     * Returns the last value.
+     *
+     * @return last value
+     */
     public long getEnd() {
-        return end;
+        return start + size - 1;
     }
 
+    /**
+     * Returns the block size.
+     *
+     * @return block size
+     */
     public long getSize() {
         return size;
     }
 
+    /**
+     * Returns the next ID in the block.
+     *
+     * @return next ID
+     * @throws UnavailableIdException if there is no available ID in the block.
+     */
+    public long getNextId() {
+        if (currentId > getEnd()) {
+            throw new UnavailableIdException(String.format(
+                    "use all IDs in allocated space (size: %d, end: %d, current: %d)",
+                    size, getEnd(), currentId
+            ));
+        }
+        long id = currentId;
+        currentId++;
+
+        return id;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        IdBlock that = (IdBlock) o;
+        return Objects.equal(this.start, that.start)
+                && Objects.equal(this.size, that.size)
+                && Objects.equal(this.currentId, that.currentId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(start, size, currentId);
+    }
+
     @Override
     public String toString() {
-        return "IdBlock [start=" + start + ", end=" + end + ", size=" + size
-                + "]";
+        return Objects.toStringHelper(getClass())
+                .add("start", start)
+                .add("size", size)
+                .add("currentId", currentId)
+                .toString();
     }
 }
diff --git a/src/main/java/net/onrc/onos/core/registry/StandaloneRegistry.java b/src/main/java/net/onrc/onos/core/registry/StandaloneRegistry.java
index 97e445f..bf5612c 100644
--- a/src/main/java/net/onrc/onos/core/registry/StandaloneRegistry.java
+++ b/src/main/java/net/onrc/onos/core/registry/StandaloneRegistry.java
@@ -148,7 +148,7 @@
             long blockHead = blockTop;
             long blockTail = blockTop + BLOCK_SIZE;
 
-            IdBlock block = new IdBlock(blockHead, blockTail - 1, BLOCK_SIZE);
+            IdBlock block = new IdBlock(blockHead, BLOCK_SIZE);
             blockTop = blockTail;
 
             return block;
diff --git a/src/main/java/net/onrc/onos/core/registry/ZookeeperRegistry.java b/src/main/java/net/onrc/onos/core/registry/ZookeeperRegistry.java
index 5bb7f00..5bff0e8 100644
--- a/src/main/java/net/onrc/onos/core/registry/ZookeeperRegistry.java
+++ b/src/main/java/net/onrc/onos/core/registry/ZookeeperRegistry.java
@@ -489,7 +489,7 @@
                 result = distributedIdCounter.add(range);
             } while (result == null || !result.succeeded());
 
-            return new IdBlock(result.preValue(), result.postValue() - 1, range);
+            return new IdBlock(result.preValue(), range);
         } catch (Exception e) {
             log.error("Error allocating ID block");
         }
diff --git a/src/main/java/net/onrc/onos/core/util/IdBlockAllocator.java b/src/main/java/net/onrc/onos/core/util/IdBlockAllocator.java
new file mode 100644
index 0000000..3c38205
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/IdBlockAllocator.java
@@ -0,0 +1,23 @@
+package net.onrc.onos.core.util;
+
+import net.onrc.onos.core.registry.IdBlock;
+
+/**
+ * An interface that gives unique ID spaces.
+ */
+public interface IdBlockAllocator {
+    /**
+     * Allocates a unique Id Block.
+     *
+     * @return Id Block.
+     */
+    IdBlock allocateUniqueIdBlock();
+
+    /**
+     * Allocates next unique id and retrieve a new range of ids if needed.
+     *
+     * @param range range to use for the identifier
+     * @return Id Block.
+     */
+    IdBlock allocateUniqueIdBlock(long range);
+}
diff --git a/src/main/java/net/onrc/onos/core/util/UnavailableIdException.java b/src/main/java/net/onrc/onos/core/util/UnavailableIdException.java
new file mode 100644
index 0000000..17c9df5
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/UnavailableIdException.java
@@ -0,0 +1,34 @@
+package net.onrc.onos.core.util;
+
+/**
+ * Represents that there is no available IDs.
+ */
+public class UnavailableIdException extends RuntimeException {
+
+    private static final long serialVersionUID = -2287403908433720122L;
+
+    /**
+     * Constructs an exception with no message and no underlying cause.
+     */
+    public UnavailableIdException() {
+    }
+
+    /**
+     * Constructs an exception with the specified message.
+     *
+     * @param message the message describing the specific nature of the error
+     */
+    public UnavailableIdException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the specified message and the underlying cause.
+     *
+     * @param message the message describing the specific nature of the error
+     * @param cause the underlying cause of this error
+     */
+    public UnavailableIdException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/test/java/net/onrc/onos/core/newintent/IdBlockAllocatorBasedIntentIdGeneratorTest.java b/src/test/java/net/onrc/onos/core/newintent/IdBlockAllocatorBasedIntentIdGeneratorTest.java
new file mode 100644
index 0000000..97394bd
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/newintent/IdBlockAllocatorBasedIntentIdGeneratorTest.java
@@ -0,0 +1,48 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.newintent.IntentId;
+import net.onrc.onos.core.registry.IdBlock;
+import net.onrc.onos.core.util.IdBlockAllocator;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Suites of test of {@link IdBlockAllocatorBasedIntentIdGenerator}.
+ */
+public class IdBlockAllocatorBasedIntentIdGeneratorTest {
+    private IdBlockAllocator allocator;
+    private IdBlockAllocatorBasedIntentIdGenerator sut;
+
+    @Before
+    public void setUp() {
+        allocator = createMock(IdBlockAllocator.class);
+
+    }
+
+    /**
+     * Tests generated IntentId sequences using two {@link IdBlock blocks}.
+     */
+    @Test
+    public void testIds() {
+        expect(allocator.allocateUniqueIdBlock())
+                .andReturn(new IdBlock(0, 3))
+                .andReturn(new IdBlock(4, 3));
+
+        replay(allocator);
+        sut = new IdBlockAllocatorBasedIntentIdGenerator(allocator);
+
+        assertThat(sut.getNewId(), is(new IntentId(0)));
+        assertThat(sut.getNewId(), is(new IntentId(1)));
+        assertThat(sut.getNewId(), is(new IntentId(2)));
+
+        assertThat(sut.getNewId(), is(new IntentId(4)));
+        assertThat(sut.getNewId(), is(new IntentId(5)));
+        assertThat(sut.getNewId(), is(new IntentId(6)));
+    }
+}
diff --git a/src/test/java/net/onrc/onos/core/registry/IdBlockTest.java b/src/test/java/net/onrc/onos/core/registry/IdBlockTest.java
new file mode 100644
index 0000000..aec55fa
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/registry/IdBlockTest.java
@@ -0,0 +1,33 @@
+package net.onrc.onos.core.registry;
+
+import net.onrc.onos.core.util.UnavailableIdException;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+/**
+ * Suites of test of {@link IdBlock}.
+ */
+public class IdBlockTest {
+
+    private final IdBlock sut = new IdBlock(0, 3);
+
+    /**
+     * Tests generated sequences. Also checks occurrence of {@link UnavailableIdException},
+     * when the number of generated IDs exceeds the block size.
+     */
+    @Test
+    public void basics() {
+        assertThat(sut.getNextId(), is(0L));
+        assertThat(sut.getNextId(), is(1L));
+        assertThat(sut.getNextId(), is(2L));
+
+        try {
+            sut.getNextId();
+            fail("UnavailableIdException should be thrown");
+        } catch (UnavailableIdException e) {
+            assertTrue(true);
+        }
+    }
+}