Add option to use Hazelcast as datastore for development environment.

This patch will set the default backend as Hazelcast.
To use RAMCloud as backend data store, add -Dnet.onrc.onos.datastore.backend=ramcloud to java option.

- ClientMode: Use existing Hazelcast Instance if it exist.
  - map name starting with datastore:// is now configured to be strong consistent

- add main for manual testing
- follow PMD,etc. where easily possible

- make HZClient Singleton

Change-Id: Ibe2afc3bfddfd7fd567c91477c16cd679fc543d4
diff --git a/src/test/java/net/onrc/onos/datastore/hazelcast/HZTableTest.java b/src/test/java/net/onrc/onos/datastore/hazelcast/HZTableTest.java
new file mode 100644
index 0000000..a3bfab6
--- /dev/null
+++ b/src/test/java/net/onrc/onos/datastore/hazelcast/HZTableTest.java
@@ -0,0 +1,365 @@
+package net.onrc.onos.datastore.hazelcast;
+
+import static org.junit.Assert.*;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.TreeMap;
+
+import net.onrc.onos.datastore.IKVTable.IKVEntry;
+import net.onrc.onos.datastore.ObjectDoesntExistException;
+import net.onrc.onos.datastore.ObjectExistsException;
+import net.onrc.onos.datastore.WrongVersionException;
+import net.onrc.onos.datastore.hazelcast.HZTable.VersionedValue;
+import net.onrc.onos.datastore.utils.ByteArrayComparator;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+public class HZTableTest {
+    @Rule
+    public TestName name= new TestName();
+
+    static final String TEST_TABLE_NAME = "TableForUnitTest";
+    HZTable table;
+
+    @Before
+    public void setUp() throws Exception {
+	table = (HZTable) HZClient.getClient().getTable(TEST_TABLE_NAME);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+	HZClient.getClient().dropTable(table);
+    }
+
+    public void assertEntryInTable(final byte[] key, final byte[] value, final long version) {
+	VersionedValue valueblob = table.getBackendMap().get(key);
+	assertNotNull(valueblob);
+	assertArrayEquals(value, valueblob.getValue());
+	assertEquals(version, valueblob.getVersion());
+    }
+
+    public void assertKeyNotInTable(final byte[] key) {
+	VersionedValue valueblob = table.getBackendMap().get(key);
+	assertNull(valueblob);
+    }
+
+    @Test
+    public void testGetInitialVersion() {
+	final long version1 = HZTable.getInitialVersion();
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version1);
+
+	final long version2 = HZTable.getInitialVersion();
+	assertNotEquals(HZClient.VERSION_NONEXISTENT, version2);
+	assertNotEquals(version1, version2);
+    }
+
+    @Test
+    public void testGetNextVersion() {
+	final long nextVersion = HZTable.getNextVersion(1);
+	assertNotEquals(nextVersion, HZClient.VERSION_NONEXISTENT);
+	assertNotEquals(nextVersion, 1);
+
+	final long nextVersion1 = HZTable.getNextVersion(Long.MAX_VALUE);
+	assertNotEquals(nextVersion1, HZClient.VERSION_NONEXISTENT);
+	assertNotEquals(nextVersion1, Long.MAX_VALUE);
+
+	final long nextVersion11 = HZTable.getNextVersion(HZClient.VERSION_NONEXISTENT-1);
+	assertNotEquals(nextVersion11, HZClient.VERSION_NONEXISTENT);
+	assertNotEquals(nextVersion11, HZClient.VERSION_NONEXISTENT-1);
+    }
+
+    @Ignore // nothing to test for now
+    @Test
+    public void testHZTable() {
+	fail("Not yet implemented");
+    }
+
+    @Test
+    public void testGetTableName() {
+	assertEquals(TEST_TABLE_NAME, table.getTableName());
+    }
+
+    @Test
+    public void testVERSION_NONEXISTENT() {
+	assertEquals(HZClient.VERSION_NONEXISTENT, table.VERSION_NONEXISTENT());
+    }
+
+    @Test
+    public void testGetTableId() {
+	// for Hazelcast implementation IKVTableID is table itself
+	assertEquals(table, table.getTableId());
+    }
+
+    @Test
+    public void testCreate() throws ObjectExistsException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	final long version = table.create(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+
+	assertEntryInTable(key, value, version);
+    }
+
+    @Test(expected = ObjectExistsException.class)
+    public void testCreateConflict() throws ObjectExistsException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	final long version = table.create(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+
+	assertEntryInTable(key, value, version);
+
+	table.create(key, value);
+	fail("Should have thrown exception");
+    }
+
+    @Test
+    public void testForceCreate() {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	final long version = table.forceCreate(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+
+
+	final long version1 = table.forceCreate(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version1);
+	assertNotEquals(version, version1);
+	assertEntryInTable(key, value, version1);
+    }
+
+    @Test
+    public void testRead() throws ObjectDoesntExistException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to read
+	final long version = table.forceCreate(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+
+	// test body
+	final IKVEntry readValue = table.read(key);
+	assertArrayEquals(key, readValue.getKey());
+	assertArrayEquals(value, readValue.getValue());
+	assertEquals(version, readValue.getVersion());
+    }
+
+
+    @Test(expected = ObjectDoesntExistException.class)
+    public void testReadNotExist() throws ObjectDoesntExistException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+
+	table.read(key);
+	fail("Should have thrown exception");
+    }
+
+    @Test
+    public void testUpdateByteArrayByteArrayLongSuccess() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to update
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+
+	final long version = table.update(key, value, oldVersion);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+    }
+
+    @Test(expected = ObjectDoesntExistException.class)
+    public void testUpdateByteArrayByteArrayLongFailNoOldValue() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	final long oldVersion = 0xDEAD;
+
+	final long version = table.update(key, value, oldVersion);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+    }
+
+    @Test(expected = WrongVersionException.class)
+    public void testUpdateByteArrayByteArrayLongFailWrongVersion() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to update
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+	// some one updates (from different thread/process in reality)
+	table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+
+
+	table.update(key, value, oldVersion);
+	fail("Should have thrown exception");
+    }
+
+    @Test
+    public void testUpdateByteArrayByteArraySuccess() throws ObjectDoesntExistException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to update
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+
+	final long version = table.update(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+    }
+
+    @Test(expected = ObjectDoesntExistException.class)
+    public void testUpdateByteArrayByteArrayFailNoOldValue() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	final long version = table.update(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+	fail("Should have thrown exception");
+    }
+
+    @Test
+    public void testUpdateByteArrayByteArraySuccessIgnoreVersion() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+	final byte[] value = "SomeValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to update
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+	// someone updates (from different thread/process in reality)
+	table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+
+
+	final long version = table.update(key, value);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,version);
+	assertEntryInTable(key, value, version);
+    }
+
+    @Test
+    public void testDelete() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to delete
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+
+	long version = table.delete(key, oldVersion);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEquals(oldVersion, version);
+	assertKeyNotInTable(key);
+    }
+
+    @Test(expected = ObjectDoesntExistException.class)
+    public void testDeleteFailNoEntry() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+
+	final long oldVersion = 0xDEAD;
+
+	try {
+	    table.delete(key, oldVersion);
+	} catch (ObjectDoesntExistException | WrongVersionException e) {
+	    assertKeyNotInTable(key);
+	    throw e;
+	}
+	fail("Should have thrown exception");
+    }
+
+    @Test(expected = WrongVersionException.class)
+    public void testDeleteFailWrongVersion() throws ObjectDoesntExistException, WrongVersionException {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to delete
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+	// someone updates (from different thread/process in reality)
+	final long latestVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,latestVersion);
+
+	try {
+	    table.delete(key, oldVersion);
+	} catch (ObjectDoesntExistException | WrongVersionException e) {
+	    assertEntryInTable(key, oldValue, latestVersion);
+	    throw e;
+	}
+	fail("Should have thrown exception");
+    }
+
+
+
+    @Test
+    public void testForceDelete() {
+	final byte[] key = name.getMethodName().getBytes(StandardCharsets.UTF_8);
+	final byte[] oldValue = "OldValue".getBytes(StandardCharsets.UTF_8);
+
+	// put data to delete
+	final long oldVersion = table.forceCreate(key, oldValue);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEntryInTable(key, oldValue, oldVersion);
+
+	long version = table.forceDelete(key);
+	assertNotEquals(HZClient.VERSION_NONEXISTENT,oldVersion);
+	assertEquals(oldVersion, version);
+	assertKeyNotInTable(key);
+    }
+
+    @Test
+    public void testGetAllEntries() {
+	final int DATASETSIZE = 100;
+	final Map<byte[], VersionedValue> testdata = new TreeMap<>(ByteArrayComparator.BYTEARRAY_COMPARATOR);
+	for(int i = 0 ; i < DATASETSIZE ; ++i) {
+	    final byte[] key = (name.getMethodName()+i).getBytes(StandardCharsets.UTF_8);
+	    final byte[] value = ("Value"+i).getBytes(StandardCharsets.UTF_8);
+
+	    // put data to delete
+	    final long version = table.forceCreate(key, value);
+	    assertNotEquals(HZClient.VERSION_NONEXISTENT, version);
+	    assertEntryInTable(key, value, version);
+
+	    testdata.put(key, new VersionedValue(value, version));
+	}
+
+	Iterable<IKVEntry> datastore = table.getAllEntries();
+	for( IKVEntry entry : datastore ) {
+	    VersionedValue expectedValue = testdata.get(entry.getKey());
+	    assertNotNull(expectedValue);
+	    assertArrayEquals(expectedValue.getValue(), entry.getValue());
+	    assertEquals(expectedValue.getVersion(), entry.getVersion());
+
+	    testdata.remove(entry.getKey());
+	}
+
+	assertTrue(testdata.isEmpty());
+    }
+
+    @Test
+    public void testToString() {
+	assertEquals("[HZTable " + TEST_TABLE_NAME + "]", table.toString());
+    }
+
+}