Fixes to RAMCloud Java bindings

- Fix FindBugs warnings
- Change RejectRule to be a static class
- Change nested Exception classes to be static classes
- Changed RejectRules methods to clarify its roll, and allow chaining
- Represent RejectRules flags as bits in int to decrease number of JNI crossing
- multiWrite value size parameter needs to be int to match C++ decl.
- Add conditional read, write, remove
- Deprecate writeRule method
- Fixed RejectRules related Exception class inheritance
- Declare throws on conditional operations

Change-Id: I07c3a2111d06ca051a29415224d59b9bfa7a4ddb
diff --git a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
index 4694c4e..41d87a5 100644
--- a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
+++ b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
@@ -25,6 +25,12 @@
 /// We will need this when using FindClass, etc.
 #define PACKAGE_PATH "edu/stanford/ramcloud/"
 
+// Java RejectRules flags bit index
+const static int DoesntExist = 1;
+const static int Exists = 1 << 1;
+const static int VersionLeGiven = 1 << 2;
+const static int VersionNeGiven = 1 << 3;
+
 #define check_null(var, msg)                                                \
     if (var == NULL) {                                                      \
         throw Exception(HERE, "JRamCloud: NULL returned: " msg "\n");       \
@@ -34,7 +40,7 @@
  * 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. 
+ * ReleaseStringUTFChars dance.
  */
 class JStringGetter {
   public:
@@ -45,14 +51,14 @@
     {
         check_null(string, "GetStringUTFChars failed");
     }
-    
+
     ~JStringGetter()
     {
         if (string != NULL)
             env->ReleaseStringUTFChars(jString, string);
     }
 
-  private:    
+  private:
     JNIEnv* env;
     jstring jString;
 
@@ -76,7 +82,7 @@
     {
         check_null(pointer, "GetByteArrayElements failed");
     }
-    
+
     ~JByteArrayGetter()
     {
         if (pointer != NULL) {
@@ -86,7 +92,7 @@
         }
     }
 
-  private:    
+  private:
     JNIEnv* env;
     jbyteArray jByteArray;
 
@@ -137,7 +143,7 @@
 {
     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));    
+    return reinterpret_cast<TableEnumerator*>(env->GetLongField(jTableEnumerator, fieldId));
 }
 
 static void
@@ -146,37 +152,41 @@
     // 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$";
+    string fullName(PACKAGE_PATH "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");
+    env->ThrowNew(cls, "");
+}
 
-    jstring jString = env->NewStringUTF("");
-    check_null(jString, "NewStringUTF failed");
+static void
+setRejectRules(JNIEnv* env, jobject jRejectRules, RejectRules& rules)
+{
+    const static jclass jc_RejectRules = (jclass) env->NewGlobalRef(
+            env->FindClass(PACKAGE_PATH "JRamCloud$RejectRules"));
+    static const jfieldID jf_flags = env->GetFieldID(jc_RejectRules, "flags", "I");
+    check_null(jf_flags, "flags field ID is null");
+    static const jfieldID jf_givenVersion = env->GetFieldID(jc_RejectRules,
+            "givenVersion", "J");
+    check_null(jf_givenVersion, "givenVersion field ID is null");
 
-    jthrowable exception = reinterpret_cast<jthrowable>(
-        env->NewObject(cls, methodId, jRamCloud, jString));
-    check_null(exception, "NewObject failed");
-
-    env->Throw(exception);
+    const jint rejectFlag = env->GetIntField(jRejectRules, jf_flags);
+    rules.doesntExist = (rejectFlag & DoesntExist) != 0;
+    rules.exists = (rejectFlag & Exists) != 0;
+    rules.versionLeGiven = (rejectFlag & VersionLeGiven) != 0;
+    rules.versionNeGiven = (rejectFlag & VersionNeGiven) != 0;
+    if (rules.versionLeGiven || rules.versionNeGiven) {
+        rules.givenVersion = env->GetLongField(jRejectRules, jf_givenVersion);
+    }
 }
 
 /**
  * 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. 
+ * 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,
@@ -210,7 +220,7 @@
  * Method:    connect
  * Signature: (Ljava/lang/String;)J
  */
-JNIEXPORT jlong 
+JNIEXPORT jlong
 JNICALL Java_edu_stanford_ramcloud_JRamCloud_connect(JNIEnv *env,
                                jclass jRamCloud,
                                jstring coordinatorLocator)
@@ -219,7 +229,7 @@
     RamCloud* ramcloud = NULL;
     try {
         ramcloud = new RamCloud(locator.string);
-    } EXCEPTION_CATCHER(NULL);
+    } EXCEPTION_CATCHER(reinterpret_cast<jlong>(NULL));
     return reinterpret_cast<jlong>(ramcloud);
 }
 
@@ -350,20 +360,61 @@
                           static_cast<jlong>(version));
 }
 
+// Workaround for javah generating incorrect signature for inner class
+// 00024 is an escaped signature for $ character
+#ifdef __cplusplus
+extern "C" {
+#endif
+JNIEXPORT jobject JNICALL Java_edu_stanford_ramcloud_JRamCloud_read__J_3BLedu_stanford_ramcloud_JRamCloud_00024RejectRules_2
+  (JNIEnv *, jobject, jlong, jbyteArray, jobject);
+#ifdef __cplusplus
+}
+#endif
+
 /*
  * Class:     edu_stanford_ramcloud_JRamCloud
  * Method:    read
- * Signature: (J[BLJRamCloud/RejectRules;)LJRamCloud/Object;
+ * Signature: (J[BLedu/stanford/ramcloud/JRamCloud/RejectRules;)Ledu/stanford/ramcloud/JRamCloud/Object;
  */
 JNIEXPORT jobject
