Merge blueprints-ramcloud-graph into ONOS code
diff --git a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
new file mode 100755
index 0000000..fc93cb4
--- /dev/null
+++ b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
@@ -0,0 +1,766 @@
+/* Copyright (c) 2013 Stanford University
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <RamCloud.h>
+#include <TableEnumerator.h>
+#include <Object.h>
+#include "edu_stanford_ramcloud_JRamCloud.h"
+#include "edu_stanford_ramcloud_JRamCloud_TableEnumerator.h"
+
+using namespace RAMCloud;
+
+/// Our JRamCloud java library is packaged under "edu.stanford.ramcloud".
+/// We will need this when using FindClass, etc.
+#define PACKAGE_PATH "edu/stanford/ramcloud/"
+
+#define check_null(var, msg)                                                \
+    if (var == NULL) {                                                      \
+        throw Exception(HERE, "JRamCloud: NULL returned: " msg "\n");       \
+    }
+
+/**
+ * This class provides a simple means of extracting C-style strings
+ * from a jstring and cleans up when the destructor is called. This
+ * avoids having to manually do the annoying GetStringUTFChars /
+ * ReleaseStringUTFChars dance. 
+ */
+class JStringGetter {
+  public:
+    JStringGetter(JNIEnv* env, jstring jString)
+        : env(env)
+        , jString(jString)
+        , string(env->GetStringUTFChars(jString, 0))
+    {
+        check_null(string, "GetStringUTFChars failed");
+    }
+    
+    ~JStringGetter()
+    {
+        if (string != NULL)
+            env->ReleaseStringUTFChars(jString, string);
+    }
+
+  private:    
+    JNIEnv* env;
+    jstring jString;
+
+  public:
+    const char* const string;
+};
+
+/**
+ * This class provides a simple means of accessing jbyteArrays as
+ * C-style void* buffers and cleans up when the destructor is called.
+ * This avoids having to manually do the annoying GetByteArrayElements /
+ * ReleaseByteArrayElements dance.
+ */
+class JByteArrayGetter {
+  public:
+    JByteArrayGetter(JNIEnv* env, jbyteArray jByteArray)
+        : env(env)
+        , jByteArray(jByteArray)
+        , pointer(static_cast<void*>(env->GetByteArrayElements(jByteArray, 0)))
+        , length(env->GetArrayLength(jByteArray))
+    {
+        check_null(pointer, "GetByteArrayElements failed");
+    }
+    
+    ~JByteArrayGetter()
+    {
+        if (pointer != NULL) {
+            env->ReleaseByteArrayElements(jByteArray,
+                                          reinterpret_cast<jbyte*>(pointer),
+                                          0);
+        }
+    }
+
+  private:    
+    JNIEnv* env;
+    jbyteArray jByteArray;
+
+  public:
+    void* const pointer;
+    const jsize length;
+};
+
+class JByteArrayReference {
+  public:
+    JByteArrayReference(JNIEnv* env, jbyteArray jByteArray)
+        : env(env)
+        , jByteArray(jByteArray)
+        , pointer(static_cast<const void*>(env->GetByteArrayElements(jByteArray, 0)))
+        , length(env->GetArrayLength(jByteArray))
+    {
+        check_null(pointer, "GetByteArrayElements failed");
+    }
+
+    ~JByteArrayReference()
+    {
+        if (pointer != NULL) {
+            env->ReleaseByteArrayElements(jByteArray,
+                                          (jbyte*)pointer,
+                                          JNI_ABORT);
+        }
+    }
+
+  private:
+    JNIEnv* env;
+    jbyteArray jByteArray;
+
+  public:
+    const void* const pointer;
+    const jsize length;
+};
+
+static RamCloud*
+getRamCloud(JNIEnv* env, jobject jRamCloud)
+{
+    const static jclass cls = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud"));
+    const static jfieldID fieldId = env->GetFieldID(cls, "ramcloudObjectPointer", "J");
+    return reinterpret_cast<RamCloud*>(env->GetLongField(jRamCloud, fieldId));
+}
+
+static TableEnumerator*
+getTableEnumerator(JNIEnv* env, jobject jTableEnumerator)
+{
+    const static jclass cls = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$TableEnumerator"));
+    const static jfieldID fieldId = env->GetFieldID(cls, "tableEnumeratorObjectPointer", "J");
+    return reinterpret_cast<TableEnumerator*>(env->GetLongField(jTableEnumerator, fieldId));    
+}
+
+static void
+createException(JNIEnv* env, jobject jRamCloud, const char* name)
+{
+    // Need to specify the full class name, including the package. To make it
+    // slightly more complicated, our exceptions are nested under the JRamCloud
+    // class.
+    string fullName = PACKAGE_PATH;
+    fullName += "JRamCloud$";
+    fullName += name;
+
+    // This would be much easier if we didn't make our Exception classes nested
+    // under JRamCloud since env->ThrowNew() could be used instead. The problem
+    // is that ThrowNew assumes a particular method signature that happens to
+    // be incompatible with the nested classes' signatures.
+    jclass cls = env->FindClass(fullName.c_str());
+    check_null(cls, "FindClass failed");
+
+    jmethodID methodId = env->GetMethodID(cls,
+                                          "<init>",
+                                          "(L" PACKAGE_PATH "JRamCloud;Ljava/lang/String;)V");
+    check_null(methodId, "GetMethodID failed");
+
+    jstring jString = env->NewStringUTF("");
+    check_null(jString, "NewStringUTF failed");
+
+    jthrowable exception = reinterpret_cast<jthrowable>(
+        env->NewObject(cls, methodId, jRamCloud, jString));
+    check_null(exception, "NewObject failed");
+
+    env->Throw(exception);
+}
+
+/**
+ * This macro is used to catch C++ exceptions and convert them into Java
+ * exceptions. Be sure to wrap the individual RamCloud:: calls in try blocks,
+ * rather than the entire methods, since doing so with functions that return
+ * non-void is a bad idea with undefined(?) behaviour. 
+ *
+ * _returnValue is the value that should be returned from the JNI function
+ * when an exception is caught and generated in Java. As far as I can tell,
+ * the exception fires immediately upon returning from the JNI method. I
+ * don't think anything else would make sense, but the JNI docs kind of
+ * suck.
+ */
+#define EXCEPTION_CATCHER(_returnValue)                                        \
+    catch (TableDoesntExistException& e) {                                     \
+        createException(env, jRamCloud, "TableDoesntExistException");          \
+        return _returnValue;                                                   \
+    } catch (ObjectDoesntExistException& e) {                                  \
+        createException(env, jRamCloud, "ObjectDoesntExistException");         \
+        return _returnValue;                                                   \
+    } catch (ObjectExistsException& e) {                                       \
+        createException(env, jRamCloud, "ObjectExistsException");              \
+        return _returnValue;                                                   \
+    } catch (WrongVersionException& e) {                                       \
+        createException(env, jRamCloud, "WrongVersionException");              \
+        return _returnValue;                                                   \
+    } catch (RejectRulesException& e) {                                        \
+        createException(env, jRamCloud, "RejectRulesException");               \
+        return _returnValue;                                                   \
+    } catch (InvalidObjectException& e) {                                      \
+        createException(env, jRamCloud, "InvalidObjectException");             \
+        return _returnValue;                                                   \
+    }
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    connect
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong 
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_connect(JNIEnv *env,
+                               jclass jRamCloud,
+                               jstring coordinatorLocator)
+{
+    JStringGetter locator(env, coordinatorLocator);
+    RamCloud* ramcloud = NULL;
+    try {
+        ramcloud = new RamCloud(locator.string);
+    } EXCEPTION_CATCHER(NULL);
+    return reinterpret_cast<jlong>(ramcloud);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    disconnect
+ * Signature: (J)V
+ */
+JNIEXPORT void
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_disconnect(JNIEnv *env,
+                                  jclass jRamCloud,
+                                  jlong ramcloudObjectPointer)
+{
+    delete reinterpret_cast<RamCloud*>(ramcloudObjectPointer);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    createTable
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_createTable__Ljava_lang_String_2(JNIEnv *env,
+                                                        jobject jRamCloud,
+                                                        jstring jTableName)
+{
+    return Java_edu_stanford_ramcloud_JRamCloud_createTable__Ljava_lang_String_2I(env,
+                                                            jRamCloud,
+                                                            jTableName,
+                                                            1);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    createTable
+ * Signature: (Ljava/lang/String;I)I
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_createTable__Ljava_lang_String_2I(JNIEnv *env,
+                                                         jobject jRamCloud,
+                                                         jstring jTableName,
+                                                         jint jServerSpan)
+{
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JStringGetter tableName(env, jTableName);
+    uint64_t tableId;
+    try {
+        tableId = ramcloud->createTable(tableName.string, jServerSpan);
+    } EXCEPTION_CATCHER(-1);
+    return static_cast<jlong>(tableId);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    dropTable
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT void
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_dropTable(JNIEnv *env,
+                                 jobject jRamCloud,
+                                 jstring jTableName)
+{
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JStringGetter tableName(env, jTableName);
+    try {
+        ramcloud->dropTable(tableName.string);
+    } EXCEPTION_CATCHER();
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    getTableId
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_getTableId(JNIEnv *env,
+                                  jobject jRamCloud,
+                                  jstring jTableName)
+{
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JStringGetter tableName(env, jTableName);
+    uint64_t tableId;
+    try {
+        tableId = ramcloud->getTableId(tableName.string);
+    } EXCEPTION_CATCHER(-1);
+    return tableId;
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    read
+ * Signature: (J[B)LJRamCloud/Object;
+ */
+JNIEXPORT jobject
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_read__J_3B(JNIEnv *env,
+                                  jobject jRamCloud,
+                                  jlong jTableId,
+                                  jbyteArray jKey)
+{
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+
+    Buffer buffer;
+    uint64_t version;
+    try {
+        ramcloud->read(jTableId, key.pointer, key.length, &buffer, NULL, &version);
+    } EXCEPTION_CATCHER(NULL);
+
+    jbyteArray jValue = env->NewByteArray(buffer.getTotalLength());
+    check_null(jValue, "NewByteArray failed");
+    JByteArrayGetter value(env, jValue);
+    buffer.copy(0, buffer.getTotalLength(), value.pointer);
+
+    // Note that using 'javap -s' on the class file will print out the method
+    // signatures (the third argument to GetMethodID).
+    const static jclass cls = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$Object"));
+    check_null(cls, "FindClass failed");
+
+    const static jmethodID methodId = env->GetMethodID(cls,
+                                          "<init>",
+                                          "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+    check_null(methodId, "GetMethodID failed");
+
+    return env->NewObject(cls,
+                          methodId,
+                          jRamCloud,
+                          jKey,
+                          jValue,
+                          static_cast<jlong>(version));
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    read
+ * Signature: (J[BLJRamCloud/RejectRules;)LJRamCloud/Object;
+ */
+JNIEXPORT jobject
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_read__J_3BLJRamCloud_RejectRules_2(JNIEnv *env,
+                                                          jobject jRamCloud,
+                                                          jlong jTableId,
+                                                          jbyteArray jKey,
+                                                          jobject jRejectRules)
+{
+    // XXX-- implement me by generalising the other read() method.
+    return NULL;
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    multiRead
+ * Signature: ([Ledu/stanford/ramcloud/JRamCloud$multiReadObject;I)[Ledu/stanford/ramcloud/JRamCloud$Object;
+ */
+JNIEXPORT jobjectArray
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiRead(JNIEnv *env,   
+                                                        jobject jRamCloud,
+                                                        jobjectArray jmultiReadArray){
+
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    const jint requestNum = env->GetArrayLength(jmultiReadArray);
+    MultiReadObject objects[requestNum];
+    Tub<Buffer> values[requestNum];
+    jbyteArray jKey[requestNum];
+    MultiReadObject* requests[requestNum];
+
+    const static jclass jc_multiReadObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$multiReadObject"));
+    const static jfieldID jf_tableId = env->GetFieldID(jc_multiReadObject, "tableId", "J");
+    const static jfieldID jf_key = env->GetFieldID(jc_multiReadObject, "key", "[B");
+
+    for (int i = 0 ; i < requestNum ; i++){
+        jobject obj = env->GetObjectArrayElement(jmultiReadArray, i);
+        check_null(obj, "GetObjectArrayElement failed");
+        jlong jTableId = env->GetLongField(obj, jf_tableId);
+
+        jKey[i] = (jbyteArray)env->GetObjectField(obj, jf_key);
+
+        jbyte* data = env->GetByteArrayElements(jKey[i], NULL);
+        check_null(data, "GetByteArrayElements failed");
+
+        objects[i].tableId = jTableId;
+        objects[i].key = data;
+        objects[i].keyLength = env->GetArrayLength(jKey[i]);
+        objects[i].value = &values[i];
+        requests[i] = &objects[i];
+    }
+
+    try {
+        ramcloud->multiRead(requests, requestNum);
+    } EXCEPTION_CATCHER(NULL);
+    
+    const static jclass jc_RcObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$Object"));
+    check_null(jc_RcObject, "FindClass failed");
+    const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
+                                        "<init>",
+                                        "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+
+    jobjectArray outJNIArray = env->NewObjectArray(requestNum, jc_RcObject , NULL);
+    check_null(outJNIArray, "NewObjectArray failed");
+    
+    for (int i = 0 ; i < requestNum ; i++) {
+	if (objects[i].status == 0) {
+	    jbyteArray jValue = env->NewByteArray(values[i].get()->getTotalLength());
+	    check_null(jValue, "NewByteArray failed");
+	    JByteArrayGetter value(env, jValue);
+	    values[i].get()->copy(0, values[i].get()->getTotalLength(), value.pointer);
+	    jobject obj = env->NewObject(jc_RcObject, jm_init, jRamCloud, jKey[i], jValue);
+	    check_null(obj, "NewObject failed");
+	    env->SetObjectArrayElement(outJNIArray, i, obj);
+	}
+	// keys are read only so no need to copy back to Java side: JNI_ABORT
+	env->ReleaseByteArrayElements(jKey[i], (jbyte *) objects[i].key, JNI_ABORT);
+    }
+    return outJNIArray;
+}
+
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    remove
+ * Signature: (J[B)J
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_remove__J_3B(JNIEnv *env,
+                                    jobject jRamCloud,
+                                    jlong jTableId,
+                                    jbyteArray jKey)
+{
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+    uint64_t version;
+    try {
+        ramcloud->remove(jTableId, key.pointer, key.length, NULL, &version);
+    } EXCEPTION_CATCHER(-1);
+    return static_cast<jlong>(version);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    remove
+ * Signature: (J[BLJRamCloud/RejectRules;)J
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_remove__J_3BLJRamCloud_RejectRules_2(JNIEnv *env,
+                                                           jobject jRamCloud,
+                                                           jlong jTableId,
+                                                           jbyteArray jKey,
+                                                           jobject jRejectRules)
+{
+    // XXX- handle RejectRules
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+    uint64_t version;
+    try {
+        ramcloud->remove(jTableId, key.pointer, key.length, NULL, &version);
+    } EXCEPTION_CATCHER(-1);
+    return static_cast<jlong>(version);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    write
+ * Signature: (J[B[B)J
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_write__J_3B_3B(JNIEnv *env,
+                                      jobject jRamCloud,
+                                      jlong jTableId,
+                                      jbyteArray jKey,
+                                      jbyteArray jValue)
+{
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+    JByteArrayGetter value(env, jValue);
+    uint64_t version;
+    try {
+        ramcloud->write(jTableId,
+                        key.pointer, key.length,
+                        value.pointer, value.length,
+                        NULL,
+                        &version);
+    } EXCEPTION_CATCHER(-1);
+    return static_cast<jlong>(version);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    write
+ * Signature: (J[B[BLJRamCloud/RejectRules;)J
+ */
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_write__J_3B_3BLJRamCloud_RejectRules_2(JNIEnv *env,
+                                                           jobject jRamCloud,
+                                                           jlong jTableId,
+                                                           jbyteArray jKey,
+                                                           jbyteArray jValue,
+                                                           jobject jRejectRules)
+{
+    // XXX- handle RejectRules    
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+    JByteArrayGetter value(env, jValue);
+    RejectRules rules;
+    jclass ruleClass = env->GetObjectClass(jRejectRules);
+    jfieldID fid = env->GetFieldID(ruleClass, "doesntExist", "Z");
+    rules.doesntExist = (uint8_t) env->GetBooleanField(jRejectRules, fid);
+
+    fid = env->GetFieldID(ruleClass, "exists", "Z");
+    rules.exists = (uint8_t) env->GetBooleanField(jRejectRules, fid);
+
+    fid = env->GetFieldID(ruleClass, "givenVersion", "J");
+    rules.givenVersion = env->GetLongField(jRejectRules, fid);
+
+    fid = env->GetFieldID(ruleClass, "versionLeGiven", "Z");
+    rules.versionLeGiven = (uint8_t) env->GetBooleanField(jRejectRules, fid);
+
+    fid = env->GetFieldID(ruleClass, "versionNeGiven", "Z");
+    rules.versionNeGiven = (uint8_t) env->GetBooleanField(jRejectRules, fid);
+
+    uint64_t version;
+    try {
+        ramcloud->write(jTableId,
+                key.pointer, key.length,
+                value.pointer, value.length,
+                &rules,
+                &version);
+    }
+    EXCEPTION_CATCHER(-1);
+    return static_cast<jlong> (version);
+}
+
+JNIEXPORT jlong
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_writeRule(JNIEnv *env,
+        jobject jRamCloud,
+        jlong jTableId,
+        jbyteArray jKey,
+        jbyteArray jValue,
+        jobject jRejectRules) {
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+    JByteArrayGetter value(env, jValue);
+    uint64_t version;
+    RejectRules rules = {};
+    const static jclass jc_RejectRules = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$RejectRules"));
+
+    const static jfieldID jf_doesntExist = env->GetFieldID(jc_RejectRules, "doesntExist", "Z");
+    check_null(jf_doesntExist, "doesentExist field id is null");
+    jboolean ruleBool;
+    ruleBool = env->GetBooleanField(jRejectRules, jf_doesntExist);
+    rules.doesntExist = ruleBool ? 1 : 0;
+
+    const static jfieldID jf_exists = env->GetFieldID(jc_RejectRules, "exists", "Z");
+    check_null(jf_exists, "exists field id is null");
+    ruleBool = env->GetBooleanField(jRejectRules, jf_exists);
+    rules.exists = ruleBool ? 1 : 0;
+
+    const static jfieldID jf_givenVersion = env->GetFieldID(jc_RejectRules, "givenVersion", "J");
+    check_null(jf_givenVersion, "givenVersion field id is null");
+    rules.givenVersion = env->GetLongField(jRejectRules, jf_givenVersion);
+
+    const static jfieldID jf_versionLeGiven = env->GetFieldID(jc_RejectRules, "versionLeGiven", "Z");
+    check_null(jf_versionLeGiven, "versionLeGiven field id is null");
+    ruleBool = env->GetBooleanField(jRejectRules, jf_versionLeGiven);
+    rules.versionLeGiven = ruleBool ? 1 : 0;
+
+    const static jfieldID jf_versionNeGiven = env->GetFieldID(jc_RejectRules, "versionNeGiven", "Z");
+    check_null(jf_versionNeGiven, "versionNeGiven field id is null");
+    ruleBool = env->GetBooleanField(jRejectRules, jf_versionNeGiven);
+    rules.versionNeGiven = ruleBool ? 1 : 0;
+    try {
+        ramcloud->write(jTableId,
+                key.pointer, key.length,
+                value.pointer, value.length,
+                &rules,
+                &version);
+    }
+    EXCEPTION_CATCHER(-1);
+    return static_cast<jlong> (version);
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud_TableEnumerator
+ * Method:    init
+ * Signature: (J)V
+ */
+JNIEXPORT jlong JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_init(JNIEnv *env, 
+                                                                                      jobject jTableEnumerator, 
+                                                                                      jlong jTableId)
+{
+    const static jclass cls = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$TableEnumerator"));
+    const static jfieldID fieldId = env->GetFieldID(cls, "ramCloudObjectPointer", "J");
+    RamCloud* ramcloud = reinterpret_cast<RamCloud*>(env->GetLongField(jTableEnumerator, fieldId));
+  
+    return reinterpret_cast<jlong>(new TableEnumerator(*ramcloud, jTableId));
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud_TableEnumerator
+ * Method:    hasNext
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_hasNext( JNIEnv *env, 
+                                                                                              jobject jTableEnumerator)
+{
+    TableEnumerator* tableEnum = getTableEnumerator(env, jTableEnumerator);
+    return static_cast<jboolean>(tableEnum->hasNext());
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud_TableEnumerator
+ * Method:    next
+ * Signature: ()Ledu/stanford/ramcloud/JRamCloud/Object;
+ */
+JNIEXPORT jobject JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_next( JNIEnv *env, 
+                                                                                          jobject jTableEnumerator)
+{
+    TableEnumerator* tableEnum = getTableEnumerator(env, jTableEnumerator);
+
+    if(tableEnum->hasNext()) 
+    {
+        uint32_t size = 0;
+        const void* buffer = 0;
+        uint64_t version = 0;
+
+        tableEnum->next(&size, &buffer);
+        Object object(buffer, size);
+
+        jbyteArray jKey = env->NewByteArray(object.getKeyLength());
+        jbyteArray jValue = env->NewByteArray(object.getDataLength());
+        
+        JByteArrayGetter key(env, jKey);
+        JByteArrayGetter value(env, jValue);
+
+        memcpy(key.pointer, object.getKey(), object.getKeyLength());
+        memcpy(value.pointer, object.getData(), object.getDataLength());
+
+        version = object.getVersion();
+        
+        const static jclass cls = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$Object"));
+        check_null(cls, "FindClass failed");
+        const static jmethodID methodId = env->GetMethodID(cls,
+                                          "<init>",
+                                          "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+        check_null(methodId, "GetMethodID failed");
+        return env->NewObject(cls,
+                              methodId,
+                              jTableEnumerator,
+                              jKey,
+                              jValue,
+                              static_cast<jlong>(version));        
+    } else 
+        return NULL;
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    multiWrite
+ * Signature: ([Ledu/stanford/ramcloud/JRamCloud/MultiWriteObject;)[Ledu/stanford/ramcloud/JRamCloud/MultiWriteRspObject;
+ */
+JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env, jobject jRamCloud, jobjectArray jmultiWriteArray) {
+    jint requestNum = env->GetArrayLength(jmultiWriteArray);
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    Tub<MultiWriteObject> objects[requestNum];
+    MultiWriteObject *requests[requestNum];
+    RejectRules rules[requestNum];
+    jbyteArray jKey[requestNum];
+    jbyteArray jValue[requestNum];
+    
+    const static jclass jc_multiWriteObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$MultiWriteObject"));
+    const static jclass jc_RejectRules = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$RejectRules"));
+    const static jfieldID jf_tableId = env->GetFieldID(jc_multiWriteObject, "tableId", "J");
+    const static jfieldID jf_key = env->GetFieldID(jc_multiWriteObject, "key", "[B");
+    const static jfieldID jf_value = env->GetFieldID(jc_multiWriteObject, "value", "[B");
+    const static jfieldID jf_reject_rules = env->GetFieldID(jc_multiWriteObject, "rules", "L" PACKAGE_PATH "JRamCloud$RejectRules;");
+
+    const static jfieldID jf_doesntExist = env->GetFieldID(jc_RejectRules, "doesntExist", "Z");
+    check_null(jf_doesntExist, "doesentExist field id is null");
+    const static jfieldID jf_exists = env->GetFieldID(jc_RejectRules, "exists", "Z");
+    check_null(jf_exists, "exists field id is null");
+    const static jfieldID jf_givenVersion = env->GetFieldID(jc_RejectRules, "givenVersion", "J");
+    check_null(jf_givenVersion, "givenVersion field id is null");
+    const static jfieldID jf_versionLeGiven = env->GetFieldID(jc_RejectRules, "versionLeGiven", "Z");
+    check_null(jf_versionLeGiven, "versionLeGiven field id is null");
+    const static jfieldID jf_versionNeGiven = env->GetFieldID(jc_RejectRules, "versionNeGiven", "Z");
+    check_null(jf_versionNeGiven, "versionNeGiven field id is null");
+
+    for (int i = 0; i < requestNum; i++) {
+        jobject obj = env->GetObjectArrayElement(jmultiWriteArray, i);
+        check_null(obj, "multi write GetObjectArrayElement failed");
+
+        uint64_t tableId = env->GetLongField(obj, jf_tableId);
+
+        jKey[i] = (jbyteArray)env->GetObjectField(obj, jf_key);
+        jbyte* keyData = env->GetByteArrayElements(jKey[i], NULL);
+        uint16_t keyLength = env->GetArrayLength(jKey[i]);
+
+        jValue[i] = (jbyteArray)env->GetObjectField(obj, jf_value);
+        jbyte* valueData = env->GetByteArrayElements(jValue[i], NULL);
+        uint32_t valueLength = env->GetArrayLength(jValue[i]);
+
+        jobject jRejectRules = env->GetObjectField(obj, jf_reject_rules);
+        rules[i] = {};
+
+        if (jRejectRules != NULL) {
+            jboolean ruleBool;
+
+            ruleBool = env->GetBooleanField(jRejectRules, jf_doesntExist);
+            rules[i].doesntExist = ruleBool ? 1 : 0;
+
+            ruleBool = env->GetBooleanField(jRejectRules, jf_exists);
+            rules[i].exists = ruleBool ? 1 : 0;
+
+            rules[i].givenVersion = env->GetLongField(jRejectRules, jf_givenVersion);
+
+            ruleBool = env->GetBooleanField(jRejectRules, jf_versionLeGiven);
+            rules[i].versionLeGiven = ruleBool ? 1 : 0;
+
+            ruleBool = env->GetBooleanField(jRejectRules, jf_versionNeGiven);
+            rules[i].versionNeGiven = ruleBool ? 1 : 0;
+        }
+        objects[i].construct(tableId, keyData, keyLength, valueData, valueLength, &rules[i]);
+        requests[i] = objects[i].get();
+    }
+    try {
+        ramcloud->multiWrite(requests, requestNum);
+    } EXCEPTION_CATCHER(NULL);
+ 
+    const static jclass jc_RcObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$MultiWriteRspObject"));
+    check_null(jc_RcObject, "FindClass failed");
+    const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
+                                        "<init>",
+                                        "(L" PACKAGE_PATH "JRamCloud;IJ)V");
+
+    jobjectArray outJNIArray = env->NewObjectArray(requestNum, jc_RcObject , NULL);
+    check_null(outJNIArray, "NewObjectArray failed");
+    
+    for (int i = 0 ; i < requestNum ; i++) {
+        jobject obj = env->NewObject(jc_RcObject, jm_init, jRamCloud, objects[i]->status, objects[i]->version);
+        check_null(obj, "NewObject failed");
+        env->SetObjectArrayElement(outJNIArray, i, obj);
+        env->ReleaseByteArrayElements(jKey[i], (jbyte *)requests[i]->key, JNI_ABORT);
+        env->ReleaseByteArrayElements(jValue[i], (jbyte *)requests[i]->value, JNI_ABORT);
+        objects[i].destroy();
+    }
+    return outJNIArray;
+}