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/main/java/net/onrc/onos/datastore/hazelcast/HZMultiEntryOperation.java b/src/main/java/net/onrc/onos/datastore/hazelcast/HZMultiEntryOperation.java
new file mode 100644
index 0000000..1da7f1a
--- /dev/null
+++ b/src/main/java/net/onrc/onos/datastore/hazelcast/HZMultiEntryOperation.java
@@ -0,0 +1,162 @@
+package net.onrc.onos.datastore.hazelcast;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.onrc.onos.datastore.IKVTableID;
+import net.onrc.onos.datastore.IMultiEntryOperation;
+import net.onrc.onos.datastore.hazelcast.HZTable.VersionedValue;
+import net.onrc.onos.datastore.internal.IModifiableMultiEntryOperation;
+import net.onrc.onos.datastore.utils.ByteArrayUtil;
+
+public class HZMultiEntryOperation implements IMultiEntryOperation, IModifiableMultiEntryOperation {
+    private static final Logger log = LoggerFactory.getLogger(HZMultiEntryOperation.class);
+
+    private final HZTable table;
+    private final byte[] key;
+    protected final OPERATION operation;
+    private STATUS status;
+
+    // for read op
+    private Future<VersionedValue> future;
+    // for write op
+    private VersionedValue writeValue;
+
+    /**
+     * Constructor for Read/ForceDelete Operation.
+     * @param table
+     * @param key
+     * @param operation
+     */
+    public HZMultiEntryOperation(final HZTable table, final byte[] key, final OPERATION operation) {
+	this.table = table;
+	this.key = key;
+	this.status = STATUS.NOT_EXECUTED;
+	this.operation = operation;
+
+	this.future = null;
+	this.writeValue = null;
+    }
+
+    /**
+     * Constructor for Other Operations.
+     * @param table
+     * @param key
+     * @param value
+     * @param version
+     * @param operation
+     */
+    public HZMultiEntryOperation(final HZTable table, final byte[] key, final byte[] value, final long version, final OPERATION operation) {
+	this.table = table;
+	this.key = key;
+	this.status = STATUS.NOT_EXECUTED;
+	this.operation = operation;
+
+	this.future = null;
+	this.writeValue = new VersionedValue(value, version);
+    }
+
+    @Override
+    public boolean hasSucceeded() {
+
+	VersionedValue value = get();
+	return (value != null) && (this.status == STATUS.SUCCESS);
+    }
+
+    @Override
+    public STATUS getStatus() {
+	get();
+	return status;
+    }
+
+    @Override
+    public IKVTableID getTableId() {
+	return table;
+    }
+
+    @Override
+    public byte[] getKey() {
+	return key;
+    }
+
+    @Override
+    public byte[] getValue() {
+	if (future != null) {
+	    VersionedValue value = get();
+	    return value.getValue();
+	}
+	return writeValue.getValue();
+    }
+
+    @Override
+    public long getVersion() {
+	if (future != null) {
+	    VersionedValue value = get();
+	    return value.getVersion();
+	}
+	return writeValue.getVersion();
+    }
+
+    @Override
+    public OPERATION getOperation() {
+	return operation;
+    }
+
+    /**
+     * Evaluate Future object and set Status and Value+Version.
+     * @return the value read or null on failure.
+     */
+    private VersionedValue get() {
+        try {
+            VersionedValue value = future.get();
+            setValue(value.getValue(), value.getVersion());
+            setStatus(STATUS.SUCCESS);
+            return value;
+        } catch (CancellationException | InterruptedException | ExecutionException e) {
+            log.error(this + " has failed.", e);
+            setStatus(STATUS.FAILED);
+            return null;
+        }
+    }
+
+    @Override
+    public void setValue(final byte[] value, final long version) {
+	writeValue = new VersionedValue(value, version);
+	setVersion(version);
+    }
+
+    @Override
+    public void setVersion(final long version) {
+	if (future != null) {
+	    // no-op on read
+	}
+	if (writeValue == null) {
+	    writeValue = new VersionedValue(null, version);
+	}
+    }
+
+    @Override
+    public void setStatus(final STATUS status) {
+	this.status = status;
+    }
+
+    @Override
+    public IModifiableMultiEntryOperation getActualOperation() {
+	return this;
+    }
+
+    void setFuture(final Future<VersionedValue> future) {
+        this.future = future;
+    }
+
+    @Override
+    public String toString() {
+	return "[HZMultiEntryOperation table=" + table + ", key="
+	        + ByteArrayUtil.toHexStringBuffer(key, ":") + ", operation=" + operation
+	        + ", status=" + status + ", writeValue=" + writeValue + "]";
+    }
+}