-JNICALL Java_edu_stanford_ramcloud_JRamCloud_read__J_3BLJRamCloud_RejectRules_2(JNIEnv *env,
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_read__J_3BLedu_stanford_ramcloud_JRamCloud_00024RejectRules_2(JNIEnv *env,
                                                           jobject jRamCloud,
                                                           jlong jTableId,
                                                           jbyteArray jKey,
                                                           jobject jRejectRules)
 {
-    // XXX-- implement me by generalising the other read() method.
-    return NULL;
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    JByteArrayReference key(env, jKey);
+
+    Buffer buffer;
+    uint64_t version;
+    RejectRules rules = {};
+    setRejectRules(env, jRejectRules, rules);
+
+    try {
+        ramcloud->read(jTableId, key.pointer, key.length, &buffer, &rules, &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>",
+                                          "([B[BJ)V");
+    check_null(methodId, "GetMethodID failed");
+
+    return env->NewObject(cls,
+                          methodId,
+                          jKey,
+                          jValue,
+                          static_cast<jlong>(version));
 }
 
 /*
@@ -372,7 +423,7 @@
  * Signature: ([Ledu/stanford/ramcloud/JRamCloud$multiReadObject;I)[Ledu/stanford/ramcloud/JRamCloud$Object;
  */
 JNIEXPORT jobjectArray
-JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiRead(JNIEnv *env,   
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiRead(JNIEnv *env,
                                                         jobject jRamCloud,
 							jlongArray jTableId,
 							jobjectArray jKeyData,
@@ -390,7 +441,7 @@
     jbyte* keyData[jrequestNum];
 
     for (int i = 0 ; i < jrequestNum ; i++){
-        jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i); 
+        jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i);
 
         env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
 
@@ -400,7 +451,7 @@
         env->GetLongArrayRegion(jTableId, i, 1, &tableId);
         objects[i].tableId = tableId;
         objects[i].key = keyData[i];
-	objects[i].keyLength = keyLength;
+        objects[i].keyLength = keyLength;
         objects[i].value = &values[i];
         requests[i] = &objects[i];
     }
@@ -408,7 +459,7 @@
     try {
         ramcloud->multiRead(requests, jrequestNum);
     } 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,
@@ -417,7 +468,7 @@
 
     jobjectArray outJNIArray = env->NewObjectArray(jrequestNum, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
-    
+
     for (int i = 0 ; i < jrequestNum ; i++) {
 	if (objects[i].status == 0) {
 	    jbyteArray jValue = env->NewByteArray(values[i].get()->getTotalLength());
@@ -478,12 +529,13 @@
                                                            jbyteArray jKey,
                                                            jobject jRejectRules)
 {
-    // XXX- handle RejectRules
     RamCloud* ramcloud = getRamCloud(env, jRamCloud);
     JByteArrayReference key(env, jKey);
+    RejectRules rules = {};
+    setRejectRules(env, jRejectRules, rules);
     uint64_t version;
     try {
-        ramcloud->remove(jTableId, key.pointer, key.length, NULL, &version);
+        ramcloud->remove(jTableId, key.pointer, key.length, &rules, &version);
     } EXCEPTION_CATCHER(-1);
     return static_cast<jlong>(version);
 }
@@ -514,98 +566,59 @@
     return static_cast<jlong>(version);
 }
 
+// Workaround for javah generating incorrect signature for inner class
+// 00024 is an escaped signature for $ character
+#ifdef __cplusplus
+extern "C" {
+#endif
+JNIEXPORT jlong JNICALL Java_edu_stanford_ramcloud_JRamCloud_write__J_3B_3BLedu_stanford_ramcloud_JRamCloud_00024RejectRules_2
+  (JNIEnv *, jobject, jlong, jbyteArray, jbyteArray, jobject);
+#ifdef __cplusplus
+}
+#endif
+
 /*
  * Class:     edu_stanford_ramcloud_JRamCloud
  * Method:    write
- * Signature: (J[B[BLJRamCloud/RejectRules;)J
+ * Signature: (J[B[BLedu/stanford/ramcloud/JRamCloud/RejectRules;)J
  */
 JNIEXPORT jlong
-JNICALL Java_edu_stanford_ramcloud_JRamCloud_write__J_3B_3BLJRamCloud_RejectRules_2(JNIEnv *env,
+JNICALL Java_edu_stanford_ramcloud_JRamCloud_write__J_3B_3BLedu_stanford_ramcloud_JRamCloud_00024RejectRules_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);
+    return Java_edu_stanford_ramcloud_JRamCloud_writeRule(env, jRamCloud, jTableId, jKey, jValue, jRejectRules);
 }
 
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    writeRule
+ * Signature: (J[B[BLedu/stanford/ramcloud/JRamCloud/RejectRules;)J
+ */
 JNIEXPORT jlong
 JNICALL Java_edu_stanford_ramcloud_JRamCloud_writeRule(JNIEnv *env,
         jobject jRamCloud,
         jlong jTableId,
         jbyteArray jKey,
         jbyteArray jValue,
-        jobject jRejectRules) {
+        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;
+    setRejectRules(env, jRejectRules, rules);
+    uint64_t version;
     try {
         ramcloud->write(jTableId,
                 key.pointer, key.length,
                 value.pointer, value.length,
                 &rules,
                 &version);
-    }
-    EXCEPTION_CATCHER(-1);
+    } EXCEPTION_CATCHER(-1);
     return static_cast<jlong> (version);
 }
 
@@ -614,14 +627,14 @@
  * Method:    init
  * Signature: (J)V
  */
-JNIEXPORT jlong JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_init(JNIEnv *env, 
-                                                                                      jobject jTableEnumerator, 
+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));
 }
 
@@ -630,7 +643,7 @@
  * Method:    hasNext
  * Signature: ()Z
  */
-JNIEXPORT jboolean JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_hasNext( JNIEnv *env, 
+JNIEXPORT jboolean JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_hasNext( JNIEnv *env,
                                                                                               jobject jTableEnumerator)
 {
     TableEnumerator* tableEnum = getTableEnumerator(env, jTableEnumerator);
@@ -642,13 +655,12 @@
  * Method:    next
  * Signature: ()Ledu/stanford/ramcloud/JRamCloud/Object;
  */
-JNIEXPORT jobject JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_next( JNIEnv *env, 
+JNIEXPORT jobject JNICALL Java_edu_stanford_ramcloud_JRamCloud_00024TableEnumerator_next( JNIEnv *env,
                                                                                           jobject jTableEnumerator)
 {
     TableEnumerator* tableEnum = getTableEnumerator(env, jTableEnumerator);
 
-    if(tableEnum->hasNext()) 
-    {
+    if (tableEnum->hasNext()) {
         uint32_t size = 0;
         const void* buffer = 0;
         uint64_t version = 0;
@@ -658,7 +670,7 @@
 
         jbyteArray jKey = env->NewByteArray(object.getKeyLength());
         jbyteArray jValue = env->NewByteArray(object.getDataLength());
-        
+
         JByteArrayGetter key(env, jKey);
         JByteArrayGetter value(env, jValue);
 
@@ -666,7 +678,7 @@
         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,
@@ -677,9 +689,10 @@
                               methodId,
                               jKey,
                               jValue,
-                              static_cast<jlong>(version));        
-    } else 
+                              static_cast<jlong>(version));
+    } else {
         return NULL;
+    }
 }
 
 /*
@@ -694,28 +707,25 @@
 
     RamCloud* ramcloud = getRamCloud(env, jRamCloud);
 
-    Buffer state;
-    Buffer objects;
-    bool done = false;
-    uint64_t version = 0;
-
-    uint32_t nextOffset = 0;
-
     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>",
                                         "([B[BJ)V");
     check_null(jm_init, "GetMethodID failed");
-    
+
     const static jclass jc_RcTableObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$TableEnumeratorObject"));
     check_null(jc_RcTableObject, "FindClass failed");
     const static jmethodID jm_TableEnumeratorObject_init = env->GetMethodID(jc_RcTableObject,
                                         "<init>",
-                                        "([Ledu/stanford/ramcloud/JRamCloud$Object;J)V");
+                                        "([L" PACKAGE_PATH "JRamCloud$Object;J)V");
     check_null(jm_TableEnumeratorObject_init, "GetMethodID failed");
 
-    
+
+    Buffer state;
+    Buffer objects;
+    bool done = false;
+
     while (true) {
         jTabletNextHash = ramcloud->enumerateTable(jTableId, jTabletNextHash, state, objects);
         if (objects.getTotalLength() > 0) {
@@ -724,21 +734,22 @@
         if (objects.getTotalLength() == 0 && jTabletNextHash == 0) {
             done = true;
             break;
-        }    
+        }
     }
-    
+
     if (done) {
         return env->NewObject(jc_RcTableObject, jm_TableEnumeratorObject_init, env->NewObjectArray(0, jc_RcObject , NULL), 0);
     }
-    
-    int numOfTable;
-    for (numOfTable = 0; nextOffset < objects.getTotalLength() ; numOfTable++) {
+
+    int numOfObjects = 0;
+    uint32_t nextOffset = 0;
+    for (numOfObjects = 0; nextOffset < objects.getTotalLength() ; numOfObjects++) {
         uint32_t objectSize = *objects.getOffset<uint32_t>(nextOffset);
         nextOffset += downCast<uint32_t>(sizeof(uint32_t));
         nextOffset += objectSize;
     }
-    
-    jobjectArray outJNIArray = env->NewObjectArray(numOfTable, jc_RcObject , NULL);
+
+    jobjectArray outJNIArray = env->NewObjectArray(numOfObjects, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
 
     nextOffset = 0;
@@ -760,7 +771,7 @@
         memcpy(key.pointer, object.getKey(), object.getKeyLength());
         memcpy(value.pointer, object.getData(), object.getDataLength());
 
-        version = object.getVersion();
+        uint64_t version = object.getVersion();
 
         jobject obj = env->NewObject(jc_RcObject, jm_init, jKey, jValue, static_cast<jlong>(version));
         check_null(obj, "NewObject failed");
@@ -769,7 +780,6 @@
     }
 
     return env->NewObject(jc_RcTableObject, jm_TableEnumeratorObject_init, outJNIArray, jTabletNextHash);
-
 }
 
 /*
@@ -777,74 +787,67 @@
  * Method:    multiWrite
  * Signature: ([Ledu/stanford/ramcloud/JRamCloud/MultiWriteObject;)[Ledu/stanford/ramcloud/JRamCloud/MultiWriteRspObject;
  */
-JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env, 
+JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env,
 	                                                                       jobject jRamCloud,
 	                                                                       jlongArray jTableId,
 	                                                                       jobjectArray jKeyData,
 	                                                                       jshortArray jKeyLength,
 	                                                                       jobjectArray jValueData,
-	                                                                       jshortArray jValueLength,
+	                                                                       jintArray jValueLength,
 	                                                                       jint jrequestNum,
 	                                                                       jobjectArray jRules ) {
-    
+
     RamCloud* ramcloud = getRamCloud(env, jRamCloud);
     Tub<MultiWriteObject> objects[jrequestNum];
     MultiWriteObject *requests[jrequestNum];
     RejectRules rules[jrequestNum];
     jbyteArray jKey[jrequestNum];
     jbyteArray jValue[jrequestNum];
-    
+
     jlong tableId;
     jshort keyLength;
-    jshort valueLength;
+    jint valueLength;
     jbyte* keyData[jrequestNum];
     jbyte* valueData[jrequestNum];
-    
+
     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");
-    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");
+    check_null(jf_givenVersion, "givenVersion field ID is null");
+
+    const static jfieldID jf_flags = env->GetFieldID(jc_RejectRules, "flags", "I");
+    check_null(jf_flags, "flags field ID is null");
 
     for (int i = 0; i < jrequestNum; i++) {
-	env->GetLongArrayRegion(jTableId, i, 1, &tableId);
+        env->GetLongArrayRegion(jTableId, i, 1, &tableId);
 
-	env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
-	jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i);
-	keyData[i] = (jbyte *) malloc(keyLength);
-	env->GetByteArrayRegion(jKey[i], 0, keyLength, keyData[i]);
-	
-	env->GetShortArrayRegion(jValueLength, i, 1, &valueLength);
+        env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
+        jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i);
+        keyData[i] = (jbyte *) malloc(keyLength);
+        env->GetByteArrayRegion(jKey[i], 0, keyLength, keyData[i]);
+
+        env->GetIntArrayRegion(jValueLength, i, 1, &valueLength);
         jValue[i] = (jbyteArray)env->GetObjectArrayElement(jValueData, i);
-	valueData[i] = (jbyte *) malloc(valueLength);
-	env->GetByteArrayRegion(jValue[i], 0, valueLength, valueData[i]);
-	
-	jobject jRejectRules = (jbyteArray)env->GetObjectArrayElement(jRules, i);
+        valueData[i] = (jbyte *) malloc(valueLength);
+        env->GetByteArrayRegion(jValue[i], 0, valueLength, valueData[i]);
+
+        jobject jRejectRules = (jbyteArray)env->GetObjectArrayElement(jRules, i);
         rules[i] = {};
 
         if (jRejectRules != NULL) {
-            jboolean ruleBool;
+            const jint flags = env->GetIntField(jRejectRules, jf_flags);
 
-            ruleBool = env->GetBooleanField(jRejectRules, jf_doesntExist);
-            rules[i].doesntExist = ruleBool ? 1 : 0;
+            rules[i].doesntExist = (flags & DoesntExist) != 0;
 
-            ruleBool = env->GetBooleanField(jRejectRules, jf_exists);
-            rules[i].exists = ruleBool ? 1 : 0;
+            rules[i].exists = (flags & Exists) != 0;
 
-            rules[i].givenVersion = env->GetLongField(jRejectRules, jf_givenVersion);
+            rules[i].versionLeGiven = (flags & VersionLeGiven) != 0;
 
-            ruleBool = env->GetBooleanField(jRejectRules, jf_versionLeGiven);
-            rules[i].versionLeGiven = ruleBool ? 1 : 0;
+            rules[i].versionNeGiven = (flags & VersionNeGiven) != 0;
 
-            ruleBool = env->GetBooleanField(jRejectRules, jf_versionNeGiven);
-            rules[i].versionNeGiven = ruleBool ? 1 : 0;
+            if (rules[i].versionLeGiven || rules[i].versionNeGiven) {
+                rules[i].givenVersion = env->GetLongField(jRejectRules, jf_givenVersion);
+            }
         }
         objects[i].construct(tableId, keyData[i], keyLength, valueData[i], valueLength, &rules[i]);
         requests[i] = objects[i].get();
@@ -852,7 +855,7 @@
     try {
         ramcloud->multiWrite(requests, jrequestNum);
     } 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,
@@ -861,13 +864,13 @@
 
     jobjectArray outJNIArray = env->NewObjectArray(jrequestNum, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
-    
+
     for (int i = 0 ; i < jrequestNum ; i++) {
         jobject obj = env->NewObject(jc_RcObject, jm_init, objects[i]->status, objects[i]->version);
         check_null(obj, "NewObject failed");
         env->SetObjectArrayElement(outJNIArray, i, obj);
-	free(keyData[i]);
-	free(valueData[i]);
+        free(keyData[i]);
+        free(valueData[i]);
         objects[i].destroy();
     }
     return outJNIArray;
diff --git a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java
index ad519c8..5b5cea0 100644
--- a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java
+++ b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java
@@ -25,7 +25,6 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -57,6 +56,7 @@
 
 import edu.stanford.ramcloud.JRamCloud;
 import edu.stanford.ramcloud.JRamCloud.MultiWriteObject;
+import edu.stanford.ramcloud.JRamCloud.RejectRules;
 
 public class RamCloudGraph implements IndexableGraph, KeyIndexableGraph, TransactionalGraph, Serializable {
     private final static Logger log = LoggerFactory.getLogger(RamCloudGraph.class);
@@ -247,9 +247,9 @@
 	    }
 	    vertices.add(v);
 	}
-	
+
 	MultiWriteObject mwo = new MultiWriteObject(vertices.size()*2);
-		
+
 	for (int i=0; i < vertices.size(); i++) {
 	    RamCloudVertex v = vertices.get(i);
 	    mwo.setObject(i*2, vertTableId, v.rcKey, ByteBuffer.allocate(0).array(), null);
@@ -320,8 +320,8 @@
 		    log.error("Got an exception while serializing element's property map", e);
 		    return;
 		}
-		JRamCloud.RejectRules rules = rcClient.new RejectRules();
-		rules.setNeVersion(instanceEntry.version);
+		JRamCloud.RejectRules rules = new RejectRules();
+		rules.rejectIfNeVersion(instanceEntry.version);
 		try {
 		    rcClient.writeRule(instanceTableId, "nextInstanceId".getBytes(), rcValue, rules);
 		    instanceId = curInstanceId;
diff --git a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudIndex.java b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudIndex.java
index 634df25..e62b2d4 100644
--- a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudIndex.java
+++ b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudIndex.java
@@ -38,6 +38,7 @@
 import com.tinkerpop.blueprints.impls.ramcloud.RamCloudGraphProtos.IndexBlob.Builder;
 
 import edu.stanford.ramcloud.JRamCloud;
+import edu.stanford.ramcloud.JRamCloud.RejectRules;
 
 // FIXME Index instance should be representing an Index table, not a IndexTable K-V pair
 public class RamCloudIndex<T extends Element> implements Index<T>, Serializable {
@@ -91,8 +92,8 @@
 	    PerfMon pm = PerfMon.getInstance();
 	    try {
 		JRamCloud rcClient = graph.getRcClient();
-		JRamCloud.RejectRules rules = rcClient.new RejectRules();
-		rules.setExists();
+		JRamCloud.RejectRules rules = new RejectRules();
+		rules.rejectIfExists();
 
 		pm.indexwrite_start("RamCloudIndex create()");
 		rcClient.writeRule(tableId, rcKey, ByteBuffer.allocate(0).array(), rules);
diff --git a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java
index 03c4ba3..a6a5320 100644
--- a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java
+++ b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java
@@ -256,11 +256,11 @@
 				}
 
 				EdgeListProtoBuf edgeList = buildProtoBufFromEdgeSet(edges);
-				JRamCloud.RejectRules rules = rcClient.new RejectRules();
+				JRamCloud.RejectRules rules = new RejectRules();
 				if ( expected_version == 0L ) {
-					rules.setExists();
+					rules.rejectIfExists();
 				} else {
-					rules.setNeVersion(expected_version);
+					rules.rejectIfNeVersion(expected_version);
 				}
 				pm.write_start("RAMCloudVertex updateEdgeAdjList()");
 				long updated_version = rcClient.writeRule(graph.vertTableId, rcKey, edgeList.toByteArray(), rules);
@@ -490,7 +490,7 @@
 		// TODO: Existence check costs extra (presently 2 reads), could use option to turn on/off
 		if (!exists()) {
 			PerfMon pm = PerfMon.getInstance();
-			JRamCloud vertTable = graph.getRcClient();			
+			JRamCloud vertTable = graph.getRcClient();
 			MultiWriteObject mwo = new MultiWriteObject(2);
 			mwo.setObject(1, graph.vertTableId, rcKey, ByteBuffer.allocate(0).array(), null);
 			mwo.setObject(2, graph.vertPropTableId, rcKey, ByteBuffer.allocate(0).array(), null);
diff --git a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudWrite.java b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudWrite.java
index 69eed7f..030d088 100644
--- a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudWrite.java
+++ b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudWrite.java
@@ -1,6 +1,8 @@
 package com.tinkerpop.blueprints.impls.ramcloud;
 
 import edu.stanford.ramcloud.JRamCloud;
+import edu.stanford.ramcloud.JRamCloud.RejectRules;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -10,14 +12,14 @@
 	INDEXWRITE
     }
     private final static Logger log = LoggerFactory.getLogger(RamCloudGraph.class);
-    
+
     public static boolean writeWithRules(long tableId, byte[] rcKey, byte[] rcValue, long expectedVersion, RamCloudGraph graph, PerfMonEnum perfMonKind) {
-	JRamCloud.RejectRules rules = graph.getRcClient().new RejectRules();
+	JRamCloud.RejectRules rules = new RejectRules();
 
 	if (expectedVersion == 0) {
-	    rules.setExists();
+	    rules.rejectIfExists();
 	} else {
-	    rules.setNeVersion(expectedVersion);
+	    rules.rejectIfNeVersion(expectedVersion);
 	}
 
 	PerfMon pm = PerfMon.getInstance();
diff --git a/src/main/java/edu/stanford/ramcloud/JRamCloud.java b/src/main/java/edu/stanford/ramcloud/JRamCloud.java
index 63b0748..83fdd44 100644
--- a/src/main/java/edu/stanford/ramcloud/JRamCloud.java
+++ b/src/main/java/edu/stanford/ramcloud/JRamCloud.java
@@ -14,6 +14,7 @@
  */
 
 package edu.stanford.ramcloud;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.LinkedList;
 
@@ -35,6 +36,8 @@
         System.loadLibrary("edu_stanford_ramcloud_JRamCloud");
     }
 
+    public static final long VERSION_NONEXISTENT = 0L;
+
     /// Pointer to the underlying C++ RAMCloud object associated with this
     /// object.
     private long ramcloudObjectPointer = 0;
@@ -42,85 +45,106 @@
     /**
      * See src/RejectRules.h.
      */
-    public class RejectRules {
-	private long givenVersion;
-        private boolean doesntExist;
-        private boolean exists;
-        private boolean versionLeGiven;
-        private boolean versionNeGiven;
+    public static class RejectRules {
+        // these needs to be in sync with JNI code;
+        public static final int DoesntExist = 1;
+        public static final int Exists = 1 << 1;
+        public static final int VersionLeGiven = 1 << 2;
+        public static final int VersionNeGiven = 1 << 3;
+
+        protected long givenVersion;
+        protected int flags;
 
         public RejectRules() {
-            this.givenVersion = -1;
-            this.exists = this.doesntExist = this.versionLeGiven = this.versionNeGiven = false;
+            clear();
         }
 
-        public void setLeVersion(long version) {
+        public void clear() {
+            this.givenVersion = VERSION_NONEXISTENT;
+            this.flags = 0x0;
+        }
+
+        public RejectRules set(int flags, long version) {
+            this.flags = flags;
+            this.givenVersion = version;
+            return this;
+        }
+
+        public RejectRules set(int flags) {
+            return set(flags,VERSION_NONEXISTENT);
+        }
+
+        public RejectRules rejectIfLeVersion(long version) {
             setVersion(version);
-            this.versionLeGiven = true;
+            this.flags |= VersionLeGiven;
+            return this;
         }
 
-        public void setExists() {
-            this.exists = true;
+        public RejectRules rejectIfExists() {
+            this.flags |= Exists;
+            return this;
         }
 
-        public void setDoesntExists() {
-            this.doesntExist = true;
+        public RejectRules rejectIfDoesntExists() {
+            this.flags |= DoesntExist;
+            return this;
         }
 
-        public void setNeVersion(long version) {
+        public RejectRules rejectIfNeVersion(long version) {
             setVersion(version);
-            this.versionNeGiven = true;
+            this.flags |= VersionNeGiven;
+            return this;
         }
 
         private void setVersion(long version) {
             this.givenVersion = version;
-        }	
+        }
     }
-    
+
     public static class MultiReadObject {
         public long[] tableId;
         public byte[] key[];
-	public short[] keyLength;
-        
+        public short[] keyLength;
+
         public MultiReadObject(int size){
             this.tableId = new long[size];
             this.key = new byte[size][];
-	    this.keyLength = new short[size];
+            this.keyLength = new short[size];
         }
-	
+
 	public void setObject(int num, long tableId, byte key[]){
             this.tableId[num] = tableId;
             this.key[num] = key;
-	    this.keyLength[num] = (short) this.key[num].length;
+            this.keyLength[num] = (short) this.key[num].length;
 	}
     }
-    
+
     public static class MultiWriteObject {
         public long[] tableId;
         public byte[] key[];
-	public short[] keyLength;
+        public short[] keyLength;
         public byte[] value[];
-	public short[] valueLength;
+        public int[] valueLength;
         public RejectRules[] rules;
 
         public MultiWriteObject(int size) {
             this.tableId = new long[size];
             this.key = new byte[size][];
-	    this.keyLength = new short[size];
+            this.keyLength = new short[size];
             this.value = new byte[size][];
-	    this.valueLength = new short[size];
+            this.valueLength = new int[size];
             this.rules = new RejectRules[size];
         }
-	
+
 	public void setObject(int num, long tableId, byte key[], byte value[], RejectRules rules){
 	    this.tableId[num] = tableId;
 	    this.key[num] = key;
 	    this.keyLength[num] = (short) key.length;
 	    this.value[num] = value;
-	    this.valueLength[num] = (short) value.length;
+	    this.valueLength[num] = value.length;
 	    this.rules[num] = rules;
 	}
-	
+
     }
 
     public static class MultiWriteRspObject {
@@ -159,27 +183,27 @@
         public String
         getKey()
         {
-            return new String(key);
+            return new String(key,StandardCharsets.UTF_8);
         }
 
         public String
         getValue()
         {
-            return new String(value);
+            return new String(value,StandardCharsets.UTF_8);
         }
 
         final public byte[] key;
         final public byte[] value;
         final public long version;
     }
-    
+
     public static class TableEnumeratorObject {
 	TableEnumeratorObject(Object[] _object, long _nextHash)
-        {
+	{
 	    object = _object;
 	    nextHash = _nextHash;
 	}
-	
+
 	final public Object[] object;
 	final public long nextHash;
     }
@@ -187,32 +211,32 @@
     public class TableEnumerator {
         private long tableEnumeratorObjectPointer = 0;
         private long ramCloudObjectPointer = 0;
-        
+
         public TableEnumerator(long tableId)
         {
             ramCloudObjectPointer = ramcloudObjectPointer;
             tableEnumeratorObjectPointer = init(tableId);
         }
-        
+
         private native long init(long tableId);
         public native boolean hasNext();
         public native Object next();
     }
 
     public class TableEnumerator2 {
-        
+
 	protected long tableId;
 	protected LinkedList<JRamCloud.Object> rcobjs = null;
 	protected long nextHash = 0;
 	protected boolean done = false;
-	
+
         public TableEnumerator2(long tableId)
         {
 	    this.tableId = tableId;
 	    rcobjs = new LinkedList<>();
         }
         public boolean hasNext() {
-	    if (rcobjs.isEmpty()) 
+	    if (rcobjs.isEmpty())
 	    {
 		if (done) {
 		    return false;
@@ -229,13 +253,13 @@
 	    }
 	    return true;
 	}
-	
-	public Object next() 
+
+	public Object next()
 	{
 	    return rcobjs.pop();
 	}
     }
-    
+
     /**
      * Connect to the RAMCloud cluster specified by the given coordinator's
      * service locator string. This causes the JNI code to instantiate the
@@ -265,7 +289,8 @@
      * object. The user really should have called disconnect, but in case
      * they did not, be sure to clean up after them.
      */
-    public void
+    @Override
+    protected void
     finalize()
     {
         System.err.println("warning: JRamCloud::disconnect() was not called " +
@@ -280,34 +305,36 @@
     public Object
     read(long tableId, String key)
     {
-        return read(tableId, key.getBytes());
+        return read(tableId, key.getBytes(StandardCharsets.UTF_8));
     }
 
     /**
      * Convenience read() wrapper that take a String key argument.
+     * @throws RejectRulesException
      */
     public Object
-    read(long tableId, String key, RejectRules rules)
+    read(long tableId, String key, RejectRules rules) throws RejectRulesException
     {
-        return read(tableId, key.getBytes(), rules);
+        return read(tableId, key.getBytes(StandardCharsets.UTF_8), rules);
     }
-    
+
     /**
      * Convenience remove() wrapper that take a String key argument.
      */
     public long
     remove(long tableId, String key)
     {
-        return remove(tableId, key.getBytes());
+        return remove(tableId, key.getBytes(StandardCharsets.UTF_8));
     }
 
     /**
      * Convenience remove() wrapper that take a String key argument.
+     * @throws RejectRulesException
      */
     public long
-    remove(long tableId, String key, RejectRules rules)
+    remove(long tableId, String key, RejectRules rules) throws RejectRulesException
     {
-        return remove(tableId, key.getBytes(), rules);
+        return remove(tableId, key.getBytes(StandardCharsets.UTF_8), rules);
     }
 
     /**
@@ -316,16 +343,17 @@
     public long
     write(long tableId, String key, String value)
     {
-        return write(tableId, key.getBytes(), value.getBytes());
+        return write(tableId, key.getBytes(StandardCharsets.UTF_8), value.getBytes(StandardCharsets.UTF_8));
     }
 
     /**
      * Convenience write() wrapper that take String key and value arguments.
+     * @throws RejectRulesException
      */
     public long
-    write(long tableId, String key, String value, RejectRules rules)
+    write(long tableId, String key, String value, RejectRules rules) throws RejectRulesException
     {
-        return write(tableId, key.getBytes(), value.getBytes(), rules);
+        return write(tableId, key.getBytes(StandardCharsets.UTF_8), value.getBytes(StandardCharsets.UTF_8), rules);
     }
 
     /**
@@ -335,19 +363,20 @@
     public long
     write(long tableId, String key, byte[] value)
     {
-        return write(tableId, key.getBytes(), value);
+        return write(tableId, key.getBytes(StandardCharsets.UTF_8), value);
     }
 
     /**
      * Convenience write() wrapper that takes a String key and a byte[] value
      * argument.
+     * @throws RejectRulesException
      */
     public long
-    write(long tableId, String key, byte[] value, RejectRules rules)
+    write(long tableId, String key, byte[] value, RejectRules rules) throws RejectRulesException
     {
-        return write(tableId, key.getBytes(), value, rules);
+        return write(tableId, key.getBytes(StandardCharsets.UTF_8), value, rules);
     }
-    
+
     private static native long connect(String coordinatorLocator);
     private static native void disconnect(long ramcloudObjectPointer);
 
@@ -356,79 +385,79 @@
     public native void dropTable(String name);
     public native long getTableId(String name);
     public native Object read(long tableId, byte[] key);
-    public native Object read(long tableId, byte[] key, RejectRules rules);
+    public native Object read(long tableId, byte[] key, RejectRules rules) throws RejectRulesException;
     public native Object[] multiRead(long[] tableId, byte[] keydata[], short[] keyDataSize, int requestNum);
     public native long remove(long tableId, byte[] key);
-    public native long remove(long tableId, byte[] key, RejectRules rules);
+    public native long remove(long tableId, byte[] key, RejectRules rules) throws RejectRulesException;
     public native long write(long tableId, byte[] key, byte[] value);
-    public native long write(long tableId, byte[] key, byte[] value, RejectRules rules);
+    public native long write(long tableId, byte[] key, byte[] value, RejectRules rules) throws RejectRulesException;
+    @Deprecated
     public native long writeRule(long tableId, byte[] key, byte[] value, RejectRules rules);
-    public native MultiWriteRspObject[] multiWrite(long[] tableId, byte[] key[], short[] keyDataSize, byte[] value[], short[] valueDataSize, int requestNum, RejectRules[] rules);
+    public native MultiWriteRspObject[] multiWrite(long[] tableId, byte[] key[], short[] keyDataSize, byte[] value[], int[] valueDataSize, int requestNum, RejectRules[] rules);
     public native TableEnumeratorObject getTableObjects(long tableId, long nextHash);
 
     /*
      * The following exceptions may be thrown by the JNI functions:
      */
 
-    public class TableDoesntExistException extends Exception {
+    public static class TableDoesntExistException extends RuntimeException {
         public TableDoesntExistException(String message)
         {
             super(message);
         }
     }
 
-    public class ObjectDoesntExistException extends Exception {
+    public static class ObjectDoesntExistException extends RejectRulesException {
         public ObjectDoesntExistException(String message)
         {
             super(message);
         }
     }
 
-    public class ObjectExistsException extends Exception {
+    public static class ObjectExistsException extends RejectRulesException {
         public ObjectExistsException(String message)
         {
             super(message);
         }
     }
 
-    public class WrongVersionException extends Exception {
+    public static class WrongVersionException extends RejectRulesException {
         public WrongVersionException(String message)
         {
             super(message);
         }
     }
-    
-    public class InvalidObjectException extends Exception {
+
+    public static class InvalidObjectException extends RuntimeException {
         public InvalidObjectException(String message) {
             super(message);
         }
     }
-    
-    public class RejectRulesException extends Exception {
+
+    public static class RejectRulesException extends Exception {
         public RejectRulesException(String message) {
             super(message);
         }
     }
-    
+
     public static void tableEnumeratorTest(JRamCloud ramcloud) {
         long startTime = 0;
         for (int x = 0 ; x < 2 ; x ++){
         for(int N = 1000; N < 10000; N += 1000) {
             long EnumerateTesttable = ramcloud.createTable("EnumerateTest");
             for(int i = 0 ; i < N ; ++i) {
-                String key = new Integer(i).toString();
-                ramcloud.write(EnumerateTesttable, key.getBytes(), "Hello, World!".getBytes());
+                String key = String.valueOf(i);
+                ramcloud.write(EnumerateTesttable, key.getBytes(StandardCharsets.UTF_8), "Hello, World!".getBytes(StandardCharsets.UTF_8));
             }
 
-            MultiReadObject mread[] = new MultiReadObject[N];
             long tableIdList[] = new long[N];
             byte[] keydata[] = new byte[N][];
             short keydataSize[] = new short[N];
-	    startTime = System.nanoTime();
+            startTime = System.nanoTime();
             for (int j = 0 ; j < N ; ++j) {
                 tableIdList[j] = EnumerateTesttable;
-                String key = new Integer(j).toString();
-                keydata[j] = key.getBytes();
+                String key = String.valueOf(j);
+                keydata[j] = key.getBytes(StandardCharsets.UTF_8);
                 keydataSize[j] = (short) keydata[j].length;
             }
             JRamCloud.Object out[] = ramcloud.multiRead(tableIdList, keydata, keydataSize, N);
@@ -448,13 +477,13 @@
                     System.out.println("tableEnumerator object: key = [" + tableEntry.getKey() + "], value = [" + tableEntry.getValue() + "]");
                 }
             }
-	    System.out.println("old TableEnumerator : " + N + " Time : " + (System.nanoTime()-startTime));
+            System.out.println("old TableEnumerator : " + N + " Time : " + (System.nanoTime()-startTime));
 
             startTime = System.nanoTime();
             JRamCloud.TableEnumerator2 tableEnum2 = ramcloud.new TableEnumerator2(EnumerateTesttable);
-	    while (tableEnum2.hasNext()) {
-		Object tableEntry2 = tableEnum2.next();
-		if (tableEntry2 != null) {
+            while (tableEnum2.hasNext()) {
+                Object tableEntry2 = tableEnum2.next();
+                if (tableEntry2 != null) {
                     System.out.println("tableEnumerator2 object: key = [" + tableEntry2.getKey() + "], value = [" + tableEntry2.getValue() + "]");
                 }
             }
@@ -492,15 +521,16 @@
         }
 
         ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue");
-        
+
         long before = System.nanoTime();
         for (int i = 0; i < 1000; i++) {
+            @SuppressWarnings("unused")
             JRamCloud.Object unused = ramcloud.read(tableId, "thisIsTheKey");
         }
         long after = System.nanoTime();
         System.out.println("Avg read latency: " +
             ((double)(after - before) / 1000 / 1000) + " usec");
-        
+
         // multiRead test
         long tableId4 = ramcloud.createTable("table4");
         System.out.println("table4 id " + tableId4);
@@ -516,9 +546,9 @@
 
 	MultiReadObject mr = new MultiReadObject(2);
 	MultiWriteObject mw = new MultiWriteObject(2);
-	
-	mr.setObject(0, tableId4, "object1-1".getBytes());
-	mr.setObject(1, tableId5, "object2-1".getBytes());
+
+	mr.setObject(0, tableId4, "object1-1".getBytes(StandardCharsets.UTF_8));
+	mr.setObject(1, tableId5, "object2-1".getBytes(StandardCharsets.UTF_8));
 
         JRamCloud.Object out[] = ramcloud.multiRead(mr.tableId, mr.key, mr.keyLength, 2);
         for (int i = 0 ; i < 2 ; i++){
@@ -527,12 +557,12 @@
         }
 
         for (int i = 0; i < 1000; i++) {
-            String key1 = "key1" + new Integer(i).toString();
-            String key2 = "key2" + new Integer(i).toString();
-	    
-	    mw.setObject(0, tableId4, key1.getBytes(), "v0-value".getBytes(), null);
-	    mw.setObject(1, tableId5, key2.getBytes(), "v1".getBytes(), null);
-      	    
+            String key1 = "key1" + String.valueOf(i);
+            String key2 = "key2" + String.valueOf(i);
+
+            mw.setObject(0, tableId4, key1.getBytes(StandardCharsets.UTF_8), "v0-value".getBytes(StandardCharsets.UTF_8), null);
+            mw.setObject(1, tableId5, key2.getBytes(StandardCharsets.UTF_8), "v1".getBytes(StandardCharsets.UTF_8), null);
+
             MultiWriteRspObject[] rsp = ramcloud.multiWrite(mw.tableId, mw.key, mw.keyLength, mw.value, mw.valueLength, 2, mw.rules);
             if (rsp != null) {
                 for (int j = 0; j < rsp.length; j++) {
@@ -541,20 +571,20 @@
             }
         }
         for (int i = 0; i < 1000; i++) {
-            String key1 = "key1" + new Integer(i).toString();
-            String key2 = "key2" + new Integer(i).toString();
+            String key1 = "key1" + String.valueOf(i);
+            String key2 = "key2" + String.valueOf(i);
 
-	    mr.setObject(0, tableId4, key1.getBytes());
-	    mr.setObject(1, tableId5, key2.getBytes());
-	    
+            mr.setObject(0, tableId4, key1.getBytes(StandardCharsets.UTF_8));
+            mr.setObject(1, tableId5, key2.getBytes(StandardCharsets.UTF_8));
+
             out = ramcloud.multiRead(mr.tableId, mr.key, mr.keyLength, 2);
             for (int j = 0; j < 2; j++) {
                 System.out.println("multi read object: key = [" + out[j].getKey() + "], value = [" + out[j].getValue() + "]");
             }
         }
 
-	tableEnumeratorTest(ramcloud);
-	
+        tableEnumeratorTest(ramcloud);
+
         ramcloud.dropTable("table4");
         ramcloud.dropTable("table5");
         ramcloud.dropTable("table6");
diff --git a/src/main/java/net/onrc/onos/datastore/RCObject.java b/src/main/java/net/onrc/onos/datastore/RCObject.java
index 77f5d15..009b584 100644
--- a/src/main/java/net/onrc/onos/datastore/RCObject.java
+++ b/src/main/java/net/onrc/onos/datastore/RCObject.java
@@ -24,7 +24,6 @@
 import edu.stanford.ramcloud.JRamCloud.ObjectDoesntExistException;
 import edu.stanford.ramcloud.JRamCloud.ObjectExistsException;
 import edu.stanford.ramcloud.JRamCloud.RejectRules;
-import edu.stanford.ramcloud.JRamCloud.TableEnumerator;
 import edu.stanford.ramcloud.JRamCloud.TableEnumerator2;
 import edu.stanford.ramcloud.JRamCloud.WrongVersionException;
 
@@ -150,7 +149,7 @@
 
     protected void serializeAndSetValue(Kryo kryo,
 	    Map<Object, Object> javaObject) {
-	
+
 
 	// value
 	byte[] rcTemp = new byte[1024 * 1024];
@@ -176,7 +175,7 @@
 	    Class<T> type) {
 	if (this.value == null)
 	    return null;
-	
+
 	Input input = new Input(this.value);
 	T map = kryo.readObject(input, type);
 	this.propertyMap = map;
@@ -307,7 +306,7 @@
 	JRamCloud rcClient = RCClient.getClient();
 
 	final int reqs = req.size();
-	
+
 	MultiReadObject multiReadObjects = new MultiReadObject(req.size());
 
 	// setup multi-read operation
@@ -426,19 +425,18 @@
 	    WriteOp op = ops.get(i);
 	    RCObject obj = op.getObject();
 
-	    // FIXME JRamCloud.RejectRules definition is messed up
-	    RejectRules rules = rcClient.new RejectRules();
+	    RejectRules rules = new RejectRules();
 
 	    switch (op.getOp()) {
 	    case CREATE:
-		rules.setExists();
+		rules.rejectIfExists();
 		break;
 	    case FORCE_CREATE:
 		// no reject rule
 		break;
 	    case UPDATE:
-		rules.setDoesntExists();
-		rules.setNeVersion(obj.getVersion());
+		rules.rejectIfDoesntExists();
+		rules.rejectIfNeVersion(obj.getVersion());
 		break;
 	    }
 	    multiWriteObjects.setObject(i, obj.getTableId(), obj.getKey(), obj.getValue(), rules);
@@ -465,7 +463,7 @@
 
 	return fail_exists;
     }
-   
+
     public static abstract class ObjectIterator<E extends RCObject> implements
 	    Iterator<E> {
 
@@ -491,7 +489,7 @@
 //	    obj.setValueAndDeserialize(o.value, o.version);
 //	    return obj;
 //	}
-	
+
 	@Deprecated
 	@Override
 	public void remove() {
diff --git a/src/main/java/net/onrc/onos/datastore/RCTable.java b/src/main/java/net/onrc/onos/datastore/RCTable.java
index e74a5a4..0617bf7 100644
--- a/src/main/java/net/onrc/onos/datastore/RCTable.java
+++ b/src/main/java/net/onrc/onos/datastore/RCTable.java
@@ -9,6 +9,7 @@
 import edu.stanford.ramcloud.JRamCloud.ObjectDoesntExistException;
 import edu.stanford.ramcloud.JRamCloud.ObjectExistsException;
 import edu.stanford.ramcloud.JRamCloud.RejectRules;
+import edu.stanford.ramcloud.JRamCloud.RejectRulesException;
 import edu.stanford.ramcloud.JRamCloud.WrongVersionException;
 
 /**
@@ -106,16 +107,21 @@
 
 	JRamCloud rcClient = RCClient.getClient();
 
-	RejectRules rules = rcClient.new RejectRules();
-	rules.setExists();
+	RejectRules rules = new RejectRules();
+	rules.rejectIfExists();
 
-	long updated_version = rcClient.writeRule(this.rcTableId, key, value,
-	        rules);
-	return updated_version;
+	try {
+	    return rcClient.write(this.rcTableId, key, value, rules);
+	} catch (ObjectExistsException e) {
+	    throw e;
+	} catch (RejectRulesException e) {
+	    log.error("Unexpected RejectRulesException", e);
+	    return JRamCloud.VERSION_NONEXISTENT;
+	}
     }
 
     /**
-     * Create a Key-Value entry on table, without existance checking.
+     * Create a Key-Value entry on table, without existence checking.
      *
      * @param key
      * @param value
@@ -140,13 +146,17 @@
 
 	JRamCloud rcClient = RCClient.getClient();
 
-	// FIXME underlying JRamCloud cannot detect "not exist"
-	// RejectRules rules = rcClient.new RejectRules();
-	// rules.setDoesntExists();
-	// JRamCloud.Object rcObj = rcClient.read(this.rcTableId, key, rules);
-	JRamCloud.Object rcObj = rcClient.read(this.rcTableId, key);
-
-	return new Entry(rcObj.key, rcObj.value, rcObj.version);
+	RejectRules rules = new RejectRules();
+	rules.rejectIfDoesntExists();
+	try {
+	    JRamCloud.Object rcObj = rcClient.read(this.rcTableId, key, rules);
+	    return new Entry(rcObj.key, rcObj.value, rcObj.version);
+	} catch (ObjectDoesntExistException e) {
+	    throw e;
+	} catch (RejectRulesException e) {
+	    log.error("Unexpected RejectRulesException", e);
+	    return null;
+	}
     }
 
     /**
@@ -165,13 +175,18 @@
 
 	JRamCloud rcClient = RCClient.getClient();
 
-	RejectRules rules = rcClient.new RejectRules();
-	rules.setDoesntExists();
-	rules.setNeVersion(version);
+	RejectRules rules = new RejectRules();
+	rules.rejectIfDoesntExists();
+	rules.rejectIfNeVersion(version);
 
-	long updated_version = rcClient.writeRule(this.rcTableId, key, value,
-	        rules);
-	return updated_version;
+	try {
+	    return rcClient.write(this.rcTableId, key, value, rules);
+	} catch (ObjectDoesntExistException|WrongVersionException e) {
+	    throw e;
+	} catch (RejectRulesException e) {
+	    log.error("Unexpected RejectRulesException", e);
+	    return JRamCloud.VERSION_NONEXISTENT;
+	}
     }
 
     /**
@@ -187,13 +202,17 @@
 
 	JRamCloud rcClient = RCClient.getClient();
 
-	RejectRules rules = rcClient.new RejectRules();
-	rules.setDoesntExists();
+	RejectRules rules = new RejectRules();
+	rules.rejectIfDoesntExists();
 
-	long updated_version = rcClient.writeRule(this.rcTableId, key, value,
-	        rules);
-	return updated_version;
-
+	try {
+	    return rcClient.write(this.rcTableId, key, value, rules);
+	} catch (ObjectDoesntExistException e) {
+	    throw e;
+	} catch (RejectRulesException e) {
+	    log.error("Unexpected RejectRulesException", e);
+	    return JRamCloud.VERSION_NONEXISTENT;
+	}
     }
 
     /**
@@ -210,13 +229,18 @@
 	    throws ObjectDoesntExistException, WrongVersionException {
 	JRamCloud rcClient = RCClient.getClient();
 
-	// FIXME underlying JRamCloud does not support cond. remove now
-	RejectRules rules = rcClient.new RejectRules();
-	rules.setDoesntExists();
-	rules.setNeVersion(version);
+	RejectRules rules = new RejectRules();
+	rules.rejectIfDoesntExists();
+	rules.rejectIfNeVersion(version);
 
-	long removed_version = rcClient.remove(this.rcTableId, key, rules);
-	return removed_version;
+	try {
+	    return rcClient.remove(this.rcTableId, key, rules);
+	} catch (ObjectDoesntExistException|WrongVersionException e) {
+	    throw e;
+	} catch (RejectRulesException e) {
+	    log.error("Unexpected RejectRulesException", e);
+	    return JRamCloud.VERSION_NONEXISTENT;
+	}
     }
 
     /**