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);
+ }
+ }
+}