]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/rocksdb/java/rocksjni/portal.h
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / rocksdb / java / rocksjni / portal.h
index a0d1846a65950fc5f24504ccee8173849913284f..70e67653ec44d4ea1b13adb5be5e4fd6067c8ccc 100644 (file)
 #ifndef JAVA_ROCKSJNI_PORTAL_H_
 #define JAVA_ROCKSJNI_PORTAL_H_
 
+#include <algorithm>
 #include <cstring>
-#include <jni.h>
 #include <functional>
 #include <iostream>
 #include <iterator>
+#include <jni.h>
 #include <limits>
 #include <memory>
 #include <string>
 #include "rocksdb/filter_policy.h"
 #include "rocksdb/rate_limiter.h"
 #include "rocksdb/status.h"
+#include "rocksdb/table.h"
 #include "rocksdb/utilities/backupable_db.h"
+#include "rocksdb/utilities/memory_util.h"
 #include "rocksdb/utilities/transaction_db.h"
 #include "rocksdb/utilities/write_batch_with_index.h"
 #include "rocksjni/compaction_filter_factory_jnicallback.h"
 #include "rocksjni/comparatorjnicallback.h"
 #include "rocksjni/loggerjnicallback.h"
+#include "rocksjni/table_filter_jnicallback.h"
+#include "rocksjni/trace_writer_jnicallback.h"
 #include "rocksjni/transaction_notifier_jnicallback.h"
+#include "rocksjni/wal_filter_jnicallback.h"
 #include "rocksjni/writebatchhandlerjnicallback.h"
 
 // Remove macro on windows
 
 namespace rocksdb {
 
-// Detect if jlong overflows size_t
-inline Status check_if_jlong_fits_size_t(const jlong& jvalue) {
-  Status s = Status::OK();
-  if (static_cast<uint64_t>(jvalue) > std::numeric_limits<size_t>::max()) {
-    s = Status::InvalidArgument(Slice("jlong overflows 32 bit value."));
-  }
-  return s;
-}
-
 class JavaClass {
  public:
   /**
@@ -158,11 +155,12 @@ template<class DERIVED> class JavaException : public JavaClass {
   }
 };
 
-// The portal class for org.rocksdb.RocksDB
-class RocksDBJni : public RocksDBNativeClass<rocksdb::DB*, RocksDBJni> {
+// The portal class for java.lang.IllegalArgumentException
+class IllegalArgumentExceptionJni :
+    public JavaException<IllegalArgumentExceptionJni> {
  public:
   /**
-   * Get the Java Class org.rocksdb.RocksDB
+   * Get the Java Class java.lang.IllegalArgumentException
    *
    * @param env A pointer to the Java environment
    *
@@ -171,7 +169,34 @@ class RocksDBJni : public RocksDBNativeClass<rocksdb::DB*, RocksDBJni> {
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/RocksDB");
+    return JavaException::getJClass(env, "java/lang/IllegalArgumentException");
+  }
+
+  /**
+   * Create and throw a Java IllegalArgumentException with the provided status
+   *
+   * If s.ok() == true, then this function will not throw any exception.
+   *
+   * @param env A pointer to the Java environment
+   * @param s The status for the exception
+   *
+   * @return true if an exception was thrown, false otherwise
+   */
+  static bool ThrowNew(JNIEnv* env, const Status& s) {
+    assert(!s.ok());
+    if (s.ok()) {
+      return false;
+    }
+
+    // get the IllegalArgumentException class
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      std::cerr << "IllegalArgumentExceptionJni::ThrowNew/class - Error: unexpected exception!" << std::endl;
+      return env->ExceptionCheck();
+    }
+
+    return JavaException::ThrowNew(env, s.ToString());
   }
 };
 
@@ -472,6 +497,100 @@ class StatusJni : public RocksDBNativeClass<rocksdb::Status*, StatusJni> {
     }
   }
 
+  static std::unique_ptr<rocksdb::Status> toCppStatus(
+      const jbyte jcode_value, const jbyte jsub_code_value) {
+    std::unique_ptr<rocksdb::Status> status;
+    switch (jcode_value) {
+      case 0x0:
+        //Ok
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::OK()));
+        break;
+      case 0x1:
+        //NotFound
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::NotFound(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x2:
+        //Corruption
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::Corruption(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x3:
+        //NotSupported
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::NotSupported(
+                rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x4:
+        //InvalidArgument
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::InvalidArgument(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x5:
+        //IOError
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::IOError(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x6:
+        //MergeInProgress
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::MergeInProgress(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x7:
+        //Incomplete
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::Incomplete(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x8:
+        //ShutdownInProgress
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::ShutdownInProgress(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x9:
+        //TimedOut
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::TimedOut(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0xA:
+        //Aborted
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::Aborted(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0xB:
+        //Busy
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::Busy(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0xC:
+        //Expired
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::Expired(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0xD:
+        //TryAgain
+        status = std::unique_ptr<rocksdb::Status>(
+            new rocksdb::Status(rocksdb::Status::TryAgain(
+              rocksdb::SubCodeJni::toCppSubCode(jsub_code_value))));
+        break;
+      case 0x7F:
+      default:
+        return nullptr;
+    }
+    return status;
+  }
+
   // Returns the equivalent rocksdb::Status for the Java org.rocksdb.Status
   static std::unique_ptr<rocksdb::Status> toCppStatus(JNIEnv* env, const jobject jstatus) {
     jmethodID mid_code = getCodeMethod(env);
@@ -513,14 +632,14 @@ class StatusJni : public RocksDBNativeClass<rocksdb::Status*, StatusJni> {
       return nullptr;
     }
 
-    jbyte jsubCode_value = 0x0;  // None
+    jbyte jsub_code_value = 0x0;  // None
     if (jsubCode != nullptr) {
       jmethodID mid_subCode_value = rocksdb::SubCodeJni::getValueMethod(env);
       if (mid_subCode_value == nullptr) {
         // exception occurred
         return nullptr;
       }
-      jsubCode_value =env->CallByteMethod(jsubCode, mid_subCode_value);
+      jsub_code_value = env->CallByteMethod(jsubCode, mid_subCode_value);
       if (env->ExceptionCheck()) {
         // exception occurred
         if (jcode != nullptr) {
@@ -547,68 +666,8 @@ class StatusJni : public RocksDBNativeClass<rocksdb::Status*, StatusJni> {
       return nullptr;
     }
 
-    std::unique_ptr<rocksdb::Status> status;
-    switch (jcode_value) {
-      case 0x0:
-        //Ok
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::OK()));
-        break;
-      case 0x1:
-        //NotFound
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::NotFound(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x2:
-        //Corruption
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::Corruption(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x3:
-        //NotSupported
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::NotSupported(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x4:
-        //InvalidArgument
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::InvalidArgument(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x5:
-        //IOError
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::IOError(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x6:
-        //MergeInProgress
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::MergeInProgress(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x7:
-        //Incomplete
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::Incomplete(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x8:
-        //ShutdownInProgress
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::ShutdownInProgress(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x9:
-        //TimedOut
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::TimedOut(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0xA:
-        //Aborted
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::Aborted(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0xB:
-        //Busy
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::Busy(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0xC:
-        //Expired
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::Expired(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0xD:
-        //TryAgain
-        status = std::unique_ptr<rocksdb::Status>(new rocksdb::Status(rocksdb::Status::TryAgain(rocksdb::SubCodeJni::toCppSubCode(jsubCode_value))));
-        break;
-      case 0x7F:
-      default:
-        return nullptr;
-    }
+    std::unique_ptr<rocksdb::Status> status =
+        toCppStatus(jcode_value, jsub_code_value);
 
     // delete all local refs
     if (jstate != nullptr) {
@@ -679,7 +738,6 @@ class RocksDBExceptionJni :
    * @return true if an exception was thrown, false otherwise
    */
   static bool ThrowNew(JNIEnv* env, const Status& s) {
-    assert(!s.ok());
     if (s.ok()) {
       return false;
     }
@@ -894,12 +952,11 @@ class RocksDBExceptionJni :
   }
 };
 
-// The portal class for java.lang.IllegalArgumentException
-class IllegalArgumentExceptionJni :
-    public JavaException<IllegalArgumentExceptionJni> {
+// The portal class for java.util.List
+class ListJni : public JavaClass {
  public:
   /**
-   * Get the Java Class java.lang.IllegalArgumentException
+   * Get the Java Class java.util.List
    *
    * @param env A pointer to the Java environment
    *
@@ -907,45 +964,25 @@ class IllegalArgumentExceptionJni :
    *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jclass getJClass(JNIEnv* env) {
-    return JavaException::getJClass(env, "java/lang/IllegalArgumentException");
+  static jclass getListClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/util/List");
   }
 
   /**
-   * Create and throw a Java IllegalArgumentException with the provided status
-   *
-   * If s.ok() == true, then this function will not throw any exception.
+   * Get the Java Class java.util.ArrayList
    *
    * @param env A pointer to the Java environment
-   * @param s The status for the exception
    *
-   * @return true if an exception was thrown, false otherwise
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static bool ThrowNew(JNIEnv* env, const Status& s) {
-    assert(!s.ok());
-    if (s.ok()) {
-      return false;
-    }
-
-    // get the IllegalArgumentException class
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      std::cerr << "IllegalArgumentExceptionJni::ThrowNew/class - Error: unexpected exception!" << std::endl;
-      return env->ExceptionCheck();
-    }
-
-    return JavaException::ThrowNew(env, s.ToString());
+  static jclass getArrayListClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/util/ArrayList");
   }
-};
-
 
-// The portal class for org.rocksdb.Options
-class OptionsJni : public RocksDBNativeClass<
-    rocksdb::Options*, OptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.Options
+   * Get the Java Class java.util.Iterator
    *
    * @param env A pointer to the Java environment
    *
@@ -953,87 +990,119 @@ class OptionsJni : public RocksDBNativeClass<
    *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/Options");
+  static jclass getIteratorClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/util/Iterator");
   }
-};
 
-// The portal class for org.rocksdb.DBOptions
-class DBOptionsJni : public RocksDBNativeClass<
-    rocksdb::DBOptions*, DBOptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.DBOptions
+   * Get the Java Method: List#iterator
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/DBOptions");
+  static jmethodID getIteratorMethod(JNIEnv* env) {
+    jclass jlist_clazz = getListClass(env);
+    if(jlist_clazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid =
+        env->GetMethodID(jlist_clazz, "iterator", "()Ljava/util/Iterator;");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.ColumnFamilyOptions
-class ColumnFamilyOptionsJni
-    : public RocksDBNativeClass<rocksdb::ColumnFamilyOptions*,
-                                ColumnFamilyOptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.ColumnFamilyOptions
+   * Get the Java Method: Iterator#hasNext
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-                                         "org/rocksdb/ColumnFamilyOptions");
+  static jmethodID getHasNextMethod(JNIEnv* env) {
+    jclass jiterator_clazz = getIteratorClass(env);
+    if(jiterator_clazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jiterator_clazz, "hasNext", "()Z");
+    assert(mid != nullptr);
+    return mid;
   }
 
   /**
-   * Create a new Java org.rocksdb.ColumnFamilyOptions object with the same
-   * properties as the provided C++ rocksdb::ColumnFamilyOptions object
+   * Get the Java Method: Iterator#next
    *
    * @param env A pointer to the Java environment
-   * @param cfoptions A pointer to rocksdb::ColumnFamilyOptions object
    *
-   * @return A reference to a Java org.rocksdb.ColumnFamilyOptions object, or
-   * nullptr if an an exception occurs
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jobject construct(JNIEnv* env, const ColumnFamilyOptions* cfoptions) {
-    auto* cfo = new rocksdb::ColumnFamilyOptions(*cfoptions);
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
+  static jmethodID getNextMethod(JNIEnv* env) {
+    jclass jiterator_clazz = getIteratorClass(env);
+    if(jiterator_clazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(J)V");
-    if (mid == nullptr) {
-      // exception thrown: NoSuchMethodException or OutOfMemoryError
+    static jmethodID mid =
+        env->GetMethodID(jiterator_clazz, "next", "()Ljava/lang/Object;");
+    assert(mid != nullptr);
+    return mid;
+  }
+
+  /**
+   * Get the Java Method: ArrayList constructor
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getArrayListConstructorMethodId(JNIEnv* env) {
+    jclass jarray_list_clazz = getArrayListClass(env);
+    if(jarray_list_clazz == nullptr) {
+      // exception occurred accessing class
       return nullptr;
     }
+    static jmethodID mid =
+        env->GetMethodID(jarray_list_clazz, "<init>", "(I)V");
+    assert(mid != nullptr);
+    return mid;
+  }
 
-    jobject jcfd = env->NewObject(jclazz, mid, reinterpret_cast<jlong>(cfo));
-    if (env->ExceptionCheck()) {
+  /**
+   * Get the Java Method: List#add
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getListAddMethodId(JNIEnv* env) {
+    jclass jlist_clazz = getListClass(env);
+    if(jlist_clazz == nullptr) {
+      // exception occurred accessing class
       return nullptr;
     }
 
-    return jcfd;
+    static jmethodID mid =
+        env->GetMethodID(jlist_clazz, "add", "(Ljava/lang/Object;)Z");
+    assert(mid != nullptr);
+    return mid;
   }
 };
 
-// The portal class for org.rocksdb.WriteOptions
-class WriteOptionsJni : public RocksDBNativeClass<
-    rocksdb::WriteOptions*, WriteOptionsJni> {
+// The portal class for java.lang.Byte
+class ByteJni : public JavaClass {
  public:
   /**
-   * Get the Java Class org.rocksdb.WriteOptions
+   * Get the Java Class java.lang.Byte
    *
    * @param env A pointer to the Java environment
    *
@@ -1042,16 +1111,11 @@ class WriteOptionsJni : public RocksDBNativeClass<
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteOptions");
+    return JavaClass::getJClass(env, "java/lang/Byte");
   }
-};
 
-// The portal class for org.rocksdb.ReadOptions
-class ReadOptionsJni : public RocksDBNativeClass<
-    rocksdb::ReadOptions*, ReadOptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.ReadOptions
+   * Get the Java Class byte[]
    *
    * @param env A pointer to the Java environment
    *
@@ -1059,66 +1123,87 @@ class ReadOptionsJni : public RocksDBNativeClass<
    *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/ReadOptions");
+  static jclass getArrayJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "[B");
   }
-};
 
-// The portal class for org.rocksdb.WriteBatch
-class WriteBatchJni : public RocksDBNativeClass<
-    rocksdb::WriteBatch*, WriteBatchJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.WriteBatch
+   * Creates a new 2-dimensional Java Byte Array byte[][]
    *
    * @param env A pointer to the Java environment
+   * @param len The size of the first dimension
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return A reference to the Java byte[][] or nullptr if an exception occurs
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteBatch");
+  static jobjectArray new2dByteArray(JNIEnv* env, const jsize len) {
+    jclass clazz = getArrayJClass(env);
+    if(clazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    return env->NewObjectArray(len, clazz, nullptr);
   }
 
   /**
-   * Create a new Java org.rocksdb.WriteBatch object
+   * Get the Java Method: Byte#byteValue
    *
    * @param env A pointer to the Java environment
-   * @param wb A pointer to rocksdb::WriteBatch object
    *
-   * @return A reference to a Java org.rocksdb.WriteBatch object, or
-   * nullptr if an an exception occurs
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retrieved
    */
-  static jobject construct(JNIEnv* env, const WriteBatch* wb) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
+  static jmethodID getByteValueMethod(JNIEnv* env) {
+    jclass clazz = getJClass(env);
+    if(clazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(J)V");
+    static jmethodID mid = env->GetMethodID(clazz, "byteValue", "()B");
+    assert(mid != nullptr);
+    return mid;
+  }
+
+  /**
+   * Calls the Java Method: Byte#valueOf, returning a constructed Byte jobject
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return A constructing Byte object or nullptr if the class or method id could not
+   *     be retrieved, or an exception occurred
+   */
+  static jobject valueOf(JNIEnv* env, jbyte jprimitive_byte) {
+    jclass clazz = getJClass(env);
+    if (clazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid =
+        env->GetStaticMethodID(clazz, "valueOf", "(B)Ljava/lang/Byte;");
     if (mid == nullptr) {
       // exception thrown: NoSuchMethodException or OutOfMemoryError
       return nullptr;
     }
 
-    jobject jwb = env->NewObject(jclazz, mid, reinterpret_cast<jlong>(wb));
+    const jobject jbyte_obj =
+        env->CallStaticObjectMethod(clazz, mid, jprimitive_byte);
     if (env->ExceptionCheck()) {
+      // exception occurred
       return nullptr;
     }
 
-    return jwb;
+    return jbyte_obj;
   }
+
 };
 
-// The portal class for org.rocksdb.WriteBatch.Handler
-class WriteBatchHandlerJni : public RocksDBNativeClass<
-    const rocksdb::WriteBatchHandlerJniCallback*,
-    WriteBatchHandlerJni> {
+// The portal class for java.lang.Integer
+class IntegerJni : public JavaClass {
  public:
   /**
-   * Get the Java Class org.rocksdb.WriteBatch.Handler
+   * Get the Java Class java.lang.Integer
    *
    * @param env A pointer to the Java environment
    *
@@ -1127,719 +1212,1752 @@ class WriteBatchHandlerJni : public RocksDBNativeClass<
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/WriteBatch$Handler");
+    return JavaClass::getJClass(env, "java/lang/Integer");
   }
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#put
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getPutCfMethodId(JNIEnv* env) {
+  static jobject valueOf(JNIEnv* env, jint jprimitive_int) {
     jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
+    if (jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "put", "(I[B[B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
+    jmethodID mid =
+        env->GetStaticMethodID(jclazz, "valueOf", "(I)Ljava/lang/Integer;");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#put
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getPutMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
+    const jobject jinteger_obj =
+        env->CallStaticObjectMethod(jclazz, mid, jprimitive_int);
+    if (env->ExceptionCheck()) {
+      // exception occurred
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "put", "([B[B)V");
-    assert(mid != nullptr);
-    return mid;
+    return jinteger_obj;
   }
+};
 
+// The portal class for java.lang.Long
+class LongJni : public JavaClass {
+ public:
   /**
-   * Get the Java Method: WriteBatch.Handler#merge
+   * Get the Java Class java.lang.Long
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jmethodID getMergeCfMethodId(JNIEnv* env) {
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/lang/Long");
+  }
+
+  static jobject valueOf(JNIEnv* env, jlong jprimitive_long) {
     jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
+    if (jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "merge", "(I[B[B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
+    jmethodID mid =
+        env->GetStaticMethodID(jclazz, "valueOf", "(J)Ljava/lang/Long;");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#merge
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getMergeMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
+    const jobject jlong_obj =
+        env->CallStaticObjectMethod(jclazz, mid, jprimitive_long);
+    if (env->ExceptionCheck()) {
+      // exception occurred
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "merge", "([B[B)V");
-    assert(mid != nullptr);
-    return mid;
+    return jlong_obj;
   }
+};
 
+// The portal class for java.lang.StringBuilder
+class StringBuilderJni : public JavaClass {
+  public:
   /**
-   * Get the Java Method: WriteBatch.Handler#delete
+   * Get the Java Class java.lang.StringBuilder
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jmethodID getDeleteCfMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
-
-    static jmethodID mid = env->GetMethodID(jclazz, "delete", "(I[B)V");
-    assert(mid != nullptr);
-    return mid;
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/lang/StringBuilder");
   }
 
   /**
-   * Get the Java Method: WriteBatch.Handler#delete
+   * Get the Java Method: StringBuilder#append
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getDeleteMethodId(JNIEnv* env) {
+  static jmethodID getListAddMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "delete", "([B)V");
+    static jmethodID mid =
+        env->GetMethodID(jclazz, "append",
+            "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: WriteBatch.Handler#singleDelete
+   * Appends a C-style string to a StringBuilder
    *
    * @param env A pointer to the Java environment
+   * @param jstring_builder Reference to a java.lang.StringBuilder
+   * @param c_str A C-style string to append to the StringBuilder
    *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
+   * @return A reference to the updated StringBuilder, or a nullptr if
+   *     an exception occurs
    */
-  static jmethodID getSingleDeleteCfMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
-
-    static jmethodID mid = env->GetMethodID(jclazz, "singleDelete", "(I[B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#singleDelete
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getSingleDeleteMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
+  static jobject append(JNIEnv* env, jobject jstring_builder,
+      const char* c_str) {
+    jmethodID mid = getListAddMethodId(env);
+    if(mid == nullptr) {
+      // exception occurred accessing class or method
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "singleDelete", "([B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#deleteRange
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getDeleteRangeCfMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if (jclazz == nullptr) {
-      // exception occurred accessing class
+    jstring new_value_str = env->NewStringUTF(c_str);
+    if(new_value_str == nullptr) {
+      // exception thrown: OutOfMemoryError
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "deleteRange", "(I[B[B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#deleteRange
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getDeleteRangeMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if (jclazz == nullptr) {
-      // exception occurred accessing class
+    jobject jresult_string_builder =
+        env->CallObjectMethod(jstring_builder, mid, new_value_str);
+    if(env->ExceptionCheck()) {
+      // exception occurred
+      env->DeleteLocalRef(new_value_str);
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "deleteRange", "([B[B)V");
-    assert(mid != nullptr);
-    return mid;
+    return jresult_string_builder;
   }
+};
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#logData
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getLogDataMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+// various utility functions for working with RocksDB and JNI
+class JniUtil {
+ public:
+    /**
+     * Detect if jlong overflows size_t
+     * 
+     * @param jvalue the jlong value
+     * 
+     * @return
+     */
+    inline static Status check_if_jlong_fits_size_t(const jlong& jvalue) {
+      Status s = Status::OK();
+      if (static_cast<uint64_t>(jvalue) > std::numeric_limits<size_t>::max()) {
+        s = Status::InvalidArgument(Slice("jlong overflows 32 bit value."));
+      }
+      return s;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "logData", "([B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#putBlobIndex
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getPutBlobIndexCfMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
+    /**
+     * Obtains a reference to the JNIEnv from
+     * the JVM
+     *
+     * If the current thread is not attached to the JavaVM
+     * then it will be attached so as to retrieve the JNIEnv
+     *
+     * If a thread is attached, it must later be manually
+     * released by calling JavaVM::DetachCurrentThread.
+     * This can be handled by always matching calls to this
+     * function with calls to {@link JniUtil::releaseJniEnv(JavaVM*, jboolean)}
+     *
+     * @param jvm (IN) A pointer to the JavaVM instance
+     * @param attached (OUT) A pointer to a boolean which
+     *     will be set to JNI_TRUE if we had to attach the thread
+     *
+     * @return A pointer to the JNIEnv or nullptr if a fatal error
+     *     occurs and the JNIEnv cannot be retrieved
+     */
+    static JNIEnv* getJniEnv(JavaVM* jvm, jboolean* attached) {
+      assert(jvm != nullptr);
 
-    static jmethodID mid = env->GetMethodID(jclazz, "putBlobIndex", "(I[B[B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
+      JNIEnv *env;
+      const jint env_rs = jvm->GetEnv(reinterpret_cast<void**>(&env),
+          JNI_VERSION_1_2);
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#markBeginPrepare
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getMarkBeginPrepareMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+      if(env_rs == JNI_OK) {
+        // current thread is already attached, return the JNIEnv
+        *attached = JNI_FALSE;
+        return env;
+      } else if(env_rs == JNI_EDETACHED) {
+        // current thread is not attached, attempt to attach
+        const jint rs_attach = jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), NULL);
+        if(rs_attach == JNI_OK) {
+          *attached = JNI_TRUE;
+          return env;
+        } else {
+          // error, could not attach the thread
+          std::cerr << "JniUtil::getJniEnv - Fatal: could not attach current thread to JVM!" << std::endl;
+          return nullptr;
+        }
+      } else if(env_rs == JNI_EVERSION) {
+        // error, JDK does not support JNI_VERSION_1_2+
+        std::cerr << "JniUtil::getJniEnv - Fatal: JDK does not support JNI_VERSION_1_2" << std::endl;
+        return nullptr;
+      } else {
+        std::cerr << "JniUtil::getJniEnv - Fatal: Unknown error: env_rs=" << env_rs << std::endl;
+        return nullptr;
+      }
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "markBeginPrepare", "()V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#markEndPrepare
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getMarkEndPrepareMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+    /**
+     * Counterpart to {@link JniUtil::getJniEnv(JavaVM*, jboolean*)}
+     *
+     * Detachess the current thread from the JVM if it was previously
+     * attached
+     *
+     * @param jvm (IN) A pointer to the JavaVM instance
+     * @param attached (IN) JNI_TRUE if we previously had to attach the thread
+     *     to the JavaVM to get the JNIEnv
+     */
+    static void releaseJniEnv(JavaVM* jvm, jboolean& attached) {
+      assert(jvm != nullptr);
+      if(attached == JNI_TRUE) {
+        const jint rs_detach = jvm->DetachCurrentThread();
+        assert(rs_detach == JNI_OK);
+        if(rs_detach != JNI_OK) {
+          std::cerr << "JniUtil::getJniEnv - Warn: Unable to detach current thread from JVM!" << std::endl;
+        }
+      }
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "markEndPrepare", "([B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#markNoop
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getMarkNoopMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+    /**
+     * Copies a Java String[] to a C++ std::vector<std::string>
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param jss (IN) The Java String array to copy
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an OutOfMemoryError or ArrayIndexOutOfBoundsException
+     *     exception occurs
+     *
+     * @return A std::vector<std:string> containing copies of the Java strings
+     */
+    static std::vector<std::string> copyStrings(JNIEnv* env,
+        jobjectArray jss, jboolean* has_exception) {
+          return rocksdb::JniUtil::copyStrings(env, jss,
+              env->GetArrayLength(jss), has_exception);
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "markNoop", "(Z)V");
-    assert(mid != nullptr);
-    return mid;
-  }
-
-  /**
-   * Get the Java Method: WriteBatch.Handler#markRollback
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getMarkRollbackMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
+    /**
+     * Copies a Java String[] to a C++ std::vector<std::string>
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param jss (IN) The Java String array to copy
+     * @param jss_len (IN) The length of the Java String array to copy
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an OutOfMemoryError or ArrayIndexOutOfBoundsException
+     *     exception occurs
+     *
+     * @return A std::vector<std:string> containing copies of the Java strings
+     */
+    static std::vector<std::string> copyStrings(JNIEnv* env,
+        jobjectArray jss, const jsize jss_len, jboolean* has_exception) {
+      std::vector<std::string> strs;
+      strs.reserve(jss_len);
+      for (jsize i = 0; i < jss_len; i++) {
+        jobject js = env->GetObjectArrayElement(jss, i);
+        if(env->ExceptionCheck()) {
+          // exception thrown: ArrayIndexOutOfBoundsException
+          *has_exception = JNI_TRUE;
+          return strs;
+        }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "markRollback", "([B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
+        jstring jstr = static_cast<jstring>(js);
+        const char* str = env->GetStringUTFChars(jstr, nullptr);
+        if(str == nullptr) {
+          // exception thrown: OutOfMemoryError
+          env->DeleteLocalRef(js);
+          *has_exception = JNI_TRUE;
+          return strs;
+        }
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#markCommit
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getMarkCommitMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
+        strs.push_back(std::string(str));
 
-    static jmethodID mid = env->GetMethodID(jclazz, "markCommit", "([B)V");
-    assert(mid != nullptr);
-    return mid;
-  }
+        env->ReleaseStringUTFChars(jstr, str);
+        env->DeleteLocalRef(js);
+      }
 
-  /**
-   * Get the Java Method: WriteBatch.Handler#shouldContinue
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getContinueMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+      *has_exception = JNI_FALSE;
+      return strs;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "shouldContinue", "()Z");
-    assert(mid != nullptr);
-    return mid;
-  }
-};
-
-class WriteBatchSavePointJni : public JavaClass {
- public:
-  /**
-   * Get the Java Class org.rocksdb.WriteBatch.SavePoint
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
-   */
-  static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "org/rocksdb/WriteBatch$SavePoint");
-  }
+    /**
+     * Copies a jstring to a C-style null-terminated byte string
+     * and releases the original jstring
+     *
+     * The jstring is copied as UTF-8
+     *
+     * If an exception occurs, then JNIEnv::ExceptionCheck()
+     * will have been called
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param js (IN) The java string to copy
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an OutOfMemoryError exception occurs
+     *
+     * @return A pointer to the copied string, or a
+     *     nullptr if has_exception == JNI_TRUE
+     */
+    static std::unique_ptr<char[]> copyString(JNIEnv* env, jstring js,
+        jboolean* has_exception) {
+      const char *utf = env->GetStringUTFChars(js, nullptr);
+      if(utf == nullptr) {
+        // exception thrown: OutOfMemoryError
+        env->ExceptionCheck();
+        *has_exception = JNI_TRUE;
+        return nullptr;
+      } else if(env->ExceptionCheck()) {
+        // exception thrown
+        env->ReleaseStringUTFChars(js, utf);
+        *has_exception = JNI_TRUE;
+        return nullptr;
+      }
 
-  /**
-   * Get the Java Method: HistogramData constructor
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getConstructorMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+      const jsize utf_len = env->GetStringUTFLength(js);
+      std::unique_ptr<char[]> str(new char[utf_len + 1]);  // Note: + 1 is needed for the c_str null terminator
+      std::strcpy(str.get(), utf);
+      env->ReleaseStringUTFChars(js, utf);
+      *has_exception = JNI_FALSE;
+      return str;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(JJJ)V");
-    assert(mid != nullptr);
-    return mid;
-  }
+    /**
+     * Copies a jstring to a std::string
+     * and releases the original jstring
+     *
+     * If an exception occurs, then JNIEnv::ExceptionCheck()
+     * will have been called
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param js (IN) The java string to copy
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an OutOfMemoryError exception occurs
+     *
+     * @return A std:string copy of the jstring, or an
+     *     empty std::string if has_exception == JNI_TRUE
+     */
+    static std::string copyStdString(JNIEnv* env, jstring js,
+      jboolean* has_exception) {
+      const char *utf = env->GetStringUTFChars(js, nullptr);
+      if(utf == nullptr) {
+        // exception thrown: OutOfMemoryError
+        env->ExceptionCheck();
+        *has_exception = JNI_TRUE;
+        return std::string();
+      } else if(env->ExceptionCheck()) {
+        // exception thrown
+        env->ReleaseStringUTFChars(js, utf);
+        *has_exception = JNI_TRUE;
+        return std::string();
+      }
 
-  /**
-   * Create a new Java org.rocksdb.WriteBatch.SavePoint object
-   *
-   * @param env A pointer to the Java environment
-   * @param savePoint A pointer to rocksdb::WriteBatch::SavePoint object
-   *
-   * @return A reference to a Java org.rocksdb.WriteBatch.SavePoint object, or
-   * nullptr if an an exception occurs
-   */
-  static jobject construct(JNIEnv* env, const SavePoint &save_point) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
+      std::string name(utf);
+      env->ReleaseStringUTFChars(js, utf);
+      *has_exception = JNI_FALSE;
+      return name;
     }
 
-    jmethodID mid = getConstructorMethodId(env);
-    if (mid == nullptr) {
-      // exception thrown: NoSuchMethodException or OutOfMemoryError
-      return nullptr;
+    /**
+     * Copies bytes from a std::string to a jByteArray
+     *
+     * @param env A pointer to the java environment
+     * @param bytes The bytes to copy
+     *
+     * @return the Java byte[], or nullptr if an exception occurs
+     * 
+     * @throws RocksDBException thrown 
+     *   if memory size to copy exceeds general java specific array size limitation.
+     */
+    static jbyteArray copyBytes(JNIEnv* env, std::string bytes) {
+      return createJavaByteArrayWithSizeCheck(env, bytes.c_str(), bytes.size());
     }
 
-    jobject jsave_point = env->NewObject(jclazz, mid,
-        static_cast<jlong>(save_point.size),
-        static_cast<jlong>(save_point.count),
-        static_cast<jlong>(save_point.content_flags));
-    if (env->ExceptionCheck()) {
-      return nullptr;
-    }
+    /**
+     * Given a Java byte[][] which is an array of java.lang.Strings
+     * where each String is a byte[], the passed function `string_fn`
+     * will be called on each String, the result is the collected by
+     * calling the passed function `collector_fn`
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param jbyte_strings (IN) A Java array of Strings expressed as bytes
+     * @param string_fn (IN) A transform function to call for each String
+     * @param collector_fn (IN) A collector which is called for the result
+     *     of each `string_fn`
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an ArrayIndexOutOfBoundsException or OutOfMemoryError
+     *     exception occurs
+     */
+    template <typename T> static void byteStrings(JNIEnv* env,
+        jobjectArray jbyte_strings,
+        std::function<T(const char*, const size_t)> string_fn,
+        std::function<void(size_t, T)> collector_fn,
+        jboolean *has_exception) {
+      const jsize jlen = env->GetArrayLength(jbyte_strings);
 
-    return jsave_point;
-  }
-};
+      for(jsize i = 0; i < jlen; i++) {
+        jobject jbyte_string_obj = env->GetObjectArrayElement(jbyte_strings, i);
+        if(env->ExceptionCheck()) {
+          // exception thrown: ArrayIndexOutOfBoundsException
+          *has_exception = JNI_TRUE;  // signal error
+          return;
+        }
 
-// The portal class for org.rocksdb.WriteBatchWithIndex
-class WriteBatchWithIndexJni : public RocksDBNativeClass<
-    rocksdb::WriteBatchWithIndex*, WriteBatchWithIndexJni> {
- public:
-  /**
-   * Get the Java Class org.rocksdb.WriteBatchWithIndex
+        jbyteArray jbyte_string_ary =
+            reinterpret_cast<jbyteArray>(jbyte_string_obj);
+        T result = byteString(env, jbyte_string_ary, string_fn, has_exception);
+
+        env->DeleteLocalRef(jbyte_string_obj);
+
+        if(*has_exception == JNI_TRUE) {
+          // exception thrown: OutOfMemoryError
+          return;
+        }
+
+        collector_fn(i, result);
+      }
+
+      *has_exception = JNI_FALSE;
+    }
+
+    /**
+     * Given a Java String which is expressed as a Java Byte Array byte[],
+     * the passed function `string_fn` will be called on the String
+     * and the result returned
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param jbyte_string_ary (IN) A Java String expressed in bytes
+     * @param string_fn (IN) A transform function to call on the String
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an OutOfMemoryError exception occurs
+     */
+    template <typename T> static T byteString(JNIEnv* env,
+        jbyteArray jbyte_string_ary,
+        std::function<T(const char*, const size_t)> string_fn,
+        jboolean* has_exception) {
+      const jsize jbyte_string_len = env->GetArrayLength(jbyte_string_ary);
+      return byteString<T>(env, jbyte_string_ary, jbyte_string_len, string_fn,
+          has_exception);
+    }
+
+    /**
+     * Given a Java String which is expressed as a Java Byte Array byte[],
+     * the passed function `string_fn` will be called on the String
+     * and the result returned
+     *
+     * @param env (IN) A pointer to the java environment
+     * @param jbyte_string_ary (IN) A Java String expressed in bytes
+     * @param jbyte_string_len (IN) The length of the Java String
+     *     expressed in bytes
+     * @param string_fn (IN) A transform function to call on the String
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an OutOfMemoryError exception occurs
+     */
+    template <typename T> static T byteString(JNIEnv* env,
+        jbyteArray jbyte_string_ary, const jsize jbyte_string_len,
+        std::function<T(const char*, const size_t)> string_fn,
+        jboolean* has_exception) {
+      jbyte* jbyte_string =
+          env->GetByteArrayElements(jbyte_string_ary, nullptr);
+      if(jbyte_string == nullptr) {
+        // exception thrown: OutOfMemoryError
+        *has_exception = JNI_TRUE;
+        return nullptr;  // signal error
+      }
+
+      T result =
+          string_fn(reinterpret_cast<char *>(jbyte_string), jbyte_string_len);
+
+      env->ReleaseByteArrayElements(jbyte_string_ary, jbyte_string, JNI_ABORT);
+
+      *has_exception = JNI_FALSE;
+      return result;
+    }
+
+    /**
+     * Converts a std::vector<string> to a Java byte[][] where each Java String
+     * is expressed as a Java Byte Array byte[].
+     *
+     * @param env A pointer to the java environment
+     * @param strings A vector of Strings
+     *
+     * @return A Java array of Strings expressed as bytes,
+     *     or nullptr if an exception is thrown
+     */
+    static jobjectArray stringsBytes(JNIEnv* env, std::vector<std::string> strings) {
+      jclass jcls_ba = ByteJni::getArrayJClass(env);
+      if(jcls_ba == nullptr) {
+        // exception occurred
+        return nullptr;
+      }
+
+      const jsize len = static_cast<jsize>(strings.size());
+
+      jobjectArray jbyte_strings = env->NewObjectArray(len, jcls_ba, nullptr);
+      if(jbyte_strings == nullptr) {
+        // exception thrown: OutOfMemoryError
+        return nullptr;
+      }
+
+      for (jsize i = 0; i < len; i++) {
+        std::string *str = &strings[i];
+        const jsize str_len = static_cast<jsize>(str->size());
+
+        jbyteArray jbyte_string_ary = env->NewByteArray(str_len);
+        if(jbyte_string_ary == nullptr) {
+          // exception thrown: OutOfMemoryError
+          env->DeleteLocalRef(jbyte_strings);
+          return nullptr;
+        }
+
+        env->SetByteArrayRegion(
+          jbyte_string_ary, 0, str_len,
+          const_cast<jbyte*>(reinterpret_cast<const jbyte*>(str->c_str())));
+        if(env->ExceptionCheck()) {
+          // exception thrown: ArrayIndexOutOfBoundsException
+          env->DeleteLocalRef(jbyte_string_ary);
+          env->DeleteLocalRef(jbyte_strings);
+          return nullptr;
+        }
+
+        env->SetObjectArrayElement(jbyte_strings, i, jbyte_string_ary);
+        if(env->ExceptionCheck()) {
+          // exception thrown: ArrayIndexOutOfBoundsException
+          // or ArrayStoreException
+          env->DeleteLocalRef(jbyte_string_ary);
+          env->DeleteLocalRef(jbyte_strings);
+          return nullptr;
+        }
+
+        env->DeleteLocalRef(jbyte_string_ary);
+      }
+
+      return jbyte_strings;
+    }
+
+     /**
+     * Converts a std::vector<std::string> to a Java String[].
+     *
+     * @param env A pointer to the java environment
+     * @param strings A vector of Strings
+     *
+     * @return A Java array of Strings,
+     *     or nullptr if an exception is thrown
+     */
+    static jobjectArray toJavaStrings(JNIEnv* env,
+        const std::vector<std::string>* strings) {
+      jclass jcls_str = env->FindClass("java/lang/String");
+      if(jcls_str == nullptr) {
+        // exception occurred
+        return nullptr;
+      }
+
+      const jsize len = static_cast<jsize>(strings->size());
+
+      jobjectArray jstrings = env->NewObjectArray(len, jcls_str, nullptr);
+      if(jstrings == nullptr) {
+        // exception thrown: OutOfMemoryError
+        return nullptr;
+      }
+
+      for (jsize i = 0; i < len; i++) {
+        const std::string *str = &((*strings)[i]);
+        jstring js = rocksdb::JniUtil::toJavaString(env, str);
+        if (js == nullptr) {
+          env->DeleteLocalRef(jstrings);
+          return nullptr;
+        }
+
+        env->SetObjectArrayElement(jstrings, i, js);
+        if(env->ExceptionCheck()) {
+          // exception thrown: ArrayIndexOutOfBoundsException
+          // or ArrayStoreException
+          env->DeleteLocalRef(js);
+          env->DeleteLocalRef(jstrings);
+          return nullptr;
+        }
+      }
+
+      return jstrings;
+    }
+
+    /**
+     * Creates a Java UTF String from a C++ std::string
+     *
+     * @param env A pointer to the java environment
+     * @param string the C++ std::string
+     * @param treat_empty_as_null true if empty strings should be treated as null
+     *
+     * @return the Java UTF string, or nullptr if the provided string
+     *     is null (or empty and treat_empty_as_null is set), or if an
+     *     exception occurs allocating the Java String.
+     */
+    static jstring toJavaString(JNIEnv* env, const std::string* string,
+        const bool treat_empty_as_null = false) {
+      if (string == nullptr) {
+        return nullptr;
+      }
+
+      if (treat_empty_as_null && string->empty()) {
+        return nullptr;
+      }
+
+      return env->NewStringUTF(string->c_str());
+    }
+    
+    /**
+      * Copies bytes to a new jByteArray with the check of java array size limitation.
+      *
+      * @param bytes pointer to memory to copy to a new jByteArray
+      * @param size number of bytes to copy
+      *
+      * @return the Java byte[], or nullptr if an exception occurs
+      * 
+      * @throws RocksDBException thrown 
+      *   if memory size to copy exceeds general java array size limitation to avoid overflow.
+      */
+    static jbyteArray createJavaByteArrayWithSizeCheck(JNIEnv* env, const char* bytes, const size_t size) {
+      // Limitation for java array size is vm specific
+      // In general it cannot exceed Integer.MAX_VALUE (2^31 - 1)
+      // Current HotSpot VM limitation for array size is Integer.MAX_VALUE - 5 (2^31 - 1 - 5)
+      // It means that the next call to env->NewByteArray can still end with 
+      // OutOfMemoryError("Requested array size exceeds VM limit") coming from VM
+      static const size_t MAX_JARRAY_SIZE = (static_cast<size_t>(1)) << 31;
+      if(size > MAX_JARRAY_SIZE) {
+        rocksdb::RocksDBExceptionJni::ThrowNew(env, "Requested array size exceeds VM limit");
+        return nullptr;
+      }
+      
+      const jsize jlen = static_cast<jsize>(size);
+      jbyteArray jbytes = env->NewByteArray(jlen);
+      if(jbytes == nullptr) {
+        // exception thrown: OutOfMemoryError  
+        return nullptr;
+      }
+      
+      env->SetByteArrayRegion(jbytes, 0, jlen,
+        const_cast<jbyte*>(reinterpret_cast<const jbyte*>(bytes)));
+      if(env->ExceptionCheck()) {
+        // exception thrown: ArrayIndexOutOfBoundsException
+        env->DeleteLocalRef(jbytes);
+        return nullptr;
+      }
+
+      return jbytes;
+    }
+
+    /**
+     * Copies bytes from a rocksdb::Slice to a jByteArray
+     *
+     * @param env A pointer to the java environment
+     * @param bytes The bytes to copy
+     *
+     * @return the Java byte[] or nullptr if an exception occurs
+     * 
+     * @throws RocksDBException thrown 
+     *   if memory size to copy exceeds general java specific array size limitation.
+     */
+    static jbyteArray copyBytes(JNIEnv* env, const Slice& bytes) {
+      return createJavaByteArrayWithSizeCheck(env, bytes.data(), bytes.size());
+    }
+
+    /*
+     * Helper for operations on a key and value
+     * for example WriteBatch->Put
+     *
+     * TODO(AR) could be used for RocksDB->Put etc.
+     */
+    static std::unique_ptr<rocksdb::Status> kv_op(
+        std::function<rocksdb::Status(rocksdb::Slice, rocksdb::Slice)> op,
+        JNIEnv* env, jobject /*jobj*/,
+        jbyteArray jkey, jint jkey_len,
+        jbyteArray jvalue, jint jvalue_len) {
+      jbyte* key = env->GetByteArrayElements(jkey, nullptr);
+      if(env->ExceptionCheck()) {
+        // exception thrown: OutOfMemoryError
+        return nullptr;
+      }
+
+      jbyte* value = env->GetByteArrayElements(jvalue, nullptr);
+      if(env->ExceptionCheck()) {
+        // exception thrown: OutOfMemoryError
+        if(key != nullptr) {
+          env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
+        }
+        return nullptr;
+      }
+
+      rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
+      rocksdb::Slice value_slice(reinterpret_cast<char*>(value),
+          jvalue_len);
+
+      auto status = op(key_slice, value_slice);
+
+      if(value != nullptr) {
+        env->ReleaseByteArrayElements(jvalue, value, JNI_ABORT);
+      }
+      if(key != nullptr) {
+        env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
+      }
+
+      return std::unique_ptr<rocksdb::Status>(new rocksdb::Status(status));
+    }
+
+    /*
+     * Helper for operations on a key
+     * for example WriteBatch->Delete
+     *
+     * TODO(AR) could be used for RocksDB->Delete etc.
+     */
+    static std::unique_ptr<rocksdb::Status> k_op(
+        std::function<rocksdb::Status(rocksdb::Slice)> op,
+        JNIEnv* env, jobject /*jobj*/,
+        jbyteArray jkey, jint jkey_len) {
+      jbyte* key = env->GetByteArrayElements(jkey, nullptr);
+      if(env->ExceptionCheck()) {
+        // exception thrown: OutOfMemoryError
+        return nullptr;
+      }
+
+      rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
+
+      auto status = op(key_slice);
+
+      if(key != nullptr) {
+        env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
+      }
+
+      return std::unique_ptr<rocksdb::Status>(new rocksdb::Status(status));
+    }
+
+    /*
+     * Helper for operations on a value
+     * for example WriteBatchWithIndex->GetFromBatch
+     */
+    static jbyteArray v_op(
+        std::function<rocksdb::Status(rocksdb::Slice, std::string*)> op,
+        JNIEnv* env, jbyteArray jkey, jint jkey_len) {
+      jbyte* key = env->GetByteArrayElements(jkey, nullptr);
+      if(env->ExceptionCheck()) {
+        // exception thrown: OutOfMemoryError
+        return nullptr;
+      }
+
+      rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
+
+      std::string value;
+      rocksdb::Status s = op(key_slice, &value);
+
+      if(key != nullptr) {
+        env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
+      }
+
+      if (s.IsNotFound()) {
+        return nullptr;
+      }
+
+      if (s.ok()) {
+        jbyteArray jret_value =
+            env->NewByteArray(static_cast<jsize>(value.size()));
+        if(jret_value == nullptr) {
+          // exception thrown: OutOfMemoryError
+          return nullptr;
+        }
+
+        env->SetByteArrayRegion(jret_value, 0, static_cast<jsize>(value.size()),
+                                const_cast<jbyte*>(reinterpret_cast<const jbyte*>(value.c_str())));
+        if(env->ExceptionCheck()) {
+          // exception thrown: ArrayIndexOutOfBoundsException
+          if(jret_value != nullptr) {
+            env->DeleteLocalRef(jret_value);
+          }
+          return nullptr;
+        }
+
+        return jret_value;
+      }
+
+      rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
+      return nullptr;
+    }
+
+    /**
+     * Creates a vector<T*> of C++ pointers from
+     *     a Java array of C++ pointer addresses.
+     * 
+     * @param env (IN) A pointer to the java environment
+     * @param pointers (IN) A Java array of C++ pointer addresses
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an ArrayIndexOutOfBoundsException or OutOfMemoryError
+     *     exception occurs.
+     * 
+     * @return A vector of C++ pointers.
+     */
+    template<typename T> static std::vector<T*> fromJPointers(
+        JNIEnv* env, jlongArray jptrs, jboolean *has_exception) {
+      const jsize jptrs_len = env->GetArrayLength(jptrs);
+      std::vector<T*> ptrs;
+      jlong* jptr = env->GetLongArrayElements(jptrs, nullptr);
+      if (jptr == nullptr) {
+        // exception thrown: OutOfMemoryError
+        *has_exception = JNI_TRUE;
+        return ptrs;
+      }
+      ptrs.reserve(jptrs_len);
+      for (jsize i = 0; i < jptrs_len; i++) {
+        ptrs.push_back(reinterpret_cast<T*>(jptr[i]));
+      }
+      env->ReleaseLongArrayElements(jptrs, jptr, JNI_ABORT);
+      return ptrs;
+    }
+
+    /**
+     * Creates a Java array of C++ pointer addresses
+     *     from a vector of C++ pointers.
+     * 
+     * @param env (IN) A pointer to the java environment
+     * @param pointers (IN) A vector of C++ pointers
+     * @param has_exception (OUT) will be set to JNI_TRUE
+     *     if an ArrayIndexOutOfBoundsException or OutOfMemoryError
+     *     exception occurs
+     * 
+     * @return Java array of C++ pointer addresses.
+     */
+    template<typename T> static jlongArray toJPointers(JNIEnv* env,
+        const std::vector<T*> &pointers,
+        jboolean *has_exception) {
+      const jsize len = static_cast<jsize>(pointers.size());
+      std::unique_ptr<jlong[]> results(new jlong[len]);
+      std::transform(pointers.begin(), pointers.end(), results.get(), [](T* pointer) -> jlong {
+        return reinterpret_cast<jlong>(pointer);
+      });
+
+      jlongArray jpointers = env->NewLongArray(len);
+      if (jpointers == nullptr) {
+        // exception thrown: OutOfMemoryError
+        *has_exception = JNI_TRUE;
+        return nullptr;
+      }
+
+      env->SetLongArrayRegion(jpointers, 0, len, results.get());
+      if (env->ExceptionCheck()) {
+        // exception thrown: ArrayIndexOutOfBoundsException
+        *has_exception = JNI_TRUE;
+        env->DeleteLocalRef(jpointers);
+        return nullptr;
+      }
+
+      *has_exception = JNI_FALSE;
+
+      return jpointers;
+    }
+};
+
+class MapJni : public JavaClass {
+ public:
+  /**
+   * Get the Java Class java.util.Map
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/util/Map");
+  }
+
+  /**
+   * Get the Java Method: Map#put
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getMapPutMethodId(JNIEnv* env) {
+    jclass jlist_clazz = getJClass(env);
+    if(jlist_clazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid =
+        env->GetMethodID(jlist_clazz, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+    assert(mid != nullptr);
+    return mid;
+  }
+};
+
+class HashMapJni : public JavaClass {
+ public:
+  /**
+   * Get the Java Class java.util.HashMap
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "java/util/HashMap");
+  }
+
+  /**
+   * Create a new Java java.util.HashMap object.
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return A reference to a Java java.util.HashMap object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject construct(JNIEnv* env, const uint32_t initial_capacity = 16) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(I)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    jobject jhash_map = env->NewObject(jclazz, mid, static_cast<jint>(initial_capacity));
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+
+    return jhash_map;
+  }
+
+  /**
+   * A function which maps a std::pair<K,V> to a std::pair<JK, JV>
+   *
+   * @return Either a pointer to a std::pair<jobject, jobject>, or nullptr
+   *     if an error occurs during the mapping
+   */
+  template <typename K, typename V, typename JK, typename JV>
+  using FnMapKV = std::function<std::unique_ptr<std::pair<JK, JV>> (const std::pair<K, V>&)>;
+
+  // template <class I, typename K, typename V, typename K1, typename V1, typename std::enable_if<std::is_same<typename std::iterator_traits<I>::value_type, std::pair<const K,V>>::value, int32_t>::type = 0>
+  // static void putAll(JNIEnv* env, const jobject jhash_map, I iterator, const FnMapKV<const K,V,K1,V1> &fn_map_kv) {
+  /**
+   * Returns true if it succeeds, false if an error occurs
+   */
+  template<class iterator_type, typename K, typename V>
+  static bool putAll(JNIEnv* env, const jobject jhash_map, iterator_type iterator, iterator_type end, const FnMapKV<K, V, jobject, jobject> &fn_map_kv) {
+    const jmethodID jmid_put = rocksdb::MapJni::getMapPutMethodId(env);
+    if (jmid_put == nullptr) {
+      return false;
+    }
+
+    for (auto it = iterator; it != end; ++it) {
+      const std::unique_ptr<std::pair<jobject, jobject>> result = fn_map_kv(*it);
+      if (result == nullptr) {
+          // an error occurred during fn_map_kv
+          return false;
+      }
+      env->CallObjectMethod(jhash_map, jmid_put, result->first, result->second);
+      if (env->ExceptionCheck()) {
+        // exception occurred
+        env->DeleteLocalRef(result->second);
+        env->DeleteLocalRef(result->first);
+        return false;
+      }
+
+      // release local references
+      env->DeleteLocalRef(result->second);
+      env->DeleteLocalRef(result->first);
+    }
+
+    return true;
+  }
+
+  /**
+   * Creates a java.util.Map<String, String> from a std::map<std::string, std::string>
+   *
+   * @param env A pointer to the Java environment
+   * @param map the Cpp map
+   *
+   * @return a reference to the Java java.util.Map object, or nullptr if an exception occcurred
+   */
+  static jobject fromCppMap(JNIEnv* env, const std::map<std::string, std::string>* map) {
+    if (map == nullptr) {
+      return nullptr;
+    }
+
+    jobject jhash_map = construct(env, static_cast<uint32_t>(map->size()));
+    if (jhash_map == nullptr) {
+      // exception occurred
+      return nullptr;
+    }
+
+    const rocksdb::HashMapJni::FnMapKV<const std::string, const std::string, jobject, jobject> fn_map_kv =
+        [env](const std::pair<const std::string, const std::string>& kv) {
+      jstring jkey = rocksdb::JniUtil::toJavaString(env, &(kv.first), false);
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      jstring jvalue = rocksdb::JniUtil::toJavaString(env, &(kv.second), true);
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        env->DeleteLocalRef(jkey);
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      return std::unique_ptr<std::pair<jobject, jobject>>(new std::pair<jobject, jobject>(static_cast<jobject>(jkey), static_cast<jobject>(jvalue)));
+    };
+
+    if (!putAll(env, jhash_map, map->begin(), map->end(), fn_map_kv)) {
+      // exception occurred
+      return nullptr;
+    }
+
+    return jhash_map;
+  }
+
+  /**
+   * Creates a java.util.Map<String, Long> from a std::map<std::string, uint32_t>
+   *
+   * @param env A pointer to the Java environment
+   * @param map the Cpp map
+   *
+   * @return a reference to the Java java.util.Map object, or nullptr if an exception occcurred
+   */
+  static jobject fromCppMap(JNIEnv* env, const std::map<std::string, uint32_t>* map) {
+    if (map == nullptr) {
+      return nullptr;
+    }
+
+    if (map == nullptr) {
+      return nullptr;
+    }
+
+    jobject jhash_map = construct(env, static_cast<uint32_t>(map->size()));
+    if (jhash_map == nullptr) {
+      // exception occurred
+      return nullptr;
+    }
+
+    const rocksdb::HashMapJni::FnMapKV<const std::string, const uint32_t, jobject, jobject> fn_map_kv =
+        [env](const std::pair<const std::string, const uint32_t>& kv) {
+      jstring jkey = rocksdb::JniUtil::toJavaString(env, &(kv.first), false);
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      jobject jvalue = rocksdb::IntegerJni::valueOf(env, static_cast<jint>(kv.second));
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        env->DeleteLocalRef(jkey);
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      return std::unique_ptr<std::pair<jobject, jobject>>(new std::pair<jobject, jobject>(static_cast<jobject>(jkey), jvalue));
+    };
+
+    if (!putAll(env, jhash_map, map->begin(), map->end(), fn_map_kv)) {
+      // exception occurred
+      return nullptr;
+    }
+
+    return jhash_map;
+  }
+
+  /**
+   * Creates a java.util.Map<String, Long> from a std::map<std::string, uint64_t>
+   *
+   * @param env A pointer to the Java environment
+   * @param map the Cpp map
+   *
+   * @return a reference to the Java java.util.Map object, or nullptr if an exception occcurred
+   */
+  static jobject fromCppMap(JNIEnv* env, const std::map<std::string, uint64_t>* map) {
+    if (map == nullptr) {
+      return nullptr;
+    }
+
+    jobject jhash_map = construct(env, static_cast<uint32_t>(map->size()));
+    if (jhash_map == nullptr) {
+      // exception occurred
+      return nullptr;
+    }
+
+    const rocksdb::HashMapJni::FnMapKV<const std::string, const uint64_t, jobject, jobject> fn_map_kv =
+        [env](const std::pair<const std::string, const uint64_t>& kv) {
+      jstring jkey = rocksdb::JniUtil::toJavaString(env, &(kv.first), false);
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      jobject jvalue = rocksdb::LongJni::valueOf(env, static_cast<jlong>(kv.second));
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        env->DeleteLocalRef(jkey);
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      return std::unique_ptr<std::pair<jobject, jobject>>(new std::pair<jobject, jobject>(static_cast<jobject>(jkey), jvalue));
+    };
+
+    if (!putAll(env, jhash_map, map->begin(), map->end(), fn_map_kv)) {
+      // exception occurred
+      return nullptr;
+    }
+
+    return jhash_map;
+  }
+
+    /**
+   * Creates a java.util.Map<String, Long> from a std::map<uint32_t, uint64_t>
+   *
+   * @param env A pointer to the Java environment
+   * @param map the Cpp map
+   *
+   * @return a reference to the Java java.util.Map object, or nullptr if an exception occcurred
+   */
+  static jobject fromCppMap(JNIEnv* env, const std::map<uint32_t, uint64_t>* map) {
+    if (map == nullptr) {
+      return nullptr;
+    }
+
+    jobject jhash_map = construct(env, static_cast<uint32_t>(map->size()));
+    if (jhash_map == nullptr) {
+      // exception occurred
+      return nullptr;
+    }
+
+    const rocksdb::HashMapJni::FnMapKV<const uint32_t, const uint64_t, jobject, jobject> fn_map_kv =
+        [env](const std::pair<const uint32_t, const uint64_t>& kv) {
+      jobject jkey = rocksdb::IntegerJni::valueOf(env, static_cast<jint>(kv.first));
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      jobject jvalue = rocksdb::LongJni::valueOf(env, static_cast<jlong>(kv.second));
+      if (env->ExceptionCheck()) {
+        // an error occurred
+        env->DeleteLocalRef(jkey);
+        return std::unique_ptr<std::pair<jobject, jobject>>(nullptr);
+      }
+
+      return std::unique_ptr<std::pair<jobject, jobject>>(new std::pair<jobject, jobject>(static_cast<jobject>(jkey), jvalue));
+    };
+
+    if (!putAll(env, jhash_map, map->begin(), map->end(), fn_map_kv)) {
+      // exception occurred
+      return nullptr;
+    }
+
+    return jhash_map;
+  }
+};
+
+// The portal class for org.rocksdb.RocksDB
+class RocksDBJni : public RocksDBNativeClass<rocksdb::DB*, RocksDBJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.RocksDB
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/RocksDB");
+  }
+};
+
+// The portal class for org.rocksdb.Options
+class OptionsJni : public RocksDBNativeClass<
+    rocksdb::Options*, OptionsJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.Options
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/Options");
+  }
+};
+
+// The portal class for org.rocksdb.DBOptions
+class DBOptionsJni : public RocksDBNativeClass<
+    rocksdb::DBOptions*, DBOptionsJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.DBOptions
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/DBOptions");
+  }
+};
+
+// The portal class for org.rocksdb.ColumnFamilyOptions
+class ColumnFamilyOptionsJni
+    : public RocksDBNativeClass<rocksdb::ColumnFamilyOptions*,
+                                ColumnFamilyOptionsJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.ColumnFamilyOptions
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+                                         "org/rocksdb/ColumnFamilyOptions");
+  }
+
+  /**
+   * Create a new Java org.rocksdb.ColumnFamilyOptions object with the same
+   * properties as the provided C++ rocksdb::ColumnFamilyOptions object
+   *
+   * @param env A pointer to the Java environment
+   * @param cfoptions A pointer to rocksdb::ColumnFamilyOptions object
+   *
+   * @return A reference to a Java org.rocksdb.ColumnFamilyOptions object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject construct(JNIEnv* env, const ColumnFamilyOptions* cfoptions) {
+    auto* cfo = new rocksdb::ColumnFamilyOptions(*cfoptions);
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(J)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    jobject jcfd = env->NewObject(jclazz, mid, reinterpret_cast<jlong>(cfo));
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+
+    return jcfd;
+  }
+};
+
+// The portal class for org.rocksdb.WriteOptions
+class WriteOptionsJni : public RocksDBNativeClass<
+    rocksdb::WriteOptions*, WriteOptionsJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.WriteOptions
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteOptions");
+  }
+};
+
+// The portal class for org.rocksdb.ReadOptions
+class ReadOptionsJni : public RocksDBNativeClass<
+    rocksdb::ReadOptions*, ReadOptionsJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.ReadOptions
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/ReadOptions");
+  }
+};
+
+// The portal class for org.rocksdb.WriteBatch
+class WriteBatchJni : public RocksDBNativeClass<
+    rocksdb::WriteBatch*, WriteBatchJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.WriteBatch
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteBatch");
+  }
+
+  /**
+   * Create a new Java org.rocksdb.WriteBatch object
+   *
+   * @param env A pointer to the Java environment
+   * @param wb A pointer to rocksdb::WriteBatch object
+   *
+   * @return A reference to a Java org.rocksdb.WriteBatch object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject construct(JNIEnv* env, const WriteBatch* wb) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(J)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    jobject jwb = env->NewObject(jclazz, mid, reinterpret_cast<jlong>(wb));
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+
+    return jwb;
+  }
+};
+
+// The portal class for org.rocksdb.WriteBatch.Handler
+class WriteBatchHandlerJni : public RocksDBNativeClass<
+    const rocksdb::WriteBatchHandlerJniCallback*,
+    WriteBatchHandlerJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.WriteBatch.Handler
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/WriteBatch$Handler");
+  }
+
+  /**
+   * Get the Java Method: WriteBatch.Handler#put
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getPutCfMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "put", "(I[B[B)V");
+    assert(mid != nullptr);
+    return mid;
+  }
+
+  /**
+   * Get the Java Method: WriteBatch.Handler#put
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getPutMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "put", "([B[B)V");
+    assert(mid != nullptr);
+    return mid;
+  }
+
+  /**
+   * Get the Java Method: WriteBatch.Handler#merge
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getMergeCfMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "merge", "(I[B[B)V");
+    assert(mid != nullptr);
+    return mid;
+  }
+
+  /**
+   * Get the Java Method: WriteBatch.Handler#merge
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/WriteBatchWithIndex");
+  static jmethodID getMergeMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "merge", "([B[B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.HistogramData
-class HistogramDataJni : public JavaClass {
- public:
   /**
-   * Get the Java Class org.rocksdb.HistogramData
+   * Get the Java Method: WriteBatch.Handler#delete
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "org/rocksdb/HistogramData");
+  static jmethodID getDeleteCfMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "delete", "(I[B)V");
+    assert(mid != nullptr);
+    return mid;
   }
 
   /**
-   * Get the Java Method: HistogramData constructor
+   * Get the Java Method: WriteBatch.Handler#delete
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getConstructorMethodId(JNIEnv* env) {
+  static jmethodID getDeleteMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(DDDDD)V");
+    static jmethodID mid = env->GetMethodID(jclazz, "delete", "([B)V");
     assert(mid != nullptr);
     return mid;
   }
-};
 
-// The portal class for org.rocksdb.BackupableDBOptions
-class BackupableDBOptionsJni : public RocksDBNativeClass<
-    rocksdb::BackupableDBOptions*, BackupableDBOptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.BackupableDBOptions
+   * Get the Java Method: WriteBatch.Handler#singleDelete
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/BackupableDBOptions");
+  static jmethodID getSingleDeleteCfMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "singleDelete", "(I[B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.BackupEngine
-class BackupEngineJni : public RocksDBNativeClass<
-    rocksdb::BackupEngine*, BackupEngineJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.BackupableEngine
+   * Get the Java Method: WriteBatch.Handler#singleDelete
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/BackupEngine");
+  static jmethodID getSingleDeleteMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "singleDelete", "([B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.RocksIterator
-class IteratorJni : public RocksDBNativeClass<
-    rocksdb::Iterator*, IteratorJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.RocksIterator
+   * Get the Java Method: WriteBatch.Handler#deleteRange
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/RocksIterator");
+  static jmethodID getDeleteRangeCfMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "deleteRange", "(I[B[B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.Filter
-class FilterJni : public RocksDBNativeClass<
-    std::shared_ptr<rocksdb::FilterPolicy>*, FilterJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.Filter
+   * Get the Java Method: WriteBatch.Handler#deleteRange
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/Filter");
+  static jmethodID getDeleteRangeMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "deleteRange", "([B[B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.ColumnFamilyHandle
-class ColumnFamilyHandleJni : public RocksDBNativeClass<
-    rocksdb::ColumnFamilyHandle*, ColumnFamilyHandleJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.ColumnFamilyHandle
+   * Get the Java Method: WriteBatch.Handler#logData
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/ColumnFamilyHandle");
+  static jmethodID getLogDataMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "logData", "([B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.FlushOptions
-class FlushOptionsJni : public RocksDBNativeClass<
-    rocksdb::FlushOptions*, FlushOptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.FlushOptions
+   * Get the Java Method: WriteBatch.Handler#putBlobIndex
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/FlushOptions");
+  static jmethodID getPutBlobIndexCfMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "putBlobIndex", "(I[B[B)V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.ComparatorOptions
-class ComparatorOptionsJni : public RocksDBNativeClass<
-    rocksdb::ComparatorJniCallbackOptions*, ComparatorOptionsJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.ComparatorOptions
+   * Get the Java Method: WriteBatch.Handler#markBeginPrepare
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/ComparatorOptions");
+  static jmethodID getMarkBeginPrepareMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "markBeginPrepare", "()V");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-// The portal class for org.rocksdb.AbstractCompactionFilterFactory
-class AbstractCompactionFilterFactoryJni : public RocksDBNativeClass<
-    const rocksdb::CompactionFilterFactoryJniCallback*,
-    AbstractCompactionFilterFactoryJni> {
- public:
   /**
-   * Get the Java Class org.rocksdb.AbstractCompactionFilterFactory
+   * Get the Java Method: WriteBatch.Handler#markEndPrepare
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getMarkEndPrepareMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "markEndPrepare", "([B)V");
+    assert(mid != nullptr);
+    return mid;
+  }
+
+  /**
+   * Get the Java Method: WriteBatch.Handler#markNoop
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/AbstractCompactionFilterFactory");
+  static jmethodID getMarkNoopMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "markNoop", "(Z)V");
+    assert(mid != nullptr);
+    return mid;
   }
 
   /**
-   * Get the Java Method: AbstractCompactionFilterFactory#name
+   * Get the Java Method: WriteBatch.Handler#markRollback
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getNameMethodId(JNIEnv* env) {
+  static jmethodID getMarkRollbackMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(
-        jclazz, "name", "()Ljava/lang/String;");
+    static jmethodID mid = env->GetMethodID(jclazz, "markRollback", "([B)V");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: AbstractCompactionFilterFactory#createCompactionFilter
+   * Get the Java Method: WriteBatch.Handler#markCommit
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getCreateCompactionFilterMethodId(JNIEnv* env) {
+  static jmethodID getMarkCommitMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz,
-      "createCompactionFilter",
-      "(ZZ)J");
+    static jmethodID mid = env->GetMethodID(jclazz, "markCommit", "([B)V");
     assert(mid != nullptr);
     return mid;
   }
-};
-
-// The portal class for org.rocksdb.AbstractTransactionNotifier
-class AbstractTransactionNotifierJni : public RocksDBNativeClass<
-    const rocksdb::TransactionNotifierJniCallback*,
-    AbstractTransactionNotifierJni> {
- public:
-  static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/AbstractTransactionNotifier");
-  }
 
-  // Get the java method `snapshotCreated`
-  // of org.rocksdb.AbstractTransactionNotifier.
-  static jmethodID getSnapshotCreatedMethodId(JNIEnv* env) {
+  /**
+   * Get the Java Method: WriteBatch.Handler#shouldContinue
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getContinueMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "snapshotCreated", "(J)V");
+    static jmethodID mid = env->GetMethodID(jclazz, "shouldContinue", "()Z");
     assert(mid != nullptr);
     return mid;
   }
 };
 
-// The portal class for org.rocksdb.AbstractComparator
-class AbstractComparatorJni : public RocksDBNativeClass<
-    const rocksdb::BaseComparatorJniCallback*,
-    AbstractComparatorJni> {
+class WriteBatchSavePointJni : public JavaClass {
  public:
   /**
-   * Get the Java Class org.rocksdb.AbstractComparator
+   * Get the Java Class org.rocksdb.WriteBatch.SavePoint
    *
    * @param env A pointer to the Java environment
    *
@@ -1848,104 +2966,125 @@ class AbstractComparatorJni : public RocksDBNativeClass<
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env,
-        "org/rocksdb/AbstractComparator");
+    return JavaClass::getJClass(env, "org/rocksdb/WriteBatch$SavePoint");
   }
 
   /**
-   * Get the Java Method: Comparator#name
+   * Get the Java Method: HistogramData constructor
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getNameMethodId(JNIEnv* env) {
+  static jmethodID getConstructorMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid =
-        env->GetMethodID(jclazz, "name", "()Ljava/lang/String;");
+    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(JJJ)V");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: Comparator#compare
+   * Create a new Java org.rocksdb.WriteBatch.SavePoint object
    *
    * @param env A pointer to the Java environment
+   * @param savePoint A pointer to rocksdb::WriteBatch::SavePoint object
    *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
+   * @return A reference to a Java org.rocksdb.WriteBatch.SavePoint object, or
+   * nullptr if an an exception occurs
    */
-  static jmethodID getCompareMethodId(JNIEnv* env) {
+  static jobject construct(JNIEnv* env, const SavePoint &save_point) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid =
-        env->GetMethodID(jclazz, "compare",
-            "(Lorg/rocksdb/AbstractSlice;Lorg/rocksdb/AbstractSlice;)I");
-    assert(mid != nullptr);
-    return mid;
+    jmethodID mid = getConstructorMethodId(env);
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    jobject jsave_point = env->NewObject(jclazz, mid,
+        static_cast<jlong>(save_point.size),
+        static_cast<jlong>(save_point.count),
+        static_cast<jlong>(save_point.content_flags));
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+
+    return jsave_point;
   }
+};
 
+// The portal class for org.rocksdb.WriteBatchWithIndex
+class WriteBatchWithIndexJni : public RocksDBNativeClass<
+    rocksdb::WriteBatchWithIndex*, WriteBatchWithIndexJni> {
+ public:
   /**
-   * Get the Java Method: Comparator#findShortestSeparator
+   * Get the Java Class org.rocksdb.WriteBatchWithIndex
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jmethodID getFindShortestSeparatorMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/WriteBatchWithIndex");
+  }
+};
 
-    static jmethodID mid =
-        env->GetMethodID(jclazz, "findShortestSeparator",
-            "(Ljava/lang/String;Lorg/rocksdb/AbstractSlice;)Ljava/lang/String;");
-    assert(mid != nullptr);
-    return mid;
+// The portal class for org.rocksdb.HistogramData
+class HistogramDataJni : public JavaClass {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.HistogramData
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/HistogramData");
   }
 
   /**
-   * Get the Java Method: Comparator#findShortSuccessor
+   * Get the Java Method: HistogramData constructor
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getFindShortSuccessorMethodId(JNIEnv* env) {
+  static jmethodID getConstructorMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
     if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid =
-        env->GetMethodID(jclazz, "findShortSuccessor",
-            "(Ljava/lang/String;)Ljava/lang/String;");
+    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "(DDDDDDJJD)V");
     assert(mid != nullptr);
     return mid;
   }
 };
 
-// The portal class for org.rocksdb.AbstractSlice
-class AbstractSliceJni : public NativeRocksMutableObject<
-    const rocksdb::Slice*, AbstractSliceJni> {
+// The portal class for org.rocksdb.BackupableDBOptions
+class BackupableDBOptionsJni : public RocksDBNativeClass<
+    rocksdb::BackupableDBOptions*, BackupableDBOptionsJni> {
  public:
   /**
-   * Get the Java Class org.rocksdb.AbstractSlice
+   * Get the Java Class org.rocksdb.BackupableDBOptions
    *
    * @param env A pointer to the Java environment
    *
@@ -1954,16 +3093,17 @@ class AbstractSliceJni : public NativeRocksMutableObject<
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/AbstractSlice");
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/BackupableDBOptions");
   }
 };
 
-// The portal class for org.rocksdb.Slice
-class SliceJni : public NativeRocksMutableObject<
-    const rocksdb::Slice*, AbstractSliceJni> {
+// The portal class for org.rocksdb.BackupEngine
+class BackupEngineJni : public RocksDBNativeClass<
+    rocksdb::BackupEngine*, BackupEngineJni> {
  public:
   /**
-   * Get the Java Class org.rocksdb.Slice
+   * Get the Java Class org.rocksdb.BackupableEngine
    *
    * @param env A pointer to the Java environment
    *
@@ -1972,45 +3112,34 @@ class SliceJni : public NativeRocksMutableObject<
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/Slice");
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/BackupEngine");
   }
+};
 
+// The portal class for org.rocksdb.RocksIterator
+class IteratorJni : public RocksDBNativeClass<
+    rocksdb::Iterator*, IteratorJni> {
+ public:
   /**
-   * Constructs a Slice object
+   * Get the Java Class org.rocksdb.RocksIterator
    *
    * @param env A pointer to the Java environment
    *
-   * @return A reference to a Java Slice object, or a nullptr if an
-   *     exception occurs
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jobject construct0(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
-
-    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "()V");
-    if(mid == nullptr) {
-      // exception occurred accessing method
-      return nullptr;
-    }
-
-    jobject jslice = env->NewObject(jclazz, mid);
-    if(env->ExceptionCheck()) {
-      return nullptr;
-    }
-
-    return jslice;
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/RocksIterator");
   }
 };
 
-// The portal class for org.rocksdb.DirectSlice
-class DirectSliceJni : public NativeRocksMutableObject<
-    const rocksdb::Slice*, AbstractSliceJni> {
+// The portal class for org.rocksdb.Filter
+class FilterJni : public RocksDBNativeClass<
+    std::shared_ptr<rocksdb::FilterPolicy>*, FilterJni> {
  public:
   /**
-   * Get the Java Class org.rocksdb.DirectSlice
+   * Get the Java Class org.rocksdb.Filter
    *
    * @param env A pointer to the Java environment
    *
@@ -2019,44 +3148,53 @@ class DirectSliceJni : public NativeRocksMutableObject<
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return RocksDBNativeClass::getJClass(env, "org/rocksdb/DirectSlice");
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/Filter");
   }
+};
 
+// The portal class for org.rocksdb.ColumnFamilyHandle
+class ColumnFamilyHandleJni : public RocksDBNativeClass<
+    rocksdb::ColumnFamilyHandle*, ColumnFamilyHandleJni> {
+ public:
   /**
-   * Constructs a DirectSlice object
+   * Get the Java Class org.rocksdb.ColumnFamilyHandle
    *
    * @param env A pointer to the Java environment
    *
-   * @return A reference to a Java DirectSlice object, or a nullptr if an
-   *     exception occurs
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jobject construct0(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
-
-    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "()V");
-    if(mid == nullptr) {
-      // exception occurred accessing method
-      return nullptr;
-    }
-
-    jobject jdirect_slice = env->NewObject(jclazz, mid);
-    if(env->ExceptionCheck()) {
-      return nullptr;
-    }
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/ColumnFamilyHandle");
+  }
+};
 
-    return jdirect_slice;
+// The portal class for org.rocksdb.FlushOptions
+class FlushOptionsJni : public RocksDBNativeClass<
+    rocksdb::FlushOptions*, FlushOptionsJni> {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.FlushOptions
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/FlushOptions");
   }
 };
 
-// The portal class for java.util.List
-class ListJni : public JavaClass {
+// The portal class for org.rocksdb.ComparatorOptions
+class ComparatorOptionsJni : public RocksDBNativeClass<
+    rocksdb::ComparatorJniCallbackOptions*, ComparatorOptionsJni> {
  public:
   /**
-   * Get the Java Class java.util.List
+   * Get the Java Class org.rocksdb.ComparatorOptions
    *
    * @param env A pointer to the Java environment
    *
@@ -2064,12 +3202,18 @@ class ListJni : public JavaClass {
    *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jclass getListClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/util/List");
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/ComparatorOptions");
   }
+};
 
+// The portal class for org.rocksdb.AbstractCompactionFilterFactory
+class AbstractCompactionFilterFactoryJni : public RocksDBNativeClass<
+    const rocksdb::CompactionFilterFactoryJniCallback*,
+    AbstractCompactionFilterFactoryJni> {
+ public:
   /**
-   * Get the Java Class java.util.ArrayList
+   * Get the Java Class org.rocksdb.AbstractCompactionFilterFactory
    *
    * @param env A pointer to the Java environment
    *
@@ -2077,132 +3221,193 @@ class ListJni : public JavaClass {
    *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jclass getArrayListClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/util/ArrayList");
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/AbstractCompactionFilterFactory");
   }
 
   /**
-   * Get the Java Class java.util.Iterator
+   * Get the Java Method: AbstractCompactionFilterFactory#name
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getIteratorClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/util/Iterator");
+  static jmethodID getNameMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "name", "()Ljava/lang/String;");
+    assert(mid != nullptr);
+    return mid;
   }
 
   /**
-   * Get the Java Method: List#iterator
+   * Get the Java Method: AbstractCompactionFilterFactory#createCompactionFilter
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getIteratorMethod(JNIEnv* env) {
-    jclass jlist_clazz = getListClass(env);
-    if(jlist_clazz == nullptr) {
+  static jmethodID getCreateCompactionFilterMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid =
-        env->GetMethodID(jlist_clazz, "iterator", "()Ljava/util/Iterator;");
+    static jmethodID mid = env->GetMethodID(jclazz,
+      "createCompactionFilter",
+      "(ZZ)J");
+    assert(mid != nullptr);
+    return mid;
+  }
+};
+
+// The portal class for org.rocksdb.AbstractTransactionNotifier
+class AbstractTransactionNotifierJni : public RocksDBNativeClass<
+    const rocksdb::TransactionNotifierJniCallback*,
+    AbstractTransactionNotifierJni> {
+ public:
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/AbstractTransactionNotifier");
+  }
+
+  // Get the java method `snapshotCreated`
+  // of org.rocksdb.AbstractTransactionNotifier.
+  static jmethodID getSnapshotCreatedMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    static jmethodID mid = env->GetMethodID(jclazz, "snapshotCreated", "(J)V");
     assert(mid != nullptr);
     return mid;
   }
+};
 
+// The portal class for org.rocksdb.AbstractComparator
+class AbstractComparatorJni : public RocksDBNativeClass<
+    const rocksdb::BaseComparatorJniCallback*,
+    AbstractComparatorJni> {
+ public:
   /**
-   * Get the Java Method: Iterator#hasNext
+   * Get the Java Class org.rocksdb.AbstractComparator
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/AbstractComparator");
+  }
+
+  /**
+   * Get the Java Method: Comparator#name
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getHasNextMethod(JNIEnv* env) {
-    jclass jiterator_clazz = getIteratorClass(env);
-    if(jiterator_clazz == nullptr) {
+  static jmethodID getNameMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jiterator_clazz, "hasNext", "()Z");
+    static jmethodID mid =
+        env->GetMethodID(jclazz, "name", "()Ljava/lang/String;");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: Iterator#next
+   * Get the Java Method: Comparator#compare
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getNextMethod(JNIEnv* env) {
-    jclass jiterator_clazz = getIteratorClass(env);
-    if(jiterator_clazz == nullptr) {
+  static jmethodID getCompareMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
     static jmethodID mid =
-        env->GetMethodID(jiterator_clazz, "next", "()Ljava/lang/Object;");
+        env->GetMethodID(jclazz, "compare",
+            "(Lorg/rocksdb/AbstractSlice;Lorg/rocksdb/AbstractSlice;)I");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: ArrayList constructor
+   * Get the Java Method: Comparator#findShortestSeparator
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getArrayListConstructorMethodId(JNIEnv* env) {
-    jclass jarray_list_clazz = getArrayListClass(env);
-    if(jarray_list_clazz == nullptr) {
+  static jmethodID getFindShortestSeparatorMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
+
     static jmethodID mid =
-        env->GetMethodID(jarray_list_clazz, "<init>", "(I)V");
+        env->GetMethodID(jclazz, "findShortestSeparator",
+            "(Ljava/lang/String;Lorg/rocksdb/AbstractSlice;)Ljava/lang/String;");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: List#add
+   * Get the Java Method: Comparator#findShortSuccessor
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getListAddMethodId(JNIEnv* env) {
-    jclass jlist_clazz = getListClass(env);
-    if(jlist_clazz == nullptr) {
+  static jmethodID getFindShortSuccessorMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
     static jmethodID mid =
-        env->GetMethodID(jlist_clazz, "add", "(Ljava/lang/Object;)Z");
+        env->GetMethodID(jclazz, "findShortSuccessor",
+            "(Ljava/lang/String;)Ljava/lang/String;");
     assert(mid != nullptr);
     return mid;
   }
 };
 
-// The portal class for java.lang.Byte
-class ByteJni : public JavaClass {
+// The portal class for org.rocksdb.AbstractSlice
+class AbstractSliceJni : public NativeRocksMutableObject<
+    const rocksdb::Slice*, AbstractSliceJni> {
  public:
   /**
-   * Get the Java Class java.lang.Byte
+   * Get the Java Class org.rocksdb.AbstractSlice
    *
    * @param env A pointer to the Java environment
    *
@@ -2211,11 +3416,16 @@ class ByteJni : public JavaClass {
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/lang/Byte");
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/AbstractSlice");
   }
+};
 
+// The portal class for org.rocksdb.Slice
+class SliceJni : public NativeRocksMutableObject<
+    const rocksdb::Slice*, AbstractSliceJni> {
+ public:
   /**
-   * Get the Java Class byte[]
+   * Get the Java Class org.rocksdb.Slice
    *
    * @param env A pointer to the Java environment
    *
@@ -2223,54 +3433,46 @@ class ByteJni : public JavaClass {
    *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
-  static jclass getArrayJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "[B");
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/Slice");
   }
 
   /**
-   * Creates a new 2-dimensional Java Byte Array byte[][]
+   * Constructs a Slice object
    *
    * @param env A pointer to the Java environment
-   * @param len The size of the first dimension
    *
-   * @return A reference to the Java byte[][] or nullptr if an exception occurs
+   * @return A reference to a Java Slice object, or a nullptr if an
+   *     exception occurs
    */
-  static jobjectArray new2dByteArray(JNIEnv* env, const jsize len) {
-    jclass clazz = getArrayJClass(env);
-    if(clazz == nullptr) {
+  static jobject construct0(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    return env->NewObjectArray(len, clazz, nullptr);
-  }
+    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "()V");
+    if(mid == nullptr) {
+      // exception occurred accessing method
+      return nullptr;
+    }
 
-  /**
-   * Get the Java Method: Byte#byteValue
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getByteValueMethod(JNIEnv* env) {
-    jclass clazz = getJClass(env);
-    if(clazz == nullptr) {
-      // exception occurred accessing class
+    jobject jslice = env->NewObject(jclazz, mid);
+    if(env->ExceptionCheck()) {
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(clazz, "byteValue", "()B");
-    assert(mid != nullptr);
-    return mid;
+    return jslice;
   }
 };
 
-// The portal class for java.lang.StringBuilder
-class StringBuilderJni : public JavaClass {
-  public:
+// The portal class for org.rocksdb.DirectSlice
+class DirectSliceJni : public NativeRocksMutableObject<
+    const rocksdb::Slice*, AbstractSliceJni> {
+ public:
   /**
-   * Get the Java Class java.lang.StringBuilder
+   * Get the Java Class org.rocksdb.DirectSlice
    *
    * @param env A pointer to the Java environment
    *
@@ -2279,64 +3481,36 @@ class StringBuilderJni : public JavaClass {
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/lang/StringBuilder");
-  }
-
-  /**
-   * Get the Java Method: StringBuilder#append
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Method ID or nullptr if the class or method id could not
-   *     be retieved
-   */
-  static jmethodID getListAddMethodId(JNIEnv* env) {
-    jclass jclazz = getJClass(env);
-    if(jclazz == nullptr) {
-      // exception occurred accessing class
-      return nullptr;
-    }
-
-    static jmethodID mid =
-        env->GetMethodID(jclazz, "append",
-            "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
-    assert(mid != nullptr);
-    return mid;
+    return RocksDBNativeClass::getJClass(env, "org/rocksdb/DirectSlice");
   }
 
   /**
-   * Appends a C-style string to a StringBuilder
+   * Constructs a DirectSlice object
    *
    * @param env A pointer to the Java environment
-   * @param jstring_builder Reference to a java.lang.StringBuilder
-   * @param c_str A C-style string to append to the StringBuilder
-   *
-   * @return A reference to the updated StringBuilder, or a nullptr if
-   *     an exception occurs
-   */
-  static jobject append(JNIEnv* env, jobject jstring_builder,
-      const char* c_str) {
-    jmethodID mid = getListAddMethodId(env);
-    if(mid == nullptr) {
-      // exception occurred accessing class or method
+   *
+   * @return A reference to a Java DirectSlice object, or a nullptr if an
+   *     exception occurs
+   */
+  static jobject construct0(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
       return nullptr;
     }
 
-    jstring new_value_str = env->NewStringUTF(c_str);
-    if(new_value_str == nullptr) {
-      // exception thrown: OutOfMemoryError
+    static jmethodID mid = env->GetMethodID(jclazz, "<init>", "()V");
+    if(mid == nullptr) {
+      // exception occurred accessing method
       return nullptr;
     }
 
-    jobject jresult_string_builder =
-        env->CallObjectMethod(jstring_builder, mid, new_value_str);
+    jobject jdirect_slice = env->NewObject(jclazz, mid);
     if(env->ExceptionCheck()) {
-      // exception occurred
-      env->DeleteLocalRef(new_value_str);
       return nullptr;
     }
 
-    return jresult_string_builder;
+    return jdirect_slice;
   }
 };
 
@@ -3345,9 +4519,98 @@ class TickerTypeJni {
         return 0x5D;
       case rocksdb::Tickers::NUMBER_MULTIGET_KEYS_FOUND:
         return 0x5E;
+      case rocksdb::Tickers::NO_ITERATOR_CREATED:
+        // -0x01 to fixate the new value that incorrectly changed TICKER_ENUM_MAX.
+        return -0x01;
+      case rocksdb::Tickers::NO_ITERATOR_DELETED:
+        return 0x60;
+      case rocksdb::Tickers::COMPACTION_OPTIMIZED_DEL_DROP_OBSOLETE:
+        return 0x61;
+      case rocksdb::Tickers::COMPACTION_CANCELLED:
+        return 0x62;
+      case rocksdb::Tickers::BLOOM_FILTER_FULL_POSITIVE:
+        return 0x63;
+      case rocksdb::Tickers::BLOOM_FILTER_FULL_TRUE_POSITIVE:
+        return 0x64;
+      case rocksdb::Tickers::BLOB_DB_NUM_PUT:
+        return 0x65;
+      case rocksdb::Tickers::BLOB_DB_NUM_WRITE:
+        return 0x66;
+      case rocksdb::Tickers::BLOB_DB_NUM_GET:
+        return 0x67;
+      case rocksdb::Tickers::BLOB_DB_NUM_MULTIGET:
+        return 0x68;
+      case rocksdb::Tickers::BLOB_DB_NUM_SEEK:
+        return 0x69;
+      case rocksdb::Tickers::BLOB_DB_NUM_NEXT:
+        return 0x6A;
+      case rocksdb::Tickers::BLOB_DB_NUM_PREV:
+        return 0x6B;
+      case rocksdb::Tickers::BLOB_DB_NUM_KEYS_WRITTEN:
+        return 0x6C;
+      case rocksdb::Tickers::BLOB_DB_NUM_KEYS_READ:
+        return 0x6D;
+      case rocksdb::Tickers::BLOB_DB_BYTES_WRITTEN:
+        return 0x6E;
+      case rocksdb::Tickers::BLOB_DB_BYTES_READ:
+        return 0x6F;
+      case rocksdb::Tickers::BLOB_DB_WRITE_INLINED:
+        return 0x70;
+      case rocksdb::Tickers::BLOB_DB_WRITE_INLINED_TTL:
+        return 0x71;
+      case rocksdb::Tickers::BLOB_DB_WRITE_BLOB:
+        return 0x72;
+      case rocksdb::Tickers::BLOB_DB_WRITE_BLOB_TTL:
+        return 0x73;
+      case rocksdb::Tickers::BLOB_DB_BLOB_FILE_BYTES_WRITTEN:
+        return 0x74;
+      case rocksdb::Tickers::BLOB_DB_BLOB_FILE_BYTES_READ:
+        return 0x75;
+      case rocksdb::Tickers::BLOB_DB_BLOB_FILE_SYNCED:
+        return 0x76;
+      case rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EXPIRED_COUNT:
+        return 0x77;
+      case rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EXPIRED_SIZE:
+        return 0x78;
+      case rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EVICTED_COUNT:
+        return 0x79;
+      case rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EVICTED_SIZE:
+        return 0x7A;
+      case rocksdb::Tickers::BLOB_DB_GC_NUM_FILES:
+        return 0x7B;
+      case rocksdb::Tickers::BLOB_DB_GC_NUM_NEW_FILES:
+        return 0x7C;
+      case rocksdb::Tickers::BLOB_DB_GC_FAILURES:
+        return 0x7D;
+      case rocksdb::Tickers::BLOB_DB_GC_NUM_KEYS_OVERWRITTEN:
+        return 0x7E;
+      case rocksdb::Tickers::BLOB_DB_GC_NUM_KEYS_EXPIRED:
+        return 0x7F;
+      case rocksdb::Tickers::BLOB_DB_GC_NUM_KEYS_RELOCATED:
+        return -0x02;
+      case rocksdb::Tickers::BLOB_DB_GC_BYTES_OVERWRITTEN:
+        return -0x03;
+      case rocksdb::Tickers::BLOB_DB_GC_BYTES_EXPIRED:
+        return -0x04;
+      case rocksdb::Tickers::BLOB_DB_GC_BYTES_RELOCATED:
+        return -0x05;
+      case rocksdb::Tickers::BLOB_DB_FIFO_NUM_FILES_EVICTED:
+        return -0x06;
+      case rocksdb::Tickers::BLOB_DB_FIFO_NUM_KEYS_EVICTED:
+        return -0x07;
+      case rocksdb::Tickers::BLOB_DB_FIFO_BYTES_EVICTED:
+        return -0x08;
+      case rocksdb::Tickers::TXN_PREPARE_MUTEX_OVERHEAD:
+        return -0x09;
+      case rocksdb::Tickers::TXN_OLD_COMMIT_MAP_MUTEX_OVERHEAD:
+        return -0x0A;
+      case rocksdb::Tickers::TXN_DUPLICATE_KEY_OVERHEAD:
+        return -0x0B;
+      case rocksdb::Tickers::TXN_SNAPSHOT_MUTEX_OVERHEAD:
+        return -0x0C;
       case rocksdb::Tickers::TICKER_ENUM_MAX:
+        // 0x5F for backwards compatibility on current minor version.
         return 0x5F;
-
       default:
         // undefined/default
         return 0x0;
@@ -3548,7 +4811,97 @@ class TickerTypeJni {
         return rocksdb::Tickers::NUMBER_ITER_SKIP;
       case 0x5E:
         return rocksdb::Tickers::NUMBER_MULTIGET_KEYS_FOUND;
+      case -0x01:
+        // -0x01 to fixate the new value that incorrectly changed TICKER_ENUM_MAX.
+        return rocksdb::Tickers::NO_ITERATOR_CREATED;
+      case 0x60:
+        return rocksdb::Tickers::NO_ITERATOR_DELETED;
+      case 0x61:
+        return rocksdb::Tickers::COMPACTION_OPTIMIZED_DEL_DROP_OBSOLETE;
+      case 0x62:
+        return rocksdb::Tickers::COMPACTION_CANCELLED;
+      case 0x63:
+        return rocksdb::Tickers::BLOOM_FILTER_FULL_POSITIVE;
+      case 0x64:
+        return rocksdb::Tickers::BLOOM_FILTER_FULL_TRUE_POSITIVE;
+      case 0x65:
+        return rocksdb::Tickers::BLOB_DB_NUM_PUT;
+      case 0x66:
+        return rocksdb::Tickers::BLOB_DB_NUM_WRITE;
+      case 0x67:
+        return rocksdb::Tickers::BLOB_DB_NUM_GET;
+      case 0x68:
+        return rocksdb::Tickers::BLOB_DB_NUM_MULTIGET;
+      case 0x69:
+        return rocksdb::Tickers::BLOB_DB_NUM_SEEK;
+      case 0x6A:
+        return rocksdb::Tickers::BLOB_DB_NUM_NEXT;
+      case 0x6B:
+        return rocksdb::Tickers::BLOB_DB_NUM_PREV;
+      case 0x6C:
+        return rocksdb::Tickers::BLOB_DB_NUM_KEYS_WRITTEN;
+      case 0x6D:
+        return rocksdb::Tickers::BLOB_DB_NUM_KEYS_READ;
+      case 0x6E:
+        return rocksdb::Tickers::BLOB_DB_BYTES_WRITTEN;
+      case 0x6F:
+        return rocksdb::Tickers::BLOB_DB_BYTES_READ;
+      case 0x70:
+        return rocksdb::Tickers::BLOB_DB_WRITE_INLINED;
+      case 0x71:
+        return rocksdb::Tickers::BLOB_DB_WRITE_INLINED_TTL;
+      case 0x72:
+        return rocksdb::Tickers::BLOB_DB_WRITE_BLOB;
+      case 0x73:
+        return rocksdb::Tickers::BLOB_DB_WRITE_BLOB_TTL;
+      case 0x74:
+        return rocksdb::Tickers::BLOB_DB_BLOB_FILE_BYTES_WRITTEN;
+      case 0x75:
+        return rocksdb::Tickers::BLOB_DB_BLOB_FILE_BYTES_READ;
+      case 0x76:
+        return rocksdb::Tickers::BLOB_DB_BLOB_FILE_SYNCED;
+      case 0x77:
+        return rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EXPIRED_COUNT;
+      case 0x78:
+        return rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EXPIRED_SIZE;
+      case 0x79:
+        return rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EVICTED_COUNT;
+      case 0x7A:
+        return rocksdb::Tickers::BLOB_DB_BLOB_INDEX_EVICTED_SIZE;
+      case 0x7B:
+        return rocksdb::Tickers::BLOB_DB_GC_NUM_FILES;
+      case 0x7C:
+        return rocksdb::Tickers::BLOB_DB_GC_NUM_NEW_FILES;
+      case 0x7D:
+        return rocksdb::Tickers::BLOB_DB_GC_FAILURES;
+      case 0x7E:
+        return rocksdb::Tickers::BLOB_DB_GC_NUM_KEYS_OVERWRITTEN;
+      case 0x7F:
+        return rocksdb::Tickers::BLOB_DB_GC_NUM_KEYS_EXPIRED;
+      case -0x02:
+        return rocksdb::Tickers::BLOB_DB_GC_NUM_KEYS_RELOCATED;
+      case -0x03:
+        return rocksdb::Tickers::BLOB_DB_GC_BYTES_OVERWRITTEN;
+      case -0x04:
+        return rocksdb::Tickers::BLOB_DB_GC_BYTES_EXPIRED;
+      case -0x05:
+        return rocksdb::Tickers::BLOB_DB_GC_BYTES_RELOCATED;
+      case -0x06:
+        return rocksdb::Tickers::BLOB_DB_FIFO_NUM_FILES_EVICTED;
+      case -0x07:
+        return rocksdb::Tickers::BLOB_DB_FIFO_NUM_KEYS_EVICTED;
+      case -0x08:
+        return rocksdb::Tickers::BLOB_DB_FIFO_BYTES_EVICTED;
+      case -0x09:
+        return rocksdb::Tickers::TXN_PREPARE_MUTEX_OVERHEAD;
+      case -0x0A:
+        return rocksdb::Tickers::TXN_OLD_COMMIT_MAP_MUTEX_OVERHEAD;
+      case -0x0B:
+        return rocksdb::Tickers::TXN_DUPLICATE_KEY_OVERHEAD;
+      case -0x0C:
+        return rocksdb::Tickers::TXN_SNAPSHOT_MUTEX_OVERHEAD;
       case 0x5F:
+        // 0x5F for backwards compatibility on current minor version.
         return rocksdb::Tickers::TICKER_ENUM_MAX;
 
       default:
@@ -3628,10 +4981,40 @@ class HistogramTypeJni {
         return 0x1D;
       case rocksdb::Histograms::READ_NUM_MERGE_OPERANDS:
         return 0x1E;
+      // 0x20 to skip 0x1F so TICKER_ENUM_MAX remains unchanged for minor version compatibility.
       case rocksdb::Histograms::FLUSH_TIME:
-        return 0x1F;
-      case rocksdb::Histograms::HISTOGRAM_ENUM_MAX:
         return 0x20;
+      case rocksdb::Histograms::BLOB_DB_KEY_SIZE:
+        return 0x21;
+      case rocksdb::Histograms::BLOB_DB_VALUE_SIZE:
+        return 0x22;
+      case rocksdb::Histograms::BLOB_DB_WRITE_MICROS:
+        return 0x23;
+      case rocksdb::Histograms::BLOB_DB_GET_MICROS:
+        return 0x24;
+      case rocksdb::Histograms::BLOB_DB_MULTIGET_MICROS:
+        return 0x25;
+      case rocksdb::Histograms::BLOB_DB_SEEK_MICROS:
+        return 0x26;
+      case rocksdb::Histograms::BLOB_DB_NEXT_MICROS:
+        return 0x27;
+      case rocksdb::Histograms::BLOB_DB_PREV_MICROS:
+        return 0x28;
+      case rocksdb::Histograms::BLOB_DB_BLOB_FILE_WRITE_MICROS:
+        return 0x29;
+      case rocksdb::Histograms::BLOB_DB_BLOB_FILE_READ_MICROS:
+        return 0x2A;
+      case rocksdb::Histograms::BLOB_DB_BLOB_FILE_SYNC_MICROS:
+        return 0x2B;
+      case rocksdb::Histograms::BLOB_DB_GC_MICROS:
+        return 0x2C;
+      case rocksdb::Histograms::BLOB_DB_COMPRESSION_MICROS:
+        return 0x2D;
+      case rocksdb::Histograms::BLOB_DB_DECOMPRESSION_MICROS:
+        return 0x2E;
+      case rocksdb::Histograms::HISTOGRAM_ENUM_MAX:
+        // 0x1F for backwards compatibility on current minor version.
+        return 0x1F;
 
       default:
         // undefined/default
@@ -3705,9 +5088,39 @@ class HistogramTypeJni {
         return rocksdb::Histograms::DECOMPRESSION_TIMES_NANOS;
       case 0x1E:
         return rocksdb::Histograms::READ_NUM_MERGE_OPERANDS;
-      case 0x1F:
-        return rocksdb::Histograms::FLUSH_TIME;
+      // 0x20 to skip 0x1F so TICKER_ENUM_MAX remains unchanged for minor version compatibility.
       case 0x20:
+        return rocksdb::Histograms::FLUSH_TIME;
+      case 0x21:
+        return rocksdb::Histograms::BLOB_DB_KEY_SIZE;
+      case 0x22:
+        return rocksdb::Histograms::BLOB_DB_VALUE_SIZE;
+      case 0x23:
+        return rocksdb::Histograms::BLOB_DB_WRITE_MICROS;
+      case 0x24:
+        return rocksdb::Histograms::BLOB_DB_GET_MICROS;
+      case 0x25:
+        return rocksdb::Histograms::BLOB_DB_MULTIGET_MICROS;
+      case 0x26:
+        return rocksdb::Histograms::BLOB_DB_SEEK_MICROS;
+      case 0x27:
+        return rocksdb::Histograms::BLOB_DB_NEXT_MICROS;
+      case 0x28:
+        return rocksdb::Histograms::BLOB_DB_PREV_MICROS;
+      case 0x29:
+        return rocksdb::Histograms::BLOB_DB_BLOB_FILE_WRITE_MICROS;
+      case 0x2A:
+        return rocksdb::Histograms::BLOB_DB_BLOB_FILE_READ_MICROS;
+      case 0x2B:
+        return rocksdb::Histograms::BLOB_DB_BLOB_FILE_SYNC_MICROS;
+      case 0x2C:
+        return rocksdb::Histograms::BLOB_DB_GC_MICROS;
+      case 0x2D:
+        return rocksdb::Histograms::BLOB_DB_COMPRESSION_MICROS;
+      case 0x2E:
+        return rocksdb::Histograms::BLOB_DB_DECOMPRESSION_MICROS;
+      case 0x1F:
+        // 0x1F for backwards compatibility on current minor version.
         return rocksdb::Histograms::HISTOGRAM_ENUM_MAX;
 
       default:
@@ -3795,6 +5208,48 @@ class RateLimiterModeJni {
   }
 };
 
+// The portal class for org.rocksdb.MemoryUsageType
+class MemoryUsageTypeJni {
+public:
+  // Returns the equivalent org.rocksdb.MemoryUsageType for the provided
+  // C++ rocksdb::MemoryUtil::UsageType enum
+  static jbyte toJavaMemoryUsageType(
+      const rocksdb::MemoryUtil::UsageType& usage_type) {
+    switch(usage_type) {
+      case rocksdb::MemoryUtil::UsageType::kMemTableTotal:
+        return 0x0;
+      case rocksdb::MemoryUtil::UsageType::kMemTableUnFlushed:
+        return 0x1;
+      case rocksdb::MemoryUtil::UsageType::kTableReadersTotal:
+        return 0x2;
+      case rocksdb::MemoryUtil::UsageType::kCacheTotal:
+        return 0x3;
+      default:
+        // undefined: use kNumUsageTypes
+        return 0x4;
+    }
+  }
+
+  // Returns the equivalent C++ rocksdb::MemoryUtil::UsageType enum for the
+  // provided Java org.rocksdb.MemoryUsageType
+  static rocksdb::MemoryUtil::UsageType toCppMemoryUsageType(
+      jbyte usage_type) {
+    switch(usage_type) {
+      case 0x0:
+        return rocksdb::MemoryUtil::UsageType::kMemTableTotal;
+      case 0x1:
+        return rocksdb::MemoryUtil::UsageType::kMemTableUnFlushed;
+      case 0x2:
+        return rocksdb::MemoryUtil::UsageType::kTableReadersTotal;
+      case 0x3:
+        return rocksdb::MemoryUtil::UsageType::kCacheTotal;
+      default:
+        // undefined/default: use kNumUsageTypes
+        return rocksdb::MemoryUtil::UsageType::kNumUsageTypes;
+    }
+  }
+};
+
 // The portal class for org.rocksdb.Transaction
 class TransactionJni : public JavaClass {
  public:
@@ -4123,704 +5578,1399 @@ class DeadlockPathJni : public JavaClass {
   }
 };
 
-// various utility functions for working with RocksDB and JNI
-class JniUtil {
+class AbstractTableFilterJni : public RocksDBNativeClass<const rocksdb::TableFilterJniCallback*, AbstractTableFilterJni> {
  public:
-    /**
-     * Obtains a reference to the JNIEnv from
-     * the JVM
-     *
-     * If the current thread is not attached to the JavaVM
-     * then it will be attached so as to retrieve the JNIEnv
-     *
-     * If a thread is attached, it must later be manually
-     * released by calling JavaVM::DetachCurrentThread.
-     * This can be handled by always matching calls to this
-     * function with calls to {@link JniUtil::releaseJniEnv(JavaVM*, jboolean)}
-     *
-     * @param jvm (IN) A pointer to the JavaVM instance
-     * @param attached (OUT) A pointer to a boolean which
-     *     will be set to JNI_TRUE if we had to attach the thread
-     *
-     * @return A pointer to the JNIEnv or nullptr if a fatal error
-     *     occurs and the JNIEnv cannot be retrieved
-     */
-    static JNIEnv* getJniEnv(JavaVM* jvm, jboolean* attached) {
-      assert(jvm != nullptr);
+  /**
+   * Get the Java Method: TableFilter#filter(TableProperties)
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getFilterMethod(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
 
-      JNIEnv *env;
-      const jint env_rs = jvm->GetEnv(reinterpret_cast<void**>(&env),
-          JNI_VERSION_1_2);
+    static jmethodID mid =
+        env->GetMethodID(jclazz, "filter", "(Lorg/rocksdb/TableProperties;)Z");
+    assert(mid != nullptr);
+    return mid;
+  }
 
-      if(env_rs == JNI_OK) {
-        // current thread is already attached, return the JNIEnv
-        *attached = JNI_FALSE;
-        return env;
-      } else if(env_rs == JNI_EDETACHED) {
-        // current thread is not attached, attempt to attach
-        const jint rs_attach = jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), NULL);
-        if(rs_attach == JNI_OK) {
-          *attached = JNI_TRUE;
-          return env;
-        } else {
-          // error, could not attach the thread
-          std::cerr << "JniUtil::getJinEnv - Fatal: could not attach current thread to JVM!" << std::endl;
-          return nullptr;
-        }
-      } else if(env_rs == JNI_EVERSION) {
-        // error, JDK does not support JNI_VERSION_1_2+
-        std::cerr << "JniUtil::getJinEnv - Fatal: JDK does not support JNI_VERSION_1_2" << std::endl;
-        return nullptr;
-      } else {
-        std::cerr << "JniUtil::getJinEnv - Fatal: Unknown error: env_rs=" << env_rs << std::endl;
-        return nullptr;
-      }
+ private:
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/TableFilter");
+  }
+};
+
+class TablePropertiesJni : public JavaClass {
+ public:
+  /**
+   * Create a new Java org.rocksdb.TableProperties object.
+   *
+   * @param env A pointer to the Java environment
+   * @param table_properties A Cpp table properties object
+   *
+   * @return A reference to a Java org.rocksdb.TableProperties object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject fromCppTableProperties(JNIEnv* env, const rocksdb::TableProperties& table_properties) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
     }
 
-    /**
-     * Counterpart to {@link JniUtil::getJniEnv(JavaVM*, jboolean*)}
-     *
-     * Detachess the current thread from the JVM if it was previously
-     * attached
-     *
-     * @param jvm (IN) A pointer to the JavaVM instance
-     * @param attached (IN) JNI_TRUE if we previously had to attach the thread
-     *     to the JavaVM to get the JNIEnv
-     */
-    static void releaseJniEnv(JavaVM* jvm, jboolean& attached) {
-      assert(jvm != nullptr);
-      if(attached == JNI_TRUE) {
-        const jint rs_detach = jvm->DetachCurrentThread();
-        assert(rs_detach == JNI_OK);
-        if(rs_detach != JNI_OK) {
-          std::cerr << "JniUtil::getJinEnv - Warn: Unable to detach current thread from JVM!" << std::endl;
-        }
-      }
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(JJJJJJJJJJJJJJJJJJJ[BLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    jbyteArray jcolumn_family_name = rocksdb::JniUtil::copyBytes(env, table_properties.column_family_name);
+    if (jcolumn_family_name == nullptr) {
+      // exception occurred creating java string
+      return nullptr;
+    }
+
+    jstring jfilter_policy_name = rocksdb::JniUtil::toJavaString(env, &table_properties.filter_policy_name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      return nullptr;
+    }
+
+    jstring jcomparator_name = rocksdb::JniUtil::toJavaString(env, &table_properties.comparator_name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      return nullptr;
+    }
+
+    jstring jmerge_operator_name = rocksdb::JniUtil::toJavaString(env, &table_properties.merge_operator_name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      return nullptr;
+    }
+
+    jstring jprefix_extractor_name = rocksdb::JniUtil::toJavaString(env, &table_properties.prefix_extractor_name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      env->DeleteLocalRef(jmerge_operator_name);
+      return nullptr;
+    }
+  
+    jstring jproperty_collectors_names = rocksdb::JniUtil::toJavaString(env, &table_properties.property_collectors_names, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      env->DeleteLocalRef(jmerge_operator_name);
+      env->DeleteLocalRef(jprefix_extractor_name);
+      return nullptr;
+    }
+
+    jstring jcompression_name = rocksdb::JniUtil::toJavaString(env, &table_properties.compression_name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      env->DeleteLocalRef(jmerge_operator_name);
+      env->DeleteLocalRef(jprefix_extractor_name);
+      env->DeleteLocalRef(jproperty_collectors_names);
+      return nullptr;
+    }
+
+    // Map<String, String>
+    jobject juser_collected_properties = rocksdb::HashMapJni::fromCppMap(env, &table_properties.user_collected_properties);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java map
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      env->DeleteLocalRef(jmerge_operator_name);
+      env->DeleteLocalRef(jprefix_extractor_name);
+      env->DeleteLocalRef(jproperty_collectors_names);
+      env->DeleteLocalRef(jcompression_name);
+      return nullptr;
+    }
+
+    // Map<String, String>
+    jobject jreadable_properties = rocksdb::HashMapJni::fromCppMap(env, &table_properties.readable_properties);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java map
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      env->DeleteLocalRef(jmerge_operator_name);
+      env->DeleteLocalRef(jprefix_extractor_name);
+      env->DeleteLocalRef(jproperty_collectors_names);
+      env->DeleteLocalRef(jcompression_name);
+      env->DeleteLocalRef(juser_collected_properties);
+      return nullptr;
+    }
+
+    // Map<String, Long>
+    jobject jproperties_offsets = rocksdb::HashMapJni::fromCppMap(env, &table_properties.properties_offsets);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java map
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfilter_policy_name);
+      env->DeleteLocalRef(jcomparator_name);
+      env->DeleteLocalRef(jmerge_operator_name);
+      env->DeleteLocalRef(jprefix_extractor_name);
+      env->DeleteLocalRef(jproperty_collectors_names);
+      env->DeleteLocalRef(jcompression_name);
+      env->DeleteLocalRef(juser_collected_properties);
+      env->DeleteLocalRef(jreadable_properties);
+      return nullptr;
+    }
+
+    jobject jtable_properties = env->NewObject(jclazz, mid,
+        static_cast<jlong>(table_properties.data_size),
+        static_cast<jlong>(table_properties.index_size),
+        static_cast<jlong>(table_properties.index_partitions),
+        static_cast<jlong>(table_properties.top_level_index_size),
+        static_cast<jlong>(table_properties.index_key_is_user_key),
+        static_cast<jlong>(table_properties.index_value_is_delta_encoded),
+        static_cast<jlong>(table_properties.filter_size),
+        static_cast<jlong>(table_properties.raw_key_size),
+        static_cast<jlong>(table_properties.raw_value_size),
+        static_cast<jlong>(table_properties.num_data_blocks),
+        static_cast<jlong>(table_properties.num_entries),
+        static_cast<jlong>(table_properties.num_deletions),
+        static_cast<jlong>(table_properties.num_merge_operands),
+        static_cast<jlong>(table_properties.num_range_deletions),
+        static_cast<jlong>(table_properties.format_version),
+        static_cast<jlong>(table_properties.fixed_key_len),
+        static_cast<jlong>(table_properties.column_family_id),
+        static_cast<jlong>(table_properties.creation_time),
+        static_cast<jlong>(table_properties.oldest_key_time),
+        jcolumn_family_name,
+        jfilter_policy_name,
+        jcomparator_name,
+        jmerge_operator_name,
+        jprefix_extractor_name,
+        jproperty_collectors_names,
+        jcompression_name,
+        juser_collected_properties,
+        jreadable_properties,
+        jproperties_offsets
+    );
+
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+
+    return jtable_properties;
+  }
+
+ private:
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/TableProperties");
+  }
+};
+
+class ColumnFamilyDescriptorJni : public JavaClass {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.ColumnFamilyDescriptor
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/ColumnFamilyDescriptor");
+  }
+
+  /**
+   * Create a new Java org.rocksdb.ColumnFamilyDescriptor object with the same
+   * properties as the provided C++ rocksdb::ColumnFamilyDescriptor object
+   *
+   * @param env A pointer to the Java environment
+   * @param cfd A pointer to rocksdb::ColumnFamilyDescriptor object
+   *
+   * @return A reference to a Java org.rocksdb.ColumnFamilyDescriptor object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject construct(JNIEnv* env, ColumnFamilyDescriptor* cfd) {
+    jbyteArray jcf_name = JniUtil::copyBytes(env, cfd->name);
+    jobject cfopts = ColumnFamilyOptionsJni::construct(env, &(cfd->options));
+
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
     }
 
-    /**
-     * Copies a Java String[] to a C++ std::vector<std::string>
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param jss (IN) The Java String array to copy
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an OutOfMemoryError or ArrayIndexOutOfBoundsException
-     *     exception occurs
-     *
-     * @return A std::vector<std:string> containing copies of the Java strings
-     */
-    static std::vector<std::string> copyStrings(JNIEnv* env,
-        jobjectArray jss, jboolean* has_exception) {
-          return rocksdb::JniUtil::copyStrings(env, jss,
-              env->GetArrayLength(jss), has_exception);
+    jmethodID mid = env->GetMethodID(jclazz, "<init>",
+                                     "([BLorg/rocksdb/ColumnFamilyOptions;)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      env->DeleteLocalRef(jcf_name);
+      return nullptr;
     }
 
-    /**
-     * Copies a Java String[] to a C++ std::vector<std::string>
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param jss (IN) The Java String array to copy
-     * @param jss_len (IN) The length of the Java String array to copy
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an OutOfMemoryError or ArrayIndexOutOfBoundsException
-     *     exception occurs
-     *
-     * @return A std::vector<std:string> containing copies of the Java strings
-     */
-    static std::vector<std::string> copyStrings(JNIEnv* env,
-        jobjectArray jss, const jsize jss_len, jboolean* has_exception) {
-      std::vector<std::string> strs;
-      for (jsize i = 0; i < jss_len; i++) {
-        jobject js = env->GetObjectArrayElement(jss, i);
-        if(env->ExceptionCheck()) {
-          // exception thrown: ArrayIndexOutOfBoundsException
-          *has_exception = JNI_TRUE;
-          return strs;
-        }
+    jobject jcfd = env->NewObject(jclazz, mid, jcf_name, cfopts);
+    if (env->ExceptionCheck()) {
+      env->DeleteLocalRef(jcf_name);
+      return nullptr;
+    }
 
-        jstring jstr = static_cast<jstring>(js);
-        const char* str = env->GetStringUTFChars(jstr, nullptr);
-        if(str == nullptr) {
-          // exception thrown: OutOfMemoryError
-          env->DeleteLocalRef(js);
-          *has_exception = JNI_TRUE;
-          return strs;
-        }
+    return jcfd;
+  }
 
-        strs.push_back(std::string(str));
+  /**
+   * Get the Java Method: ColumnFamilyDescriptor#columnFamilyName
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getColumnFamilyNameMethod(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
 
-        env->ReleaseStringUTFChars(jstr, str);
-        env->DeleteLocalRef(js);
-      }
+    static jmethodID mid = env->GetMethodID(jclazz, "columnFamilyName", "()[B");
+    assert(mid != nullptr);
+    return mid;
+  }
 
-      *has_exception = JNI_FALSE;
-      return strs;
+  /**
+   * Get the Java Method: ColumnFamilyDescriptor#columnFamilyOptions
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
+   */
+  static jmethodID getColumnFamilyOptionsMethod(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
     }
 
-    /**
-     * Copies a jstring to a C-style null-terminated byte string
-     * and releases the original jstring
-     *
-     * The jstring is copied as UTF-8
-     *
-     * If an exception occurs, then JNIEnv::ExceptionCheck()
-     * will have been called
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param js (IN) The java string to copy
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an OutOfMemoryError exception occurs
-     *
-     * @return A pointer to the copied string, or a
-     *     nullptr if has_exception == JNI_TRUE
-     */
-    static std::unique_ptr<char[]> copyString(JNIEnv* env, jstring js,
-        jboolean* has_exception) {
-      const char *utf = env->GetStringUTFChars(js, nullptr);
-      if(utf == nullptr) {
-        // exception thrown: OutOfMemoryError
-        env->ExceptionCheck();
-        *has_exception = JNI_TRUE;
-        return nullptr;
-      } else if(env->ExceptionCheck()) {
-        // exception thrown
-        env->ReleaseStringUTFChars(js, utf);
-        *has_exception = JNI_TRUE;
-        return nullptr;
-      }
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "columnFamilyOptions", "()Lorg/rocksdb/ColumnFamilyOptions;");
+    assert(mid != nullptr);
+    return mid;
+  }
+};
 
-      const jsize utf_len = env->GetStringUTFLength(js);
-      std::unique_ptr<char[]> str(new char[utf_len + 1]);  // Note: + 1 is needed for the c_str null terminator
-      std::strcpy(str.get(), utf);
-      env->ReleaseStringUTFChars(js, utf);
-      *has_exception = JNI_FALSE;
-      return str;
-    }
+// The portal class for org.rocksdb.IndexType
+class IndexTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.IndexType for the provided
+ // C++ rocksdb::IndexType enum
+ static jbyte toJavaIndexType(
+     const rocksdb::BlockBasedTableOptions::IndexType& index_type) {
+   switch(index_type) {
+     case rocksdb::BlockBasedTableOptions::IndexType::kBinarySearch:
+       return 0x0;
+     case rocksdb::BlockBasedTableOptions::IndexType::kHashSearch:
+       return 0x1;
+    case rocksdb::BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch:
+       return 0x2;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-    /**
-     * Copies a jstring to a std::string
-     * and releases the original jstring
-     *
-     * If an exception occurs, then JNIEnv::ExceptionCheck()
-     * will have been called
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param js (IN) The java string to copy
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an OutOfMemoryError exception occurs
-     *
-     * @return A std:string copy of the jstring, or an
-     *     empty std::string if has_exception == JNI_TRUE
-     */
-    static std::string copyStdString(JNIEnv* env, jstring js,
-      jboolean* has_exception) {
-      const char *utf = env->GetStringUTFChars(js, nullptr);
-      if(utf == nullptr) {
-        // exception thrown: OutOfMemoryError
-        env->ExceptionCheck();
-        *has_exception = JNI_TRUE;
-        return std::string();
-      } else if(env->ExceptionCheck()) {
-        // exception thrown
-        env->ReleaseStringUTFChars(js, utf);
-        *has_exception = JNI_TRUE;
-        return std::string();
-      }
+ // Returns the equivalent C++ rocksdb::IndexType enum for the
+ // provided Java org.rocksdb.IndexType
+ static rocksdb::BlockBasedTableOptions::IndexType toCppIndexType(
+     jbyte jindex_type) {
+   switch(jindex_type) {
+     case 0x0:
+       return rocksdb::BlockBasedTableOptions::IndexType::kBinarySearch;
+     case 0x1:
+       return rocksdb::BlockBasedTableOptions::IndexType::kHashSearch;
+     case 0x2:
+       return rocksdb::BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
+     default:
+       // undefined/default
+       return rocksdb::BlockBasedTableOptions::IndexType::kBinarySearch;
+   }
+ }
+};
 
-      std::string name(utf);
-      env->ReleaseStringUTFChars(js, utf);
-      *has_exception = JNI_FALSE;
-      return name;
-    }
+// The portal class for org.rocksdb.DataBlockIndexType
+class DataBlockIndexTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.DataBlockIndexType for the provided
+ // C++ rocksdb::DataBlockIndexType enum
+ static jbyte toJavaDataBlockIndexType(
+     const rocksdb::BlockBasedTableOptions::DataBlockIndexType& index_type) {
+   switch(index_type) {
+     case rocksdb::BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch:
+       return 0x0;
+     case rocksdb::BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinaryAndHash:
+       return 0x1;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-    /**
-     * Copies bytes from a std::string to a jByteArray
-     *
-     * @param env A pointer to the java environment
-     * @param bytes The bytes to copy
-     *
-     * @return the Java byte[] or nullptr if an exception occurs
-     * 
-     * @throws RocksDBException thrown 
-     *   if memory size to copy exceeds general java specific array size limitation.
-     */
-    static jbyteArray copyBytes(JNIEnv* env, std::string bytes) {
-      return createJavaByteArrayWithSizeCheck(env, bytes.c_str(), bytes.size());
-    }
+ // Returns the equivalent C++ rocksdb::DataBlockIndexType enum for the
+ // provided Java org.rocksdb.DataBlockIndexType
+ static rocksdb::BlockBasedTableOptions::DataBlockIndexType toCppDataBlockIndexType(
+     jbyte jindex_type) {
+   switch(jindex_type) {
+     case 0x0:
+       return rocksdb::BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch;
+     case 0x1:
+       return rocksdb::BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinaryAndHash;
+     default:
+       // undefined/default
+       return rocksdb::BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch;
+   }
+ }
+};
 
-    /**
-     * Given a Java byte[][] which is an array of java.lang.Strings
-     * where each String is a byte[], the passed function `string_fn`
-     * will be called on each String, the result is the collected by
-     * calling the passed function `collector_fn`
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param jbyte_strings (IN) A Java array of Strings expressed as bytes
-     * @param string_fn (IN) A transform function to call for each String
-     * @param collector_fn (IN) A collector which is called for the result
-     *     of each `string_fn`
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an ArrayIndexOutOfBoundsException or OutOfMemoryError
-     *     exception occurs
-     */
-    template <typename T> static void byteStrings(JNIEnv* env,
-        jobjectArray jbyte_strings,
-        std::function<T(const char*, const size_t)> string_fn,
-        std::function<void(size_t, T)> collector_fn,
-        jboolean *has_exception) {
-      const jsize jlen = env->GetArrayLength(jbyte_strings);
+// The portal class for org.rocksdb.ChecksumType
+class ChecksumTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.ChecksumType for the provided
+ // C++ rocksdb::ChecksumType enum
+ static jbyte toJavaChecksumType(
+     const rocksdb::ChecksumType& checksum_type) {
+   switch(checksum_type) {
+     case rocksdb::ChecksumType::kNoChecksum:
+       return 0x0;
+     case rocksdb::ChecksumType::kCRC32c:
+       return 0x1;
+     case rocksdb::ChecksumType::kxxHash:
+       return 0x2;
+     case rocksdb::ChecksumType::kxxHash64:
+       return 0x3;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
+
+ // Returns the equivalent C++ rocksdb::ChecksumType enum for the
+ // provided Java org.rocksdb.ChecksumType
+ static rocksdb::ChecksumType toCppChecksumType(
+     jbyte jchecksum_type) {
+   switch(jchecksum_type) {
+     case 0x0:
+       return rocksdb::ChecksumType::kNoChecksum;
+     case 0x1:
+       return rocksdb::ChecksumType::kCRC32c;
+     case 0x2:
+       return rocksdb::ChecksumType::kxxHash;
+     case 0x3:
+       return rocksdb::ChecksumType::kxxHash64;
+     default:
+       // undefined/default
+       return rocksdb::ChecksumType::kCRC32c;
+   }
+ }
+};
 
-      for(jsize i = 0; i < jlen; i++) {
-        jobject jbyte_string_obj = env->GetObjectArrayElement(jbyte_strings, i);
-        if(env->ExceptionCheck()) {
-          // exception thrown: ArrayIndexOutOfBoundsException
-          *has_exception = JNI_TRUE;  // signal error
-          return;
-        }
+// The portal class for org.rocksdb.Priority
+class PriorityJni {
+ public:
+ // Returns the equivalent org.rocksdb.Priority for the provided
+ // C++ rocksdb::Env::Priority enum
+ static jbyte toJavaPriority(
+     const rocksdb::Env::Priority& priority) {
+   switch(priority) {
+     case rocksdb::Env::Priority::BOTTOM:
+       return 0x0;
+     case rocksdb::Env::Priority::LOW:
+       return 0x1;
+     case rocksdb::Env::Priority::HIGH:
+       return 0x2;
+     case rocksdb::Env::Priority::TOTAL:
+       return 0x3;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-        jbyteArray jbyte_string_ary =
-            reinterpret_cast<jbyteArray>(jbyte_string_obj);
-        T result = byteString(env, jbyte_string_ary, string_fn, has_exception);
+ // Returns the equivalent C++ rocksdb::env::Priority enum for the
+ // provided Java org.rocksdb.Priority
+ static rocksdb::Env::Priority toCppPriority(
+     jbyte jpriority) {
+   switch(jpriority) {
+     case 0x0:
+       return rocksdb::Env::Priority::BOTTOM;
+     case 0x1:
+       return rocksdb::Env::Priority::LOW;
+     case 0x2:
+       return rocksdb::Env::Priority::HIGH;
+     case 0x3:
+       return rocksdb::Env::Priority::TOTAL;
+     default:
+       // undefined/default
+       return rocksdb::Env::Priority::LOW;
+   }
+ }
+};
 
-        env->DeleteLocalRef(jbyte_string_obj);
+// The portal class for org.rocksdb.ThreadType
+class ThreadTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.ThreadType for the provided
+ // C++ rocksdb::ThreadStatus::ThreadType enum
+ static jbyte toJavaThreadType(
+     const rocksdb::ThreadStatus::ThreadType& thread_type) {
+   switch(thread_type) {
+     case rocksdb::ThreadStatus::ThreadType::HIGH_PRIORITY:
+       return 0x0;
+     case rocksdb::ThreadStatus::ThreadType::LOW_PRIORITY:
+       return 0x1;
+     case rocksdb::ThreadStatus::ThreadType::USER:
+       return 0x2;
+     case rocksdb::ThreadStatus::ThreadType::BOTTOM_PRIORITY:
+       return 0x3;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-        if(*has_exception == JNI_TRUE) {
-          // exception thrown: OutOfMemoryError
-          return;
-        }
+ // Returns the equivalent C++ rocksdb::ThreadStatus::ThreadType enum for the
+ // provided Java org.rocksdb.ThreadType
+ static rocksdb::ThreadStatus::ThreadType toCppThreadType(
+     jbyte jthread_type) {
+   switch(jthread_type) {
+     case 0x0:
+       return rocksdb::ThreadStatus::ThreadType::HIGH_PRIORITY;
+     case 0x1:
+       return rocksdb::ThreadStatus::ThreadType::LOW_PRIORITY;
+     case 0x2:
+       return ThreadStatus::ThreadType::USER;
+     case 0x3:
+       return rocksdb::ThreadStatus::ThreadType::BOTTOM_PRIORITY;
+     default:
+       // undefined/default
+       return rocksdb::ThreadStatus::ThreadType::LOW_PRIORITY;
+   }
+ }
+};
 
-        collector_fn(i, result);
-      }
+// The portal class for org.rocksdb.OperationType
+class OperationTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.OperationType for the provided
+ // C++ rocksdb::ThreadStatus::OperationType enum
+ static jbyte toJavaOperationType(
+     const rocksdb::ThreadStatus::OperationType& operation_type) {
+   switch(operation_type) {
+     case rocksdb::ThreadStatus::OperationType::OP_UNKNOWN:
+       return 0x0;
+     case rocksdb::ThreadStatus::OperationType::OP_COMPACTION:
+       return 0x1;
+     case rocksdb::ThreadStatus::OperationType::OP_FLUSH:
+       return 0x2;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-      *has_exception = JNI_FALSE;
-    }
+ // Returns the equivalent C++ rocksdb::ThreadStatus::OperationType enum for the
+ // provided Java org.rocksdb.OperationType
+ static rocksdb::ThreadStatus::OperationType toCppOperationType(
+     jbyte joperation_type) {
+   switch(joperation_type) {
+     case 0x0:
+       return rocksdb::ThreadStatus::OperationType::OP_UNKNOWN;
+     case 0x1:
+       return rocksdb::ThreadStatus::OperationType::OP_COMPACTION;
+     case 0x2:
+       return rocksdb::ThreadStatus::OperationType::OP_FLUSH;
+     default:
+       // undefined/default
+       return rocksdb::ThreadStatus::OperationType::OP_UNKNOWN;
+   }
+ }
+};
 
-    /**
-     * Given a Java String which is expressed as a Java Byte Array byte[],
-     * the passed function `string_fn` will be called on the String
-     * and the result returned
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param jbyte_string_ary (IN) A Java String expressed in bytes
-     * @param string_fn (IN) A transform function to call on the String
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an OutOfMemoryError exception occurs
-     */
-    template <typename T> static T byteString(JNIEnv* env,
-        jbyteArray jbyte_string_ary,
-        std::function<T(const char*, const size_t)> string_fn,
-        jboolean* has_exception) {
-      const jsize jbyte_string_len = env->GetArrayLength(jbyte_string_ary);
-      return byteString<T>(env, jbyte_string_ary, jbyte_string_len, string_fn,
-          has_exception);
-    }
+// The portal class for org.rocksdb.OperationStage
+class OperationStageJni {
+ public:
+ // Returns the equivalent org.rocksdb.OperationStage for the provided
+ // C++ rocksdb::ThreadStatus::OperationStage enum
+ static jbyte toJavaOperationStage(
+     const rocksdb::ThreadStatus::OperationStage& operation_stage) {
+   switch(operation_stage) {
+     case rocksdb::ThreadStatus::OperationStage::STAGE_UNKNOWN:
+       return 0x0;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_FLUSH_RUN:
+       return 0x1;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_FLUSH_WRITE_L0:
+       return 0x2;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_PREPARE:
+       return 0x3;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_RUN:
+       return 0x4;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_PROCESS_KV:
+       return 0x5;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_INSTALL:
+       return 0x6;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_SYNC_FILE:
+       return 0x7;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_PICK_MEMTABLES_TO_FLUSH:
+       return 0x8;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_MEMTABLE_ROLLBACK:
+       return 0x9;
+     case rocksdb::ThreadStatus::OperationStage::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS:
+       return 0xA;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-    /**
-     * Given a Java String which is expressed as a Java Byte Array byte[],
-     * the passed function `string_fn` will be called on the String
-     * and the result returned
-     *
-     * @param env (IN) A pointer to the java environment
-     * @param jbyte_string_ary (IN) A Java String expressed in bytes
-     * @param jbyte_string_len (IN) The length of the Java String
-     *     expressed in bytes
-     * @param string_fn (IN) A transform function to call on the String
-     * @param has_exception (OUT) will be set to JNI_TRUE
-     *     if an OutOfMemoryError exception occurs
-     */
-    template <typename T> static T byteString(JNIEnv* env,
-        jbyteArray jbyte_string_ary, const jsize jbyte_string_len,
-        std::function<T(const char*, const size_t)> string_fn,
-        jboolean* has_exception) {
-      jbyte* jbyte_string =
-          env->GetByteArrayElements(jbyte_string_ary, nullptr);
-      if(jbyte_string == nullptr) {
-        // exception thrown: OutOfMemoryError
-        *has_exception = JNI_TRUE;
-        return nullptr;  // signal error
-      }
+ // Returns the equivalent C++ rocksdb::ThreadStatus::OperationStage enum for the
+ // provided Java org.rocksdb.OperationStage
+ static rocksdb::ThreadStatus::OperationStage toCppOperationStage(
+     jbyte joperation_stage) {
+   switch(joperation_stage) {
+     case 0x0:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_UNKNOWN;
+     case 0x1:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_FLUSH_RUN;
+     case 0x2:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_FLUSH_WRITE_L0;
+     case 0x3:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_PREPARE;
+     case 0x4:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_RUN;
+     case 0x5:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_PROCESS_KV;
+     case 0x6:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_INSTALL;
+     case 0x7:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_COMPACTION_SYNC_FILE;
+     case 0x8:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_PICK_MEMTABLES_TO_FLUSH;
+     case 0x9:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_MEMTABLE_ROLLBACK;
+     case 0xA:
+       return rocksdb::ThreadStatus::OperationStage::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS;
+     default:
+       // undefined/default
+       return rocksdb::ThreadStatus::OperationStage::STAGE_UNKNOWN;
+   }
+ }
+};
 
-      T result =
-          string_fn(reinterpret_cast<char *>(jbyte_string), jbyte_string_len);
+// The portal class for org.rocksdb.StateType
+class StateTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.StateType for the provided
+ // C++ rocksdb::ThreadStatus::StateType enum
+ static jbyte toJavaStateType(
+     const rocksdb::ThreadStatus::StateType& state_type) {
+   switch(state_type) {
+     case rocksdb::ThreadStatus::StateType::STATE_UNKNOWN:
+       return 0x0;
+     case rocksdb::ThreadStatus::StateType::STATE_MUTEX_WAIT:
+       return 0x1;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-      env->ReleaseByteArrayElements(jbyte_string_ary, jbyte_string, JNI_ABORT);
+ // Returns the equivalent C++ rocksdb::ThreadStatus::StateType enum for the
+ // provided Java org.rocksdb.StateType
+ static rocksdb::ThreadStatus::StateType toCppStateType(
+     jbyte jstate_type) {
+   switch(jstate_type) {
+     case 0x0:
+       return rocksdb::ThreadStatus::StateType::STATE_UNKNOWN;
+     case 0x1:
+       return rocksdb::ThreadStatus::StateType::STATE_MUTEX_WAIT;
+     default:
+       // undefined/default
+       return rocksdb::ThreadStatus::StateType::STATE_UNKNOWN;
+   }
+ }
+};
 
-      *has_exception = JNI_FALSE;
-      return result;
+// The portal class for org.rocksdb.ThreadStatus
+class ThreadStatusJni : public JavaClass {
+ public:
+  /**
+   * Get the Java Class org.rocksdb.ThreadStatus
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env,
+        "org/rocksdb/ThreadStatus");
+  }
+
+  /**
+   * Create a new Java org.rocksdb.ThreadStatus object with the same
+   * properties as the provided C++ rocksdb::ThreadStatus object
+   *
+   * @param env A pointer to the Java environment
+   * @param thread_status A pointer to rocksdb::ThreadStatus object
+   *
+   * @return A reference to a Java org.rocksdb.ColumnFamilyOptions object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject construct(JNIEnv* env,
+      const rocksdb::ThreadStatus* thread_status) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
     }
 
-    /**
-     * Converts a std::vector<string> to a Java byte[][] where each Java String
-     * is expressed as a Java Byte Array byte[].
-     *
-     * @param env A pointer to the java environment
-     * @param strings A vector of Strings
-     *
-     * @return A Java array of Strings expressed as bytes
-     */
-    static jobjectArray stringsBytes(JNIEnv* env, std::vector<std::string> strings) {
-      jclass jcls_ba = ByteJni::getArrayJClass(env);
-      if(jcls_ba == nullptr) {
-        // exception occurred
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(JBLjava/lang/String;Ljava/lang/String;BJB[JB)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    jstring jdb_name =
+        JniUtil::toJavaString(env, &(thread_status->db_name), true);
+    if (env->ExceptionCheck()) {
+        // an error occurred
         return nullptr;
-      }
+    }
 
-      const jsize len = static_cast<jsize>(strings.size());
+    jstring jcf_name =
+        JniUtil::toJavaString(env, &(thread_status->cf_name), true);
+    if (env->ExceptionCheck()) {
+        // an error occurred
+        env->DeleteLocalRef(jdb_name);
+        return nullptr;
+    }
 
-      jobjectArray jbyte_strings = env->NewObjectArray(len, jcls_ba, nullptr);
-      if(jbyte_strings == nullptr) {
+    // long[]
+    const jsize len = static_cast<jsize>(rocksdb::ThreadStatus::kNumOperationProperties);
+    jlongArray joperation_properties =
+        env->NewLongArray(len);
+    if (joperation_properties == nullptr) {
+      // an exception occurred
+      env->DeleteLocalRef(jdb_name);
+      env->DeleteLocalRef(jcf_name);
+      return nullptr;
+    }
+    jlong *body = env->GetLongArrayElements(joperation_properties, nullptr);
+    if (body == nullptr) {
         // exception thrown: OutOfMemoryError
+        env->DeleteLocalRef(jdb_name);
+        env->DeleteLocalRef(jcf_name);
+        env->DeleteLocalRef(joperation_properties);
         return nullptr;
-      }
+    }
+    for (size_t i = 0; i < len; ++i) {
+      body[i] = static_cast<jlong>(thread_status->op_properties[i]);
+    }
+    env->ReleaseLongArrayElements(joperation_properties, body, 0);
+
+    jobject jcfd = env->NewObject(jclazz, mid,
+        static_cast<jlong>(thread_status->thread_id),
+        ThreadTypeJni::toJavaThreadType(thread_status->thread_type),
+        jdb_name,
+        jcf_name,
+        OperationTypeJni::toJavaOperationType(thread_status->operation_type),
+        static_cast<jlong>(thread_status->op_elapsed_micros),
+        OperationStageJni::toJavaOperationStage(thread_status->operation_stage),
+        joperation_properties,
+        StateTypeJni::toJavaStateType(thread_status->state_type));
+    if (env->ExceptionCheck()) {
+      // exception occurred
+        env->DeleteLocalRef(jdb_name);
+        env->DeleteLocalRef(jcf_name);
+        env->DeleteLocalRef(joperation_properties);
+      return nullptr;
+    }
 
-      for (jsize i = 0; i < len; i++) {
-        std::string *str = &strings[i];
-        const jsize str_len = static_cast<jsize>(str->size());
+    // cleanup
+    env->DeleteLocalRef(jdb_name);
+    env->DeleteLocalRef(jcf_name);
+    env->DeleteLocalRef(joperation_properties);
 
-        jbyteArray jbyte_string_ary = env->NewByteArray(str_len);
-        if(jbyte_string_ary == nullptr) {
-          // exception thrown: OutOfMemoryError
-          env->DeleteLocalRef(jbyte_strings);
-          return nullptr;
-        }
+    return jcfd;
+  }
+};
+
+// The portal class for org.rocksdb.CompactionStyle
+class CompactionStyleJni {
+ public:
+ // Returns the equivalent org.rocksdb.CompactionStyle for the provided
+ // C++ rocksdb::CompactionStyle enum
+ static jbyte toJavaCompactionStyle(
+     const rocksdb::CompactionStyle& compaction_style) {
+   switch(compaction_style) {
+     case rocksdb::CompactionStyle::kCompactionStyleLevel:
+       return 0x0;
+     case rocksdb::CompactionStyle::kCompactionStyleUniversal:
+       return 0x1;
+     case rocksdb::CompactionStyle::kCompactionStyleFIFO:
+       return 0x2;
+     case rocksdb::CompactionStyle::kCompactionStyleNone:
+       return 0x3;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
+
+ // Returns the equivalent C++ rocksdb::CompactionStyle enum for the
+ // provided Java org.rocksdb.CompactionStyle
+ static rocksdb::CompactionStyle toCppCompactionStyle(
+     jbyte jcompaction_style) {
+   switch(jcompaction_style) {
+     case 0x0:
+       return rocksdb::CompactionStyle::kCompactionStyleLevel;
+     case 0x1:
+       return rocksdb::CompactionStyle::kCompactionStyleUniversal;
+     case 0x2:
+       return rocksdb::CompactionStyle::kCompactionStyleFIFO;
+     case 0x3:
+       return rocksdb::CompactionStyle::kCompactionStyleNone;
+     default:
+       // undefined/default
+       return rocksdb::CompactionStyle::kCompactionStyleLevel;
+   }
+ }
+};
+
+// The portal class for org.rocksdb.CompactionReason
+class CompactionReasonJni {
+ public:
+ // Returns the equivalent org.rocksdb.CompactionReason for the provided
+ // C++ rocksdb::CompactionReason enum
+ static jbyte toJavaCompactionReason(
+     const rocksdb::CompactionReason& compaction_reason) {
+   switch(compaction_reason) {
+     case rocksdb::CompactionReason::kUnknown:
+       return 0x0;
+     case rocksdb::CompactionReason::kLevelL0FilesNum:
+       return 0x1;
+     case rocksdb::CompactionReason::kLevelMaxLevelSize:
+       return 0x2;
+     case rocksdb::CompactionReason::kUniversalSizeAmplification:
+       return 0x3;
+     case rocksdb::CompactionReason::kUniversalSizeRatio:
+       return 0x4;
+     case rocksdb::CompactionReason::kUniversalSortedRunNum:
+       return 0x5;
+     case rocksdb::CompactionReason::kFIFOMaxSize:
+       return 0x6;
+     case rocksdb::CompactionReason::kFIFOReduceNumFiles:
+       return 0x7;
+     case rocksdb::CompactionReason::kFIFOTtl:
+       return 0x8;
+     case rocksdb::CompactionReason::kManualCompaction:
+       return 0x9;
+     case rocksdb::CompactionReason::kFilesMarkedForCompaction:
+       return 0x10;
+     case rocksdb::CompactionReason::kBottommostFiles:
+       return 0x0A;
+     case rocksdb::CompactionReason::kTtl:
+       return 0x0B;
+     case rocksdb::CompactionReason::kFlush:
+       return 0x0C;
+     case rocksdb::CompactionReason::kExternalSstIngestion:
+       return 0x0D;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
+
+ // Returns the equivalent C++ rocksdb::CompactionReason enum for the
+ // provided Java org.rocksdb.CompactionReason
+ static rocksdb::CompactionReason toCppCompactionReason(
+     jbyte jcompaction_reason) {
+   switch(jcompaction_reason) {
+     case 0x0:
+       return rocksdb::CompactionReason::kUnknown;
+     case 0x1:
+       return rocksdb::CompactionReason::kLevelL0FilesNum;
+     case 0x2:
+       return rocksdb::CompactionReason::kLevelMaxLevelSize;
+     case 0x3:
+       return rocksdb::CompactionReason::kUniversalSizeAmplification;
+     case 0x4:
+       return rocksdb::CompactionReason::kUniversalSizeRatio;
+     case 0x5:
+       return rocksdb::CompactionReason::kUniversalSortedRunNum;
+     case 0x6:
+       return rocksdb::CompactionReason::kFIFOMaxSize;
+     case 0x7:
+       return rocksdb::CompactionReason::kFIFOReduceNumFiles;
+     case 0x8:
+       return rocksdb::CompactionReason::kFIFOTtl;
+     case 0x9:
+       return rocksdb::CompactionReason::kManualCompaction;
+     case 0x10:
+       return rocksdb::CompactionReason::kFilesMarkedForCompaction;
+     case 0x0A:
+       return rocksdb::CompactionReason::kBottommostFiles;
+     case 0x0B:
+       return rocksdb::CompactionReason::kTtl;
+     case 0x0C:
+       return rocksdb::CompactionReason::kFlush;
+     case 0x0D:
+       return rocksdb::CompactionReason::kExternalSstIngestion;
+     default:
+       // undefined/default
+       return rocksdb::CompactionReason::kUnknown;
+   }
+ }
+};
 
-        env->SetByteArrayRegion(
-          jbyte_string_ary, 0, str_len,
-          const_cast<jbyte*>(reinterpret_cast<const jbyte*>(str->c_str())));
-        if(env->ExceptionCheck()) {
-          // exception thrown: ArrayIndexOutOfBoundsException
-          env->DeleteLocalRef(jbyte_string_ary);
-          env->DeleteLocalRef(jbyte_strings);
-          return nullptr;
-        }
+// The portal class for org.rocksdb.WalFileType
+class WalFileTypeJni {
+ public:
+ // Returns the equivalent org.rocksdb.WalFileType for the provided
+ // C++ rocksdb::WalFileType enum
+ static jbyte toJavaWalFileType(
+     const rocksdb::WalFileType& wal_file_type) {
+   switch(wal_file_type) {
+     case rocksdb::WalFileType::kArchivedLogFile:
+       return 0x0;
+     case rocksdb::WalFileType::kAliveLogFile:
+       return 0x1;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-        env->SetObjectArrayElement(jbyte_strings, i, jbyte_string_ary);
-        if(env->ExceptionCheck()) {
-          // exception thrown: ArrayIndexOutOfBoundsException
-          // or ArrayStoreException
-          env->DeleteLocalRef(jbyte_string_ary);
-          env->DeleteLocalRef(jbyte_strings);
-          return nullptr;
-        }
+ // Returns the equivalent C++ rocksdb::WalFileType enum for the
+ // provided Java org.rocksdb.WalFileType
+ static rocksdb::WalFileType toCppWalFileType(
+     jbyte jwal_file_type) {
+   switch(jwal_file_type) {
+     case 0x0:
+       return rocksdb::WalFileType::kArchivedLogFile;
+     case 0x1:
+       return rocksdb::WalFileType::kAliveLogFile;
+     default:
+       // undefined/default
+       return rocksdb::WalFileType::kAliveLogFile;
+   }
+ }
+};
 
-        env->DeleteLocalRef(jbyte_string_ary);
-      }
+class LogFileJni : public JavaClass {
+ public:
+  /**
+   * Create a new Java org.rocksdb.LogFile object.
+   *
+   * @param env A pointer to the Java environment
+   * @param log_file A Cpp log file object
+   *
+   * @return A reference to a Java org.rocksdb.LogFile object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject fromCppLogFile(JNIEnv* env, rocksdb::LogFile* log_file) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
 
-      return jbyte_strings;
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(Ljava/lang/String;JBJJ)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
     }
-    
-    /**
-      * Copies bytes to a new jByteArray with the check of java array size limitation.
-      *
-      * @param bytes pointer to memory to copy to a new jByteArray
-      * @param size number of bytes to copy
-      *
-      * @return the Java byte[] or nullptr if an exception occurs
-      * 
-      * @throws RocksDBException thrown 
-      *   if memory size to copy exceeds general java array size limitation to avoid overflow.
-      */
-    static jbyteArray createJavaByteArrayWithSizeCheck(JNIEnv* env, const char* bytes, const size_t size) {
-      // Limitation for java array size is vm specific
-      // In general it cannot exceed Integer.MAX_VALUE (2^31 - 1)
-      // Current HotSpot VM limitation for array size is Integer.MAX_VALUE - 5 (2^31 - 1 - 5)
-      // It means that the next call to env->NewByteArray can still end with 
-      // OutOfMemoryError("Requested array size exceeds VM limit") coming from VM
-      static const size_t MAX_JARRAY_SIZE = (static_cast<size_t>(1)) << 31;
-      if(size > MAX_JARRAY_SIZE) {
-        rocksdb::RocksDBExceptionJni::ThrowNew(env, "Requested array size exceeds VM limit");
-        return nullptr;
-      }
-      
-      const jsize jlen = static_cast<jsize>(size);
-      jbyteArray jbytes = env->NewByteArray(jlen);
-      if(jbytes == nullptr) {
-        // exception thrown: OutOfMemoryError  
-        return nullptr;
-      }
-      
-      env->SetByteArrayRegion(jbytes, 0, jlen,
-        const_cast<jbyte*>(reinterpret_cast<const jbyte*>(bytes)));
-      if(env->ExceptionCheck()) {
-        // exception thrown: ArrayIndexOutOfBoundsException
-        env->DeleteLocalRef(jbytes);
-        return nullptr;
-      }
 
-      return jbytes;
+    std::string path_name = log_file->PathName();
+    jstring jpath_name = rocksdb::JniUtil::toJavaString(env, &path_name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      return nullptr;
     }
 
-    /**
-     * Copies bytes from a rocksdb::Slice to a jByteArray
-     *
-     * @param env A pointer to the java environment
-     * @param bytes The bytes to copy
-     *
-     * @return the Java byte[] or nullptr if an exception occurs
-     * 
-     * @throws RocksDBException thrown 
-     *   if memory size to copy exceeds general java specific array size limitation.
-     */
-    static jbyteArray copyBytes(JNIEnv* env, const Slice& bytes) {
-      return createJavaByteArrayWithSizeCheck(env, bytes.data(), bytes.size());
+    jobject jlog_file = env->NewObject(jclazz, mid,
+        jpath_name,
+        static_cast<jlong>(log_file->LogNumber()),
+        rocksdb::WalFileTypeJni::toJavaWalFileType(log_file->Type()),
+        static_cast<jlong>(log_file->StartSequence()),
+        static_cast<jlong>(log_file->SizeFileBytes())
+    );
+
+    if (env->ExceptionCheck()) {
+      env->DeleteLocalRef(jpath_name);
+      return nullptr;
     }
 
-    /*
-     * Helper for operations on a key and value
-     * for example WriteBatch->Put
-     *
-     * TODO(AR) could be used for RocksDB->Put etc.
-     */
-    static std::unique_ptr<rocksdb::Status> kv_op(
-        std::function<rocksdb::Status(rocksdb::Slice, rocksdb::Slice)> op,
-        JNIEnv* env, jobject /*jobj*/,
-        jbyteArray jkey, jint jkey_len,
-        jbyteArray jvalue, jint jvalue_len) {
-      jbyte* key = env->GetByteArrayElements(jkey, nullptr);
-      if(env->ExceptionCheck()) {
-        // exception thrown: OutOfMemoryError
-        return nullptr;
-      }
+    // cleanup
+    env->DeleteLocalRef(jpath_name);
 
-      jbyte* value = env->GetByteArrayElements(jvalue, nullptr);
-      if(env->ExceptionCheck()) {
-        // exception thrown: OutOfMemoryError
-        if(key != nullptr) {
-          env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
-        }
-        return nullptr;
-      }
+    return jlog_file;
+  }
 
-      rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
-      rocksdb::Slice value_slice(reinterpret_cast<char*>(value),
-          jvalue_len);
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/LogFile");
+  }
+};
 
-      auto status = op(key_slice, value_slice);
+class LiveFileMetaDataJni : public JavaClass {
+ public:
+  /**
+   * Create a new Java org.rocksdb.LiveFileMetaData object.
+   *
+   * @param env A pointer to the Java environment
+   * @param live_file_meta_data A Cpp live file meta data object
+   *
+   * @return A reference to a Java org.rocksdb.LiveFileMetaData object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject fromCppLiveFileMetaData(JNIEnv* env,
+      rocksdb::LiveFileMetaData* live_file_meta_data) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
 
-      if(value != nullptr) {
-        env->ReleaseByteArrayElements(jvalue, value, JNI_ABORT);
-      }
-      if(key != nullptr) {
-        env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
-      }
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "([BILjava/lang/String;Ljava/lang/String;JJJ[B[BJZJJ)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
 
-      return std::unique_ptr<rocksdb::Status>(new rocksdb::Status(status));
+    jbyteArray jcolumn_family_name = rocksdb::JniUtil::copyBytes(
+        env, live_file_meta_data->column_family_name);
+    if (jcolumn_family_name == nullptr) {
+      // exception occurred creating java byte array
+      return nullptr;
     }
 
-    /*
-     * Helper for operations on a key
-     * for example WriteBatch->Delete
-     *
-     * TODO(AR) could be used for RocksDB->Delete etc.
-     */
-    static std::unique_ptr<rocksdb::Status> k_op(
-        std::function<rocksdb::Status(rocksdb::Slice)> op,
-        JNIEnv* env, jobject /*jobj*/,
-        jbyteArray jkey, jint jkey_len) {
-      jbyte* key = env->GetByteArrayElements(jkey, nullptr);
-      if(env->ExceptionCheck()) {
-        // exception thrown: OutOfMemoryError
-        return nullptr;
-      }
+    jstring jfile_name = rocksdb::JniUtil::toJavaString(
+        env, &live_file_meta_data->name, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      return nullptr;
+    }
 
-      rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
+    jstring jpath = rocksdb::JniUtil::toJavaString(
+        env, &live_file_meta_data->db_path, true);
+    if (env->ExceptionCheck()) {
+      // exception occurred creating java string
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfile_name);
+      return nullptr;
+    }
 
-      auto status = op(key_slice);
+    jbyteArray jsmallest_key = rocksdb::JniUtil::copyBytes(
+        env, live_file_meta_data->smallestkey);
+    if (jsmallest_key == nullptr) {
+      // exception occurred creating java byte array
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      return nullptr;
+    }
 
-      if(key != nullptr) {
-        env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
-      }
+    jbyteArray jlargest_key = rocksdb::JniUtil::copyBytes(
+        env, live_file_meta_data->largestkey);
+    if (jlargest_key == nullptr) {
+      // exception occurred creating java byte array
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      env->DeleteLocalRef(jsmallest_key);
+      return nullptr;
+    }
 
-      return std::unique_ptr<rocksdb::Status>(new rocksdb::Status(status));
+    jobject jlive_file_meta_data = env->NewObject(jclazz, mid,
+        jcolumn_family_name,
+        static_cast<jint>(live_file_meta_data->level),
+        jfile_name,
+        jpath,
+        static_cast<jlong>(live_file_meta_data->size),
+        static_cast<jlong>(live_file_meta_data->smallest_seqno),
+        static_cast<jlong>(live_file_meta_data->largest_seqno),
+        jsmallest_key,
+        jlargest_key,
+        static_cast<jlong>(live_file_meta_data->num_reads_sampled),
+        static_cast<jboolean>(live_file_meta_data->being_compacted),
+        static_cast<jlong>(live_file_meta_data->num_entries),
+        static_cast<jlong>(live_file_meta_data->num_deletions)
+    );
+
+    if (env->ExceptionCheck()) {
+      env->DeleteLocalRef(jcolumn_family_name);
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      env->DeleteLocalRef(jsmallest_key);
+      env->DeleteLocalRef(jlargest_key);
+      return nullptr;
     }
 
-    /*
-     * Helper for operations on a value
-     * for example WriteBatchWithIndex->GetFromBatch
-     */
-    static jbyteArray v_op(
-        std::function<rocksdb::Status(rocksdb::Slice, std::string*)> op,
-        JNIEnv* env, jbyteArray jkey, jint jkey_len) {
-      jbyte* key = env->GetByteArrayElements(jkey, nullptr);
-      if(env->ExceptionCheck()) {
-        // exception thrown: OutOfMemoryError
-        return nullptr;
-      }
+    // cleanup
+    env->DeleteLocalRef(jcolumn_family_name);
+    env->DeleteLocalRef(jfile_name);
+    env->DeleteLocalRef(jpath);
+    env->DeleteLocalRef(jsmallest_key);
+    env->DeleteLocalRef(jlargest_key);
 
-      rocksdb::Slice key_slice(reinterpret_cast<char*>(key), jkey_len);
+    return jlive_file_meta_data;
+  }
 
-      std::string value;
-      rocksdb::Status s = op(key_slice, &value);
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/LiveFileMetaData");
+  }
+};
 
-      if(key != nullptr) {
-        env->ReleaseByteArrayElements(jkey, key, JNI_ABORT);
-      }
+class SstFileMetaDataJni : public JavaClass {
+ public:
+  /**
+   * Create a new Java org.rocksdb.SstFileMetaData object.
+   *
+   * @param env A pointer to the Java environment
+   * @param sst_file_meta_data A Cpp sst file meta data object
+   *
+   * @return A reference to a Java org.rocksdb.SstFileMetaData object, or
+   * nullptr if an an exception occurs
+   */
+  static jobject fromCppSstFileMetaData(JNIEnv* env,
+      const rocksdb::SstFileMetaData* sst_file_meta_data) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
 
-      if (s.IsNotFound()) {
-        return nullptr;
-      }
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(Ljava/lang/String;Ljava/lang/String;JJJ[B[BJZJJ)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
 
-      if (s.ok()) {
-        jbyteArray jret_value =
-            env->NewByteArray(static_cast<jsize>(value.size()));
-        if(jret_value == nullptr) {
-          // exception thrown: OutOfMemoryError
-          return nullptr;
-        }
+    jstring jfile_name = rocksdb::JniUtil::toJavaString(
+        env, &sst_file_meta_data->name, true);
+    if (jfile_name == nullptr) {
+      // exception occurred creating java byte array
+      return nullptr;
+    }
 
-        env->SetByteArrayRegion(jret_value, 0, static_cast<jsize>(value.size()),
-                                const_cast<jbyte*>(reinterpret_cast<const jbyte*>(value.c_str())));
-        if(env->ExceptionCheck()) {
-          // exception thrown: ArrayIndexOutOfBoundsException
-          if(jret_value != nullptr) {
-            env->DeleteLocalRef(jret_value);
-          }
-          return nullptr;
-        }
+    jstring jpath = rocksdb::JniUtil::toJavaString(
+        env, &sst_file_meta_data->db_path, true);
+    if (jpath == nullptr) {
+      // exception occurred creating java byte array
+      env->DeleteLocalRef(jfile_name);
+      return nullptr;
+    }
 
-        return jret_value;
-      }
+    jbyteArray jsmallest_key = rocksdb::JniUtil::copyBytes(
+        env, sst_file_meta_data->smallestkey);
+    if (jsmallest_key == nullptr) {
+      // exception occurred creating java byte array
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      return nullptr;
+    }
 
-      rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
+    jbyteArray jlargest_key = rocksdb::JniUtil::copyBytes(
+        env, sst_file_meta_data->largestkey);
+    if (jlargest_key == nullptr) {
+      // exception occurred creating java byte array
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      env->DeleteLocalRef(jsmallest_key);
+      return nullptr;
+    }
+
+    jobject jsst_file_meta_data = env->NewObject(jclazz, mid,
+        jfile_name,
+        jpath,
+        static_cast<jlong>(sst_file_meta_data->size),
+        static_cast<jint>(sst_file_meta_data->smallest_seqno),
+        static_cast<jlong>(sst_file_meta_data->largest_seqno),
+        jsmallest_key,
+        jlargest_key,
+        static_cast<jlong>(sst_file_meta_data->num_reads_sampled),
+        static_cast<jboolean>(sst_file_meta_data->being_compacted),
+        static_cast<jlong>(sst_file_meta_data->num_entries),
+        static_cast<jlong>(sst_file_meta_data->num_deletions)
+    );
+
+    if (env->ExceptionCheck()) {
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      env->DeleteLocalRef(jsmallest_key);
+      env->DeleteLocalRef(jlargest_key);
       return nullptr;
     }
+
+    // cleanup
+      env->DeleteLocalRef(jfile_name);
+      env->DeleteLocalRef(jpath);
+      env->DeleteLocalRef(jsmallest_key);
+      env->DeleteLocalRef(jlargest_key);
+
+    return jsst_file_meta_data;
+  }
+
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/SstFileMetaData");
+  }
 };
 
-class ColumnFamilyDescriptorJni : public JavaClass {
+class LevelMetaDataJni : public JavaClass {
  public:
   /**
-   * Get the Java Class org.rocksdb.ColumnFamilyDescriptor
+   * Create a new Java org.rocksdb.LevelMetaData object.
    *
    * @param env A pointer to the Java environment
+   * @param level_meta_data A Cpp level meta data object
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return A reference to a Java org.rocksdb.LevelMetaData object, or
+   * nullptr if an an exception occurs
    */
+  static jobject fromCppLevelMetaData(JNIEnv* env,
+      const rocksdb::LevelMetaData* level_meta_data) {
+    jclass jclazz = getJClass(env);
+    if (jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
+    }
+
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(IJ[Lorg/rocksdb/SstFileMetaData;)V");
+    if (mid == nullptr) {
+      // exception thrown: NoSuchMethodException or OutOfMemoryError
+      return nullptr;
+    }
+
+    const jsize jlen =
+        static_cast<jsize>(level_meta_data->files.size());
+    jobjectArray jfiles = env->NewObjectArray(jlen, SstFileMetaDataJni::getJClass(env), nullptr);
+    if (jfiles == nullptr) {
+      // exception thrown: OutOfMemoryError
+      return nullptr;
+    }
+
+    jsize i = 0;
+    for (auto it = level_meta_data->files.begin();
+        it != level_meta_data->files.end(); ++it) {
+      jobject jfile = SstFileMetaDataJni::fromCppSstFileMetaData(env, &(*it));
+      if (jfile == nullptr) {
+        // exception occurred
+        env->DeleteLocalRef(jfiles);
+        return nullptr;
+      }
+      env->SetObjectArrayElement(jfiles, i++, jfile);
+    }
+
+    jobject jlevel_meta_data = env->NewObject(jclazz, mid,
+        static_cast<jint>(level_meta_data->level),
+        static_cast<jlong>(level_meta_data->size),
+        jfiles
+    );
+
+    if (env->ExceptionCheck()) {
+      env->DeleteLocalRef(jfiles);
+      return nullptr;
+    }
+
+    // cleanup
+    env->DeleteLocalRef(jfiles);
+
+    return jlevel_meta_data;
+  }
+
   static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "org/rocksdb/ColumnFamilyDescriptor");
+    return JavaClass::getJClass(env, "org/rocksdb/LevelMetaData");
   }
+};
 
+class ColumnFamilyMetaDataJni : public JavaClass {
+ public:
   /**
-   * Create a new Java org.rocksdb.ColumnFamilyDescriptor object with the same
-   * properties as the provided C++ rocksdb::ColumnFamilyDescriptor object
+   * Create a new Java org.rocksdb.ColumnFamilyMetaData object.
    *
    * @param env A pointer to the Java environment
-   * @param cfd A pointer to rocksdb::ColumnFamilyDescriptor object
+   * @param column_famly_meta_data A Cpp live file meta data object
    *
-   * @return A reference to a Java org.rocksdb.ColumnFamilyDescriptor object, or
+   * @return A reference to a Java org.rocksdb.ColumnFamilyMetaData object, or
    * nullptr if an an exception occurs
    */
-  static jobject construct(JNIEnv* env, ColumnFamilyDescriptor* cfd) {
-    jbyteArray jcf_name = JniUtil::copyBytes(env, cfd->name);
-    jobject cfopts = ColumnFamilyOptionsJni::construct(env, &(cfd->options));
-
+  static jobject fromCppColumnFamilyMetaData(JNIEnv* env,
+      const rocksdb::ColumnFamilyMetaData* column_famly_meta_data) {
     jclass jclazz = getJClass(env);
     if (jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    jmethodID mid = env->GetMethodID(jclazz, "<init>",
-                                     "([BLorg/rocksdb/ColumnFamilyOptions;)V");
+    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(JJ[B[Lorg/rocksdb/LevelMetaData;)V");
     if (mid == nullptr) {
       // exception thrown: NoSuchMethodException or OutOfMemoryError
-      env->DeleteLocalRef(jcf_name);
       return nullptr;
     }
 
-    jobject jcfd = env->NewObject(jclazz, mid, jcf_name, cfopts);
+    jbyteArray jname = rocksdb::JniUtil::copyBytes(
+        env, column_famly_meta_data->name);
+    if (jname == nullptr) {
+      // exception occurred creating java byte array
+      return nullptr;
+    }
+
+    const jsize jlen =
+        static_cast<jsize>(column_famly_meta_data->levels.size());
+    jobjectArray jlevels = env->NewObjectArray(jlen, LevelMetaDataJni::getJClass(env), nullptr);
+    if(jlevels == nullptr) {
+      // exception thrown: OutOfMemoryError
+      env->DeleteLocalRef(jname);
+      return nullptr;
+    }
+
+    jsize i = 0;
+    for (auto it = column_famly_meta_data->levels.begin();
+        it != column_famly_meta_data->levels.end(); ++it) {
+      jobject jlevel = LevelMetaDataJni::fromCppLevelMetaData(env, &(*it));
+      if (jlevel == nullptr) {
+        // exception occurred
+        env->DeleteLocalRef(jname);
+        env->DeleteLocalRef(jlevels);
+        return nullptr;
+      }
+      env->SetObjectArrayElement(jlevels, i++, jlevel);
+    }
+
+    jobject jcolumn_family_meta_data = env->NewObject(jclazz, mid,
+        static_cast<jlong>(column_famly_meta_data->size),
+        static_cast<jlong>(column_famly_meta_data->file_count),
+        jname,
+        jlevels
+    );
+
     if (env->ExceptionCheck()) {
-      env->DeleteLocalRef(jcf_name);
+      env->DeleteLocalRef(jname);
+      env->DeleteLocalRef(jlevels);
       return nullptr;
     }
 
-    return jcfd;
+    // cleanup
+    env->DeleteLocalRef(jname);
+    env->DeleteLocalRef(jlevels);
+
+    return jcolumn_family_meta_data;
+  }
+
+  static jclass getJClass(JNIEnv* env) {
+    return JavaClass::getJClass(env, "org/rocksdb/ColumnFamilyMetaData");
   }
+};
 
+// The portal class for org.rocksdb.AbstractTraceWriter
+class AbstractTraceWriterJni : public RocksDBNativeClass<
+    const rocksdb::TraceWriterJniCallback*,
+    AbstractTraceWriterJni> {
+ public:
   /**
-   * Get the Java Method: ColumnFamilyDescriptor#columnFamilyName
+   * Get the Java Class org.rocksdb.AbstractTraceWriter
+   *
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Class or nullptr if one of the
+   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
+   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   */
+  static jclass getJClass(JNIEnv* env) {
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/AbstractTraceWriter");
+  }
+
+  /**
+   * Get the Java Method: AbstractTraceWriter#write
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getColumnFamilyNameMethod(JNIEnv* env) {
+  static jmethodID getWriteProxyMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
-    if (jclazz == nullptr) {
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid = env->GetMethodID(jclazz, "columnFamilyName", "()[B");
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "writeProxy", "(J)S");
     assert(mid != nullptr);
     return mid;
   }
 
   /**
-   * Get the Java Method: ColumnFamilyDescriptor#columnFamilyOptions
+   * Get the Java Method: AbstractTraceWriter#closeWriter
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getColumnFamilyOptionsMethod(JNIEnv* env) {
+  static jmethodID getCloseWriterProxyMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
-    if (jclazz == nullptr) {
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
     static jmethodID mid = env->GetMethodID(
-        jclazz, "columnFamilyOptions", "()Lorg/rocksdb/ColumnFamilyOptions;");
+        jclazz, "closeWriterProxy", "()S");
     assert(mid != nullptr);
     return mid;
   }
-};
-
-class MapJni : public JavaClass {
- public:
-  /**
-   * Get the Java Class java.util.Map
-   *
-   * @param env A pointer to the Java environment
-   *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
-   */
-  static jclass getClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/util/Map");
-  }
 
   /**
-   * Get the Java Method: Map#put
+   * Get the Java Method: AbstractTraceWriter#getFileSize
    *
    * @param env A pointer to the Java environment
    *
    * @return The Java Method ID or nullptr if the class or method id could not
    *     be retieved
    */
-  static jmethodID getMapPutMethodId(JNIEnv* env) {
-    jclass jlist_clazz = getClass(env);
-    if(jlist_clazz == nullptr) {
+  static jmethodID getGetFileSizeMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    static jmethodID mid =
-        env->GetMethodID(jlist_clazz, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "getFileSize", "()J");
     assert(mid != nullptr);
     return mid;
   }
 };
 
-class HashMapJni : public JavaClass {
+// The portal class for org.rocksdb.AbstractWalFilter
+class AbstractWalFilterJni : public RocksDBNativeClass<
+    const rocksdb::WalFilterJniCallback*,
+    AbstractWalFilterJni> {
  public:
   /**
-   * Get the Java Class java.util.HashMap
+   * Get the Java Class org.rocksdb.AbstractWalFilter
    *
    * @param env A pointer to the Java environment
    *
@@ -4829,120 +6979,114 @@ class HashMapJni : public JavaClass {
    *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
    */
   static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/util/HashMap");
+    return RocksDBNativeClass::getJClass(env,
+        "org/rocksdb/AbstractWalFilter");
   }
 
   /**
-   * Create a new Java java.util.HashMap object.
+   * Get the Java Method: AbstractWalFilter#columnFamilyLogNumberMap
    *
    * @param env A pointer to the Java environment
    *
-   * @return A reference to a Java java.util.HashMap object, or
-   * nullptr if an an exception occurs
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jobject construct(JNIEnv* env, const uint32_t initial_capacity = 16) {
+  static jmethodID getColumnFamilyLogNumberMapMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
-    if (jclazz == nullptr) {
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    jmethodID mid = env->GetMethodID(jclazz, "<init>", "(I)V");
-    if (mid == nullptr) {
-      // exception thrown: NoSuchMethodException or OutOfMemoryError
-      return nullptr;
-    }
-
-    jobject jhash_map = env->NewObject(jclazz, mid, static_cast<jint>(initial_capacity));
-    if (env->ExceptionCheck()) {
-      return nullptr;
-    }
-
-    return jhash_map;
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "columnFamilyLogNumberMap",
+        "(Ljava/util/Map;Ljava/util/Map;)V");
+    assert(mid != nullptr);
+    return mid;
   }
 
   /**
-   * A function which maps a std::pair<K,V> to a std::pair<jobject, jobject>
+   * Get the Java Method: AbstractTraceWriter#logRecordFoundProxy
    *
-   * @return Either a pointer to a std::pair<jobject, jobject>, or nullptr
-   *     if an error occurs during the mapping
-   */
-  template <typename K, typename V>
-  using FnMapKV = std::function<std::unique_ptr<std::pair<jobject, jobject>> (const std::pair<K, V>&)>;
-
-  // template <class I, typename K, typename V, typename K1, typename V1, typename std::enable_if<std::is_same<typename std::iterator_traits<I>::value_type, std::pair<const K,V>>::value, int32_t>::type = 0>
-  // static void putAll(JNIEnv* env, const jobject jhash_map, I iterator, const FnMapKV<const K,V,K1,V1> &fn_map_kv) {
-  /**
-   * Returns true if it succeeds, false if an error occurs
+   * @param env A pointer to the Java environment
+   *
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  template<class iterator_type, typename K, typename V>
-  static bool putAll(JNIEnv* env, const jobject jhash_map, iterator_type iterator, iterator_type end, const FnMapKV<K, V> &fn_map_kv) {
-    const jmethodID jmid_put = rocksdb::MapJni::getMapPutMethodId(env);
-    if (jmid_put == nullptr) {
-      return false;
-    }
-
-    for (auto it = iterator; it != end; ++it) {
-      const std::unique_ptr<std::pair<jobject, jobject>> result = fn_map_kv(*it);
-      if (result == nullptr) {
-          // an error occurred during fn_map_kv
-          return false;
-      }
-      env->CallObjectMethod(jhash_map, jmid_put, result->first, result->second);
-      if (env->ExceptionCheck()) {
-        // exception occurred
-        env->DeleteLocalRef(result->second);
-        env->DeleteLocalRef(result->first);
-        return false;
-      }
-
-      // release local references
-      env->DeleteLocalRef(result->second);
-      env->DeleteLocalRef(result->first);
+  static jmethodID getLogRecordFoundProxyMethodId(JNIEnv* env) {
+    jclass jclazz = getJClass(env);
+    if(jclazz == nullptr) {
+      // exception occurred accessing class
+      return nullptr;
     }
 
-    return true;
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "logRecordFoundProxy", "(JLjava/lang/String;JJ)S");
+    assert(mid != nullptr);
+    return mid;
   }
-};
 
-class LongJni : public JavaClass {
- public:
   /**
-   * Get the Java Class java.lang.Long
+   * Get the Java Method: AbstractTraceWriter#name
    *
    * @param env A pointer to the Java environment
    *
-   * @return The Java Class or nullptr if one of the
-   *     ClassFormatError, ClassCircularityError, NoClassDefFoundError,
-   *     OutOfMemoryError or ExceptionInInitializerError exceptions is thrown
+   * @return The Java Method ID or nullptr if the class or method id could not
+   *     be retieved
    */
-  static jclass getJClass(JNIEnv* env) {
-    return JavaClass::getJClass(env, "java/lang/Long");
-  }
-
-  static jobject valueOf(JNIEnv* env, jlong jprimitive_long) {
+  static jmethodID getNameMethodId(JNIEnv* env) {
     jclass jclazz = getJClass(env);
-    if (jclazz == nullptr) {
+    if(jclazz == nullptr) {
       // exception occurred accessing class
       return nullptr;
     }
 
-    jmethodID mid =
-        env->GetStaticMethodID(jclazz, "valueOf", "(J)Ljava/lang/Long;");
-    if (mid == nullptr) {
-      // exception thrown: NoSuchMethodException or OutOfMemoryError
-      return nullptr;
-    }
+    static jmethodID mid = env->GetMethodID(
+        jclazz, "name", "()Ljava/lang/String;");
+    assert(mid != nullptr);
+    return mid;
+  }
+};
 
-    const jobject jlong_obj =
-        env->CallStaticObjectMethod(jclazz, mid, jprimitive_long);
-    if (env->ExceptionCheck()) {
-      // exception occurred
-      return nullptr;
-    }
+// The portal class for org.rocksdb.WalProcessingOption
+class WalProcessingOptionJni {
+ public:
+ // Returns the equivalent org.rocksdb.WalProcessingOption for the provided
+ // C++ rocksdb::WalFilter::WalProcessingOption enum
+ static jbyte toJavaWalProcessingOption(
+     const rocksdb::WalFilter::WalProcessingOption& wal_processing_option) {
+   switch(wal_processing_option) {
+     case rocksdb::WalFilter::WalProcessingOption::kContinueProcessing:
+       return 0x0;
+     case rocksdb::WalFilter::WalProcessingOption::kIgnoreCurrentRecord:
+       return 0x1;
+     case rocksdb::WalFilter::WalProcessingOption::kStopReplay:
+       return 0x2;
+     case rocksdb::WalFilter::WalProcessingOption::kCorruptedRecord:
+       return 0x3;
+     default:
+       return 0x7F;  // undefined
+   }
+ }
 
-    return jlong_obj;
-  }
+ // Returns the equivalent C++ rocksdb::WalFilter::WalProcessingOption enum for
+ // the provided Java org.rocksdb.WalProcessingOption
+ static rocksdb::WalFilter::WalProcessingOption toCppWalProcessingOption(
+     jbyte jwal_processing_option) {
+   switch(jwal_processing_option) {
+     case 0x0:
+       return rocksdb::WalFilter::WalProcessingOption::kContinueProcessing;
+     case 0x1:
+       return rocksdb::WalFilter::WalProcessingOption::kIgnoreCurrentRecord;
+     case 0x2:
+       return rocksdb::WalFilter::WalProcessingOption::kStopReplay;
+     case 0x3:
+       return rocksdb::WalFilter::WalProcessingOption::kCorruptedRecord;
+     default:
+       // undefined/default
+       return rocksdb::WalFilter::WalProcessingOption::kCorruptedRecord;
+   }
+ }
 };
 }  // namespace rocksdb
 #endif  // JAVA_ROCKSJNI_PORTAL_H_