From 560e9849959018f6a3fe210a24e78723d190541d Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Thu, 12 Oct 2017 11:06:51 -0700 Subject: [PATCH] Added CompactionFilterFactory support to RocksJava Summary: This PR also includes some cleanup, bugfixes and refactoring of the Java API. However these are really pre-cursors on the road to CompactionFilterFactory support. Closes https://github.com/facebook/rocksdb/pull/1241 Differential Revision: D6012778 Pulled By: sagar0 fbshipit-source-id: 0774465940ee99001a78906e4fed4ef57068ad5c --- java/Makefile | 4 +- java/rocksjni/compaction_filter_factory.cc | 37 +++++ .../compaction_filter_factory_jnicallback.cc | 76 +++++++++++ .../compaction_filter_factory_jnicallback.h | 35 +++++ java/rocksjni/comparator.cc | 32 ++--- java/rocksjni/comparatorjnicallback.cc | 108 ++++++--------- java/rocksjni/comparatorjnicallback.h | 13 +- java/rocksjni/jnicallback.cc | 52 ++++++++ java/rocksjni/jnicallback.h | 28 ++++ java/rocksjni/loggerjnicallback.cc | 38 ++---- java/rocksjni/loggerjnicallback.h | 7 +- java/rocksjni/options.cc | 57 ++++++-- java/rocksjni/portal.h | 110 ++++++++++++++- java/rocksjni/rocks_callback_object.cc | 27 ++++ java/rocksjni/sst_file_writerjni.cc | 20 ++- java/rocksjni/write_batch.cc | 13 -- java/rocksjni/write_batch_with_index.cc | 16 ++- java/rocksjni/writebatchhandlerjnicallback.cc | 32 ++--- java/rocksjni/writebatchhandlerjnicallback.h | 5 +- .../org/rocksdb/AbstractCompactionFilter.java | 29 ++++ .../AbstractCompactionFilterFactory.java | 75 +++++++++++ .../java/org/rocksdb/AbstractComparator.java | 22 +-- .../java/org/rocksdb/ColumnFamilyOptions.java | 41 +++++- .../src/main/java/org/rocksdb/Comparator.java | 9 +- .../java/org/rocksdb/DirectComparator.java | 9 +- java/src/main/java/org/rocksdb/Logger.java | 45 ++++--- java/src/main/java/org/rocksdb/Options.java | 5 +- .../java/org/rocksdb/RocksCallbackObject.java | 50 +++++++ .../main/java/org/rocksdb/SstFileWriter.java | 5 +- .../src/main/java/org/rocksdb/WriteBatch.java | 20 +-- .../java/org/rocksdb/WriteBatchWithIndex.java | 6 +- .../rocksdb/CompactionFilterFactoryTest.java | 78 +++++++++++ .../rocksdb/util/BytewiseComparatorTest.java | 126 +++++++++++------- src.mk | 4 + 34 files changed, 917 insertions(+), 317 deletions(-) create mode 100644 java/rocksjni/compaction_filter_factory.cc create mode 100644 java/rocksjni/compaction_filter_factory_jnicallback.cc create mode 100644 java/rocksjni/compaction_filter_factory_jnicallback.h create mode 100644 java/rocksjni/jnicallback.cc create mode 100644 java/rocksjni/jnicallback.h create mode 100644 java/rocksjni/rocks_callback_object.cc create mode 100644 java/src/main/java/org/rocksdb/AbstractCompactionFilterFactory.java create mode 100644 java/src/main/java/org/rocksdb/RocksCallbackObject.java create mode 100644 java/src/test/java/org/rocksdb/CompactionFilterFactoryTest.java diff --git a/java/Makefile b/java/Makefile index 92c7923ed..11c6c807e 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1,5 +1,5 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\ - org.rocksdb.AbstractComparator\ + org.rocksdb.AbstractCompactionFilterFactory\ org.rocksdb.AbstractSlice\ org.rocksdb.BackupEngine\ org.rocksdb.BackupableDBOptions\ @@ -36,6 +36,7 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\ org.rocksdb.ReadOptions\ org.rocksdb.RemoveEmptyValueCompactionFilter\ org.rocksdb.RestoreOptions\ + org.rocksdb.RocksCallbackObject\ org.rocksdb.RocksDB\ org.rocksdb.RocksEnv\ org.rocksdb.RocksIterator\ @@ -78,6 +79,7 @@ JAVA_TESTS = org.rocksdb.BackupableDBOptionsTest\ org.rocksdb.ClockCacheTest\ org.rocksdb.ColumnFamilyOptionsTest\ org.rocksdb.ColumnFamilyTest\ + org.rocksdb.CompactionFilterFactoryTest\ org.rocksdb.CompactionOptionsFIFOTest\ org.rocksdb.CompactionOptionsUniversalTest\ org.rocksdb.CompactionPriorityTest\ diff --git a/java/rocksjni/compaction_filter_factory.cc b/java/rocksjni/compaction_filter_factory.cc new file mode 100644 index 000000000..06dc795ac --- /dev/null +++ b/java/rocksjni/compaction_filter_factory.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// This file implements the "bridge" between Java and C++ for +// rocksdb::CompactionFilterFactory. + +#include +#include + +#include "include/org_rocksdb_AbstractCompactionFilterFactory.h" +#include "rocksjni/compaction_filter_factory_jnicallback.h" + +/* + * Class: org_rocksdb_AbstractCompactionFilterFactory + * Method: createNewCompactionFilterFactory0 + * Signature: ()J + */ +jlong Java_org_rocksdb_AbstractCompactionFilterFactory_createNewCompactionFilterFactory0( + JNIEnv* env, jobject jobj) { + auto* cff = new rocksdb::CompactionFilterFactoryJniCallback(env, jobj); + auto* ptr_sptr_cff = new std::shared_ptr(cff); + return reinterpret_cast(ptr_sptr_cff); +} + +/* + * Class: org_rocksdb_AbstractCompactionFilterFactory + * Method: disposeInternal + * Signature: (J)V + */ +void Java_org_rocksdb_AbstractCompactionFilterFactory_disposeInternal( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto* ptr_sptr_cff = + reinterpret_cast *>(jhandle); + delete ptr_sptr_cff; +} \ No newline at end of file diff --git a/java/rocksjni/compaction_filter_factory_jnicallback.cc b/java/rocksjni/compaction_filter_factory_jnicallback.cc new file mode 100644 index 000000000..c727a3e02 --- /dev/null +++ b/java/rocksjni/compaction_filter_factory_jnicallback.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// This file implements the callback "bridge" between Java and C++ for +// rocksdb::CompactionFilterFactory. + +#include "rocksjni/compaction_filter_factory_jnicallback.h" +#include "rocksjni/portal.h" + +namespace rocksdb { +CompactionFilterFactoryJniCallback::CompactionFilterFactoryJniCallback( + JNIEnv* env, jobject jcompaction_filter_factory) + : JniCallback(env, jcompaction_filter_factory) { + + // Note: The name of a CompactionFilterFactory will not change during + // it's lifetime, so we cache it in a global var + jmethodID jname_method_id = + AbstractCompactionFilterFactoryJni::getNameMethodId(env); + if(jname_method_id == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return; + } + + jstring jname = + (jstring)env->CallObjectMethod(m_jcallback_obj, jname_method_id); + if(env->ExceptionCheck()) { + // exception thrown + return; + } + jboolean has_exception = JNI_FALSE; + m_name = JniUtil::copyString(env, jname, &has_exception); // also releases jname + if (has_exception == JNI_TRUE) { + // exception thrown + return; + } + + m_jcreate_compaction_filter_methodid = + AbstractCompactionFilterFactoryJni::getCreateCompactionFilterMethodId(env); + if(m_jcreate_compaction_filter_methodid == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return; + } +} + +const char* CompactionFilterFactoryJniCallback::Name() const { + return m_name.get(); +} + +std::unique_ptr CompactionFilterFactoryJniCallback::CreateCompactionFilter( + const CompactionFilter::Context& context) { + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = getJniEnv(&attached_thread); + assert(env != nullptr); + + jlong addr_compaction_filter = env->CallLongMethod(m_jcallback_obj, + m_jcreate_compaction_filter_methodid, + static_cast(context.is_full_compaction), + static_cast(context.is_manual_compaction)); + + if(env->ExceptionCheck()) { + // exception thrown from CallLongMethod + env->ExceptionDescribe(); // print out exception to stderr + releaseJniEnv(attached_thread); + return nullptr; + } + + auto* cff = reinterpret_cast(addr_compaction_filter); + + releaseJniEnv(attached_thread); + + return std::unique_ptr(cff); +} + +} // namespace rocksdb diff --git a/java/rocksjni/compaction_filter_factory_jnicallback.h b/java/rocksjni/compaction_filter_factory_jnicallback.h new file mode 100644 index 000000000..10802edfd --- /dev/null +++ b/java/rocksjni/compaction_filter_factory_jnicallback.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// This file implements the callback "bridge" between Java and C++ for +// rocksdb::CompactionFilterFactory. + +#ifndef JAVA_ROCKSJNI_COMPACTION_FILTER_FACTORY_JNICALLBACK_H_ +#define JAVA_ROCKSJNI_COMPACTION_FILTER_FACTORY_JNICALLBACK_H_ + +#include +#include + +#include "rocksdb/compaction_filter.h" +#include "rocksjni/jnicallback.h" + +namespace rocksdb { + +class CompactionFilterFactoryJniCallback : public JniCallback, public CompactionFilterFactory { + public: + CompactionFilterFactoryJniCallback( + JNIEnv* env, jobject jcompaction_filter_factory); + virtual std::unique_ptr CreateCompactionFilter( + const CompactionFilter::Context& context); + virtual const char* Name() const; + + private: + std::unique_ptr m_name; + jmethodID m_jcreate_compaction_filter_methodid; +}; + +} //namespace rocksdb + +#endif // JAVA_ROCKSJNI_COMPACTION_FILTER_FACTORY_JNICALLBACK_H_ diff --git a/java/rocksjni/comparator.cc b/java/rocksjni/comparator.cc index 5955d0bf7..d4f02b28d 100644 --- a/java/rocksjni/comparator.cc +++ b/java/rocksjni/comparator.cc @@ -12,27 +12,11 @@ #include #include -#include "include/org_rocksdb_AbstractComparator.h" #include "include/org_rocksdb_Comparator.h" #include "include/org_rocksdb_DirectComparator.h" #include "rocksjni/comparatorjnicallback.h" #include "rocksjni/portal.h" -// /* @@ -42,10 +26,10 @@ void Java_org_rocksdb_AbstractComparator_disposeInternal( */ jlong Java_org_rocksdb_Comparator_createNewComparator0( JNIEnv* env, jobject jobj, jlong copt_handle) { - const rocksdb::ComparatorJniCallbackOptions* copt = - reinterpret_cast(copt_handle); - const rocksdb::ComparatorJniCallback* c = - new rocksdb::ComparatorJniCallback(env, jobj, copt); + auto* copt = + reinterpret_cast(copt_handle); + auto* c = + new rocksdb::ComparatorJniCallback(env, jobj, copt); return reinterpret_cast(c); } // @@ -59,10 +43,10 @@ jlong Java_org_rocksdb_Comparator_createNewComparator0( */ jlong Java_org_rocksdb_DirectComparator_createNewDirectComparator0( JNIEnv* env, jobject jobj, jlong copt_handle) { - const rocksdb::ComparatorJniCallbackOptions* copt = - reinterpret_cast(copt_handle); - const rocksdb::DirectComparatorJniCallback* c = - new rocksdb::DirectComparatorJniCallback(env, jobj, copt); + auto* copt = + reinterpret_cast(copt_handle); + auto* c = + new rocksdb::DirectComparatorJniCallback(env, jobj, copt); return reinterpret_cast(c); } // diff --git a/java/rocksjni/comparatorjnicallback.cc b/java/rocksjni/comparatorjnicallback.cc index 73ab46ad2..5b4d11b02 100644 --- a/java/rocksjni/comparatorjnicallback.cc +++ b/java/rocksjni/comparatorjnicallback.cc @@ -13,24 +13,9 @@ namespace rocksdb { BaseComparatorJniCallback::BaseComparatorJniCallback( JNIEnv* env, jobject jComparator, const ComparatorJniCallbackOptions* copt) - : mtx_compare(new port::Mutex(copt->use_adaptive_mutex)), + : JniCallback(env, jComparator), + mtx_compare(new port::Mutex(copt->use_adaptive_mutex)), mtx_findShortestSeparator(new port::Mutex(copt->use_adaptive_mutex)) { - // Note: Comparator methods may be accessed by multiple threads, - // so we ref the jvm not the env - const jint rs = env->GetJavaVM(&m_jvm); - if(rs != JNI_OK) { - // exception thrown - return; - } - - // Note: we want to access the Java Comparator instance - // across multiple method calls, so we create a global ref - assert(jComparator != nullptr); - m_jComparator = env->NewGlobalRef(jComparator); - if(m_jComparator == nullptr) { - // exception thrown: OutOfMemoryError - return; - } // Note: The name of a Comparator will not change during it's lifetime, // so we cache it in a global var @@ -39,7 +24,7 @@ BaseComparatorJniCallback::BaseComparatorJniCallback( // exception thrown: NoSuchMethodException or OutOfMemoryError return; } - jstring jsName = (jstring)env->CallObjectMethod(m_jComparator, jNameMethodId); + jstring jsName = (jstring)env->CallObjectMethod(m_jcallback_obj, jNameMethodId); if(env->ExceptionCheck()) { // exception thrown return; @@ -74,18 +59,18 @@ BaseComparatorJniCallback::BaseComparatorJniCallback( } const char* BaseComparatorJniCallback::Name() const { - return m_name.c_str(); + return m_name.get(); } int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); // TODO(adamretter): slice objects can potentially be cached using thread // local variables to avoid locking. Could make this configurable depending on // performance. - mtx_compare->Lock(); + mtx_compare.get()->Lock(); bool pending_exception = AbstractSliceJni::setHandle(env, m_jSliceA, &a, JNI_FALSE); @@ -94,7 +79,7 @@ int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { // exception thrown from setHandle or descendant env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return 0; } @@ -105,15 +90,15 @@ int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { // exception thrown from setHandle or descendant env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return 0; } - + jint result = - env->CallIntMethod(m_jComparator, m_jCompareMethodId, m_jSliceA, + env->CallIntMethod(m_jcallback_obj, m_jCompareMethodId, m_jSliceA, m_jSliceB); - mtx_compare->Unlock(); + mtx_compare.get()->Unlock(); if(env->ExceptionCheck()) { // exception thrown from CallIntMethod @@ -121,19 +106,19 @@ int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { result = 0; // we could not get a result from java callback so use 0 } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return result; } void BaseComparatorJniCallback::FindShortestSeparator( - std::string* start, const Slice& limit) const { + std::string* start, const Slice& limit) const { if (start == nullptr) { return; } jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); const char* startUtf = start->c_str(); @@ -143,21 +128,21 @@ void BaseComparatorJniCallback::FindShortestSeparator( if(env->ExceptionCheck()) { env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } if(env->ExceptionCheck()) { // exception thrown: OutOfMemoryError env->ExceptionDescribe(); // print out exception to stderr env->DeleteLocalRef(jsStart); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } // TODO(adamretter): slice object can potentially be cached using thread local // variable to avoid locking. Could make this configurable depending on // performance. - mtx_findShortestSeparator->Lock(); + mtx_findShortestSeparator.get()->Lock(); bool pending_exception = AbstractSliceJni::setHandle(env, m_jSliceLimit, &limit, JNI_FALSE); @@ -169,21 +154,21 @@ void BaseComparatorJniCallback::FindShortestSeparator( if(jsStart != nullptr) { env->DeleteLocalRef(jsStart); } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } jstring jsResultStart = - (jstring)env->CallObjectMethod(m_jComparator, + (jstring)env->CallObjectMethod(m_jcallback_obj, m_jFindShortestSeparatorMethodId, jsStart, m_jSliceLimit); - mtx_findShortestSeparator->Unlock(); + mtx_findShortestSeparator.get()->Unlock(); if(env->ExceptionCheck()) { // exception thrown from CallObjectMethod env->ExceptionDescribe(); // print out exception to stderr env->DeleteLocalRef(jsStart); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } @@ -192,29 +177,29 @@ void BaseComparatorJniCallback::FindShortestSeparator( if (jsResultStart != nullptr) { // update start with result jboolean has_exception = JNI_FALSE; - std::string result = JniUtil::copyString(env, jsResultStart, + std::unique_ptr result_start = JniUtil::copyString(env, jsResultStart, &has_exception); // also releases jsResultStart if (has_exception == JNI_TRUE) { if (env->ExceptionCheck()) { env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } - *start = result; + start->assign(result_start.get()); } - - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); } -void BaseComparatorJniCallback::FindShortSuccessor(std::string* key) const { +void BaseComparatorJniCallback::FindShortSuccessor( + std::string* key) const { if (key == nullptr) { return; } jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); const char* keyUtf = key->c_str(); @@ -224,25 +209,25 @@ void BaseComparatorJniCallback::FindShortSuccessor(std::string* key) const { if(env->ExceptionCheck()) { env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } else if(env->ExceptionCheck()) { // exception thrown: OutOfMemoryError env->ExceptionDescribe(); // print out exception to stderr env->DeleteLocalRef(jsKey); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } jstring jsResultKey = - (jstring)env->CallObjectMethod(m_jComparator, + (jstring)env->CallObjectMethod(m_jcallback_obj, m_jFindShortSuccessorMethodId, jsKey); if(env->ExceptionCheck()) { // exception thrown from CallObjectMethod env->ExceptionDescribe(); // print out exception to stderr env->DeleteLocalRef(jsKey); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } @@ -251,31 +236,20 @@ void BaseComparatorJniCallback::FindShortSuccessor(std::string* key) const { if (jsResultKey != nullptr) { // updates key with result, also releases jsResultKey. jboolean has_exception = JNI_FALSE; - std::string result = JniUtil::copyString(env, jsResultKey, &has_exception); + std::unique_ptr result_key = JniUtil::copyString(env, jsResultKey, + &has_exception); // also releases jsResultKey if (has_exception == JNI_TRUE) { if (env->ExceptionCheck()) { env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } - *key = result; - } - - JniUtil::releaseJniEnv(m_jvm, attached_thread); -} - -BaseComparatorJniCallback::~BaseComparatorJniCallback() { - jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); - assert(env != nullptr); - - if(m_jComparator != nullptr) { - env->DeleteGlobalRef(m_jComparator); + key->assign(result_key.get()); } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); } ComparatorJniCallback::ComparatorJniCallback( @@ -303,7 +277,7 @@ ComparatorJniCallback::ComparatorJniCallback( ComparatorJniCallback::~ComparatorJniCallback() { jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); if(m_jSliceA != nullptr) { @@ -318,7 +292,7 @@ ComparatorJniCallback::~ComparatorJniCallback() { env->DeleteGlobalRef(m_jSliceLimit); } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); } DirectComparatorJniCallback::DirectComparatorJniCallback( @@ -346,7 +320,7 @@ DirectComparatorJniCallback::DirectComparatorJniCallback( DirectComparatorJniCallback::~DirectComparatorJniCallback() { jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); if(m_jSliceA != nullptr) { @@ -361,6 +335,6 @@ DirectComparatorJniCallback::~DirectComparatorJniCallback() { env->DeleteGlobalRef(m_jSliceLimit); } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); } } // namespace rocksdb diff --git a/java/rocksjni/comparatorjnicallback.h b/java/rocksjni/comparatorjnicallback.h index a753008b3..0aa9cc0af 100644 --- a/java/rocksjni/comparatorjnicallback.h +++ b/java/rocksjni/comparatorjnicallback.h @@ -10,7 +10,9 @@ #define JAVA_ROCKSJNI_COMPARATORJNICALLBACK_H_ #include +#include #include +#include "rocksjni/jnicallback.h" #include "rocksdb/comparator.h" #include "rocksdb/slice.h" #include "port/port.h" @@ -44,12 +46,11 @@ struct ComparatorJniCallbackOptions { * introduce independent locking in regions of each of those methods * via the mutexs mtx_compare and mtx_findShortestSeparator respectively */ -class BaseComparatorJniCallback : public Comparator { +class BaseComparatorJniCallback : public JniCallback, public Comparator { public: BaseComparatorJniCallback( JNIEnv* env, jobject jComparator, const ComparatorJniCallbackOptions* copt); - virtual ~BaseComparatorJniCallback(); virtual const char* Name() const; virtual int Compare(const Slice& a, const Slice& b) const; virtual void FindShortestSeparator( @@ -58,17 +59,15 @@ class BaseComparatorJniCallback : public Comparator { private: // used for synchronisation in compare method - port::Mutex* mtx_compare; + std::unique_ptr mtx_compare; // used for synchronisation in findShortestSeparator method - port::Mutex* mtx_findShortestSeparator; - jobject m_jComparator; - std::string m_name; + std::unique_ptr mtx_findShortestSeparator; + std::unique_ptr m_name; jmethodID m_jCompareMethodId; jmethodID m_jFindShortestSeparatorMethodId; jmethodID m_jFindShortSuccessorMethodId; protected: - JavaVM* m_jvm; jobject m_jSliceA; jobject m_jSliceB; jobject m_jSliceLimit; diff --git a/java/rocksjni/jnicallback.cc b/java/rocksjni/jnicallback.cc new file mode 100644 index 000000000..301f46229 --- /dev/null +++ b/java/rocksjni/jnicallback.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// This file implements the callback "bridge" between Java and C++ for +// JNI Callbacks from C++ to sub-classes or org.rocksdb.RocksCallbackObject + +#include +#include "rocksjni/jnicallback.h" +#include "rocksjni/portal.h" + +namespace rocksdb { +JniCallback::JniCallback(JNIEnv* env, jobject jcallback_obj) { + // Note: jcallback_obj may be accessed by multiple threads, + // so we ref the jvm not the env + const jint rs = env->GetJavaVM(&m_jvm); + if(rs != JNI_OK) { + // exception thrown + return; + } + + // Note: we may want to access the Java callback object instance + // across multiple method calls, so we create a global ref + assert(jcallback_obj != nullptr); + m_jcallback_obj = env->NewGlobalRef(jcallback_obj); + if(jcallback_obj == nullptr) { + // exception thrown: OutOfMemoryError + return; + } +} + +JNIEnv* JniCallback::getJniEnv(jboolean* attached) const { + return JniUtil::getJniEnv(m_jvm, attached); +} + +void JniCallback::releaseJniEnv(jboolean& attached) const { + JniUtil::releaseJniEnv(m_jvm, attached); +} + +JniCallback::~JniCallback() { + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = getJniEnv(&attached_thread); + assert(env != nullptr); + + if(m_jcallback_obj != nullptr) { + env->DeleteGlobalRef(m_jcallback_obj); + } + + releaseJniEnv(attached_thread); +} +} // namespace rocksdb \ No newline at end of file diff --git a/java/rocksjni/jnicallback.h b/java/rocksjni/jnicallback.h new file mode 100644 index 000000000..c3068dec0 --- /dev/null +++ b/java/rocksjni/jnicallback.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// This file implements the callback "bridge" between Java and C++ for +// JNI Callbacks from C++ to sub-classes or org.rocksdb.RocksCallbackObject + +#ifndef JAVA_ROCKSJNI_JNICALLBACK_H_ +#define JAVA_ROCKSJNI_JNICALLBACK_H_ + +#include + +namespace rocksdb { + class JniCallback { + public: + JniCallback(JNIEnv* env, jobject jcallback_obj); + virtual ~JniCallback(); + + protected: + JavaVM* m_jvm; + jobject m_jcallback_obj; + JNIEnv* getJniEnv(jboolean* attached) const; + void releaseJniEnv(jboolean& attached) const; + }; +} + +#endif // JAVA_ROCKSJNI_JNICALLBACK_H_ \ No newline at end of file diff --git a/java/rocksjni/loggerjnicallback.cc b/java/rocksjni/loggerjnicallback.cc index 09140ed70..9c1074776 100644 --- a/java/rocksjni/loggerjnicallback.cc +++ b/java/rocksjni/loggerjnicallback.cc @@ -16,23 +16,8 @@ namespace rocksdb { LoggerJniCallback::LoggerJniCallback( - JNIEnv* env, jobject jlogger) { - // Note: Logger methods may be accessed by multiple threads, - // so we ref the jvm not the env - const jint rs = env->GetJavaVM(&m_jvm); - if(rs != JNI_OK) { - // exception thrown - return; - } + JNIEnv* env, jobject jlogger) : JniCallback(env, jlogger) { - // Note: we want to access the Java Logger instance - // across multiple method calls, so we create a global ref - assert(jlogger != nullptr); - m_jLogger = env->NewGlobalRef(jlogger); - if(m_jLogger == nullptr) { - // exception thrown: OutOfMemoryError - return; - } m_jLogMethodId = LoggerJni::getLogMethodId(env); if(m_jLogMethodId == nullptr) { // exception thrown: NoSuchMethodException or OutOfMemoryError @@ -153,7 +138,7 @@ void LoggerJniCallback::Logv(const InfoLogLevel log_level, // pass msg to java callback handler jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); jstring jmsg = env->NewStringUTF(msg.get()); @@ -162,28 +147,28 @@ void LoggerJniCallback::Logv(const InfoLogLevel log_level, if(env->ExceptionCheck()) { env->ExceptionDescribe(); // print out exception to stderr } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } if(env->ExceptionCheck()) { // exception thrown: OutOfMemoryError env->ExceptionDescribe(); // print out exception to stderr env->DeleteLocalRef(jmsg); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } - env->CallVoidMethod(m_jLogger, m_jLogMethodId, jlog_level, jmsg); + env->CallVoidMethod(m_jcallback_obj, m_jLogMethodId, jlog_level, jmsg); if(env->ExceptionCheck()) { // exception thrown env->ExceptionDescribe(); // print out exception to stderr env->DeleteLocalRef(jmsg); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); return; } env->DeleteLocalRef(jmsg); - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); } } @@ -202,16 +187,11 @@ std::unique_ptr LoggerJniCallback::format_str(const char* format, va_lis return buf; } - LoggerJniCallback::~LoggerJniCallback() { jboolean attached_thread = JNI_FALSE; - JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + JNIEnv* env = getJniEnv(&attached_thread); assert(env != nullptr); - if(m_jLogger != nullptr) { - env->DeleteGlobalRef(m_jLogger); - } - if(m_jdebug_level != nullptr) { env->DeleteGlobalRef(m_jdebug_level); } @@ -236,7 +216,7 @@ LoggerJniCallback::~LoggerJniCallback() { env->DeleteGlobalRef(m_jheader_level); } - JniUtil::releaseJniEnv(m_jvm, attached_thread); + releaseJniEnv(attached_thread); } } // namespace rocksdb diff --git a/java/rocksjni/loggerjnicallback.h b/java/rocksjni/loggerjnicallback.h index 2db85975d..80c5a1983 100644 --- a/java/rocksjni/loggerjnicallback.h +++ b/java/rocksjni/loggerjnicallback.h @@ -12,15 +12,16 @@ #include #include #include +#include "rocksjni/jnicallback.h" #include "port/port.h" #include "rocksdb/env.h" namespace rocksdb { - class LoggerJniCallback : public Logger { + class LoggerJniCallback : public JniCallback, public Logger { public: LoggerJniCallback(JNIEnv* env, jobject jLogger); - virtual ~LoggerJniCallback(); + ~LoggerJniCallback(); using Logger::SetInfoLogLevel; using Logger::GetInfoLogLevel; @@ -34,8 +35,6 @@ namespace rocksdb { const char* format, va_list ap); private: - JavaVM* m_jvm; - jobject m_jLogger; jmethodID m_jLogMethodId; jobject m_jdebug_level; jobject m_jinfo_level; diff --git a/java/rocksjni/options.cc b/java/rocksjni/options.cc index 4c71b5fad..45a5de865 100644 --- a/java/rocksjni/options.cc +++ b/java/rocksjni/options.cc @@ -146,12 +146,19 @@ void Java_org_rocksdb_Options_setComparatorHandle__JI( /* * Class: org_rocksdb_Options * Method: setComparatorHandle - * Signature: (JJ)V - */ -void Java_org_rocksdb_Options_setComparatorHandle__JJ( - JNIEnv* env, jobject jobj, jlong jopt_handle, jlong jcomparator_handle) { - reinterpret_cast(jopt_handle)->comparator = - reinterpret_cast(jcomparator_handle); + * Signature: (JJZ)V + */ +void Java_org_rocksdb_Options_setComparatorHandle__JJZ( + JNIEnv* env, jobject jobj, jlong jopt_handle, jlong jcomparator_handle, + jboolean is_direct) { + auto* opt = reinterpret_cast(jopt_handle); + if(is_direct) { + opt->comparator = + reinterpret_cast(jcomparator_handle); + } else { + opt->comparator = + reinterpret_cast(jcomparator_handle); + } } /* @@ -431,7 +438,7 @@ void Java_org_rocksdb_Options_setDbPaths( jtarget_sizes, ptr_jtarget_size, JNI_ABORT); return; } - std::string path = rocksdb::JniUtil::copyString( + std::string path = rocksdb::JniUtil::copyStdString( env, static_cast(jpath), &has_exception); env->DeleteLocalRef(jpath); @@ -2953,12 +2960,19 @@ void Java_org_rocksdb_ColumnFamilyOptions_setComparatorHandle__JI( /* * Class: org_rocksdb_ColumnFamilyOptions * Method: setComparatorHandle - * Signature: (JJ)V - */ -void Java_org_rocksdb_ColumnFamilyOptions_setComparatorHandle__JJ( - JNIEnv* env, jobject jobj, jlong jopt_handle, jlong jcomparator_handle) { - reinterpret_cast(jopt_handle)->comparator = - reinterpret_cast(jcomparator_handle); + * Signature: (JJZ)V + */ +void Java_org_rocksdb_ColumnFamilyOptions_setComparatorHandle__JJZ( + JNIEnv* env, jobject jobj, jlong jopt_handle, jlong jcomparator_handle, + jboolean is_direct) { + auto* opt = reinterpret_cast(jopt_handle); + if(is_direct) { + opt->comparator = + reinterpret_cast(jcomparator_handle); + } else { + opt->comparator = + reinterpret_cast(jcomparator_handle); + } } /* @@ -3005,6 +3019,21 @@ void Java_org_rocksdb_ColumnFamilyOptions_setCompactionFilterHandle( (jcompactionfilter_handle); } +/* + * Class: org_rocksdb_ColumnFamilyOptions + * Method: setCompactionFilterFactoryHandle + * Signature: (JJ)V + */ +void JNICALL Java_org_rocksdb_ColumnFamilyOptions_setCompactionFilterFactoryHandle( + JNIEnv* env , jobject jobj, jlong jopt_handle, + jlong jcompactionfilterfactory_handle) { + auto* cff_factory = + reinterpret_cast *>( + jcompactionfilterfactory_handle); + reinterpret_cast(jopt_handle)-> + compaction_filter_factory = *cff_factory; +} + /* * Class: org_rocksdb_ColumnFamilyOptions * Method: setWriteBufferSize @@ -4486,7 +4515,7 @@ void Java_org_rocksdb_DBOptions_setDbPaths( jtarget_sizes, ptr_jtarget_size, JNI_ABORT); return; } - std::string path = rocksdb::JniUtil::copyString( + std::string path = rocksdb::JniUtil::copyStdString( env, static_cast(jpath), &has_exception); env->DeleteLocalRef(jpath); diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index 2edea801b..ad77055b9 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -10,10 +10,12 @@ #ifndef JAVA_ROCKSJNI_PORTAL_H_ #define JAVA_ROCKSJNI_PORTAL_H_ +#include #include #include #include #include +#include #include #include @@ -22,6 +24,7 @@ #include "rocksdb/status.h" #include "rocksdb/utilities/backupable_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/writebatchhandlerjnicallback.h" @@ -1014,6 +1017,69 @@ class ComparatorOptionsJni : public RocksDBNativeClass< } }; +// The portal class for org.rocksdb.AbstractCompactionFilterFactory +class AbstractCompactionFilterFactoryJni : public RocksDBNativeClass< + const rocksdb::CompactionFilterFactoryJniCallback*, + AbstractCompactionFilterFactoryJni> { + public: + /** + * Get the Java Class org.rocksdb.AbstractCompactionFilterFactory + * + * @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/AbstractCompactionFilterFactory"); + } + + /** + * Get the Java Method: AbstractCompactionFilterFactory#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 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: 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 getCreateCompactionFilterMethodId(JNIEnv* env) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, + "createCompactionFilter", + "(ZZ)J"); + assert(mid != nullptr); + return mid; + } +}; + // The portal class for org.rocksdb.AbstractComparator class AbstractComparatorJni : public RocksDBNativeClass< const rocksdb::BaseComparatorJniCallback*, @@ -2988,6 +3054,46 @@ class JniUtil { return strs; } + /** + * 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 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; + } + + const jsize utf_len = env->GetStringUTFLength(js); + std::unique_ptr 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; + } + /** * Copies a jstring to a std::string * and releases the original jstring @@ -3003,8 +3109,8 @@ class JniUtil { * @return A std:string copy of the jstring, or an * empty std::string if has_exception == JNI_TRUE */ - static std::string copyString(JNIEnv* env, jstring js, - jboolean* has_exception) { + static std::string copyStdString(JNIEnv* env, jstring js, + jboolean* has_exception) { const char *utf = env->GetStringUTFChars(js, nullptr); if(utf == nullptr) { // exception thrown: OutOfMemoryError diff --git a/java/rocksjni/rocks_callback_object.cc b/java/rocksjni/rocks_callback_object.cc new file mode 100644 index 000000000..ddaf83b3f --- /dev/null +++ b/java/rocksjni/rocks_callback_object.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// This file implements the "bridge" between Java and C++ for +// JNI Callbacks from C++ to sub-classes or org.rocksdb.RocksCallbackObject + +#include + +#include "include/org_rocksdb_RocksCallbackObject.h" +#include "jnicallback.h" + +/* + * Class: org_rocksdb_RocksCallbackObject + * Method: disposeInternal + * Signature: (J)V + */ +void Java_org_rocksdb_RocksCallbackObject_disposeInternal( + JNIEnv* env, jobject jobj, jlong handle) { + // TODO(AR) is deleting from the super class JniCallback OK, or must we delete the subclass? + // Example hierarchies: + // 1) Comparator -> BaseComparatorJniCallback + JniCallback -> DirectComparatorJniCallback + // 2) Comparator -> BaseComparatorJniCallback + JniCallback -> ComparatorJniCallback + // I think this is okay, as Comparator and JniCallback both have virtual destructors... + delete reinterpret_cast(handle); +} \ No newline at end of file diff --git a/java/rocksjni/sst_file_writerjni.cc b/java/rocksjni/sst_file_writerjni.cc index ceb93384a..83f6b6145 100644 --- a/java/rocksjni/sst_file_writerjni.cc +++ b/java/rocksjni/sst_file_writerjni.cc @@ -20,16 +20,24 @@ /* * Class: org_rocksdb_SstFileWriter * Method: newSstFileWriter - * Signature: (JJJ)J + * Signature: (JJJZ)J */ -jlong Java_org_rocksdb_SstFileWriter_newSstFileWriter__JJJ(JNIEnv *env, jclass jcls, - jlong jenvoptions, - jlong joptions, - jlong jcomparator) { +jlong Java_org_rocksdb_SstFileWriter_newSstFileWriter__JJJZ(JNIEnv *env, + jclass jcls, jlong jenvoptions, jlong joptions, jlong jcomparator, + jboolean is_direct) { auto *env_options = reinterpret_cast(jenvoptions); auto *options = reinterpret_cast(joptions); - auto *comparator = reinterpret_cast(jcomparator); + + rocksdb::Comparator *comparator = nullptr; + if(is_direct) { + comparator = + reinterpret_cast(jcomparator); + } else { + comparator = + reinterpret_cast(jcomparator); + } + rocksdb::SstFileWriter *sst_file_writer = new rocksdb::SstFileWriter(*env_options, *options, comparator); return reinterpret_cast(sst_file_writer); diff --git a/java/rocksjni/write_batch.cc b/java/rocksjni/write_batch.cc index e84f6ed7d..4ec1244ed 100644 --- a/java/rocksjni/write_batch.cc +++ b/java/rocksjni/write_batch.cc @@ -298,16 +298,3 @@ jlong Java_org_rocksdb_WriteBatch_00024Handler_createNewHandler0( auto* wbjnic = new rocksdb::WriteBatchHandlerJniCallback(env, jobj); return reinterpret_cast(wbjnic); } - -/* - * Class: org_rocksdb_WriteBatch_Handler - * Method: disposeInternal - * Signature: (J)V - */ -void Java_org_rocksdb_WriteBatch_00024Handler_disposeInternal( - JNIEnv* env, jobject jobj, jlong handle) { - auto* wbjnic = - reinterpret_cast(handle); - assert(wbjnic != nullptr); - delete wbjnic; -} diff --git a/java/rocksjni/write_batch_with_index.cc b/java/rocksjni/write_batch_with_index.cc index 932d95fd8..bca85c100 100644 --- a/java/rocksjni/write_batch_with_index.cc +++ b/java/rocksjni/write_batch_with_index.cc @@ -39,14 +39,22 @@ jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__Z( /* * Class: org_rocksdb_WriteBatchWithIndex * Method: newWriteBatchWithIndex - * Signature: (JIZ)J + * Signature: (JZIZ)J */ -jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__JIZ( +jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__JZIZ( JNIEnv* env, jclass jcls, jlong jfallback_index_comparator_handle, - jint jreserved_bytes, jboolean joverwrite_key) { + jboolean is_direct, jint jreserved_bytes, jboolean joverwrite_key) { +rocksdb::Comparator *fallback_comparator = nullptr; +if(is_direct) { + fallback_comparator = + reinterpret_cast(jfallback_index_comparator_handle); +} else { + fallback_comparator = + reinterpret_cast(jfallback_index_comparator_handle); +} auto* wbwi = new rocksdb::WriteBatchWithIndex( - reinterpret_cast(jfallback_index_comparator_handle), + fallback_comparator, static_cast(jreserved_bytes), static_cast(joverwrite_key)); return reinterpret_cast(wbwi); } diff --git a/java/rocksjni/writebatchhandlerjnicallback.cc b/java/rocksjni/writebatchhandlerjnicallback.cc index 0f00766c5..47dc0f596 100644 --- a/java/rocksjni/writebatchhandlerjnicallback.cc +++ b/java/rocksjni/writebatchhandlerjnicallback.cc @@ -12,16 +12,7 @@ namespace rocksdb { WriteBatchHandlerJniCallback::WriteBatchHandlerJniCallback( JNIEnv* env, jobject jWriteBatchHandler) - : m_env(env) { - - // Note: we want to access the Java WriteBatchHandler instance - // across multiple method calls, so we create a global ref - assert(jWriteBatchHandler != nullptr); - m_jWriteBatchHandler = env->NewGlobalRef(jWriteBatchHandler); - if(m_jWriteBatchHandler == nullptr) { - // exception thrown: OutOfMemoryError - return; - } + : JniCallback(env, jWriteBatchHandler), m_env(env) { m_jPutMethodId = WriteBatchHandlerJni::getPutMethodId(env); if(m_jPutMethodId == nullptr) { @@ -83,7 +74,7 @@ void WriteBatchHandlerJniCallback::Put(const Slice& key, const Slice& value) { } m_env->CallVoidMethod( - m_jWriteBatchHandler, + m_jcallback_obj, m_jPutMethodId, j_key, j_value); @@ -130,7 +121,7 @@ void WriteBatchHandlerJniCallback::Merge(const Slice& key, const Slice& value) { } m_env->CallVoidMethod( - m_jWriteBatchHandler, + m_jcallback_obj, m_jMergeMethodId, j_key, j_value); @@ -165,7 +156,7 @@ void WriteBatchHandlerJniCallback::Delete(const Slice& key) { } m_env->CallVoidMethod( - m_jWriteBatchHandler, + m_jcallback_obj, m_jDeleteMethodId, j_key); if(m_env->ExceptionCheck()) { @@ -202,7 +193,7 @@ void WriteBatchHandlerJniCallback::DeleteRange(const Slice& beginKey, return; } - m_env->CallVoidMethod(m_jWriteBatchHandler, m_jDeleteRangeMethodId, + m_env->CallVoidMethod(m_jcallback_obj, m_jDeleteRangeMethodId, j_beginKey, j_endKey); if (m_env->ExceptionCheck()) { // exception thrown @@ -236,7 +227,7 @@ void WriteBatchHandlerJniCallback::LogData(const Slice& blob) { } m_env->CallVoidMethod( - m_jWriteBatchHandler, + m_jcallback_obj, m_jLogDataMethodId, j_blob); if(m_env->ExceptionCheck()) { @@ -255,7 +246,7 @@ void WriteBatchHandlerJniCallback::LogData(const Slice& blob) { bool WriteBatchHandlerJniCallback::Continue() { jboolean jContinue = m_env->CallBooleanMethod( - m_jWriteBatchHandler, + m_jcallback_obj, m_jContinueMethodId); if(m_env->ExceptionCheck()) { // exception thrown @@ -278,6 +269,9 @@ bool WriteBatchHandlerJniCallback::Continue() { * exception occurs */ jbyteArray WriteBatchHandlerJniCallback::sliceToJArray(const Slice& s) { + + // TODO(AR) move to JniUtil + jbyteArray ja = m_env->NewByteArray(static_cast(s.size())); if(ja == nullptr) { // exception thrown: OutOfMemoryError @@ -297,10 +291,4 @@ jbyteArray WriteBatchHandlerJniCallback::sliceToJArray(const Slice& s) { return ja; } - -WriteBatchHandlerJniCallback::~WriteBatchHandlerJniCallback() { - if(m_jWriteBatchHandler != nullptr) { - m_env->DeleteGlobalRef(m_jWriteBatchHandler); - } -} } // namespace rocksdb diff --git a/java/rocksjni/writebatchhandlerjnicallback.h b/java/rocksjni/writebatchhandlerjnicallback.h index 5d3dee3b1..9132027dd 100644 --- a/java/rocksjni/writebatchhandlerjnicallback.h +++ b/java/rocksjni/writebatchhandlerjnicallback.h @@ -10,6 +10,7 @@ #define JAVA_ROCKSJNI_WRITEBATCHHANDLERJNICALLBACK_H_ #include +#include "rocksjni/jnicallback.h" #include "rocksdb/write_batch.h" namespace rocksdb { @@ -20,11 +21,10 @@ namespace rocksdb { * which calls the appropriate Java method. * This enables Write Batch Handlers to be implemented in Java. */ -class WriteBatchHandlerJniCallback : public WriteBatch::Handler { +class WriteBatchHandlerJniCallback : public JniCallback, public WriteBatch::Handler { public: WriteBatchHandlerJniCallback( JNIEnv* env, jobject jWriteBackHandler); - ~WriteBatchHandlerJniCallback(); void Put(const Slice& key, const Slice& value); void Merge(const Slice& key, const Slice& value); void Delete(const Slice& key); @@ -34,7 +34,6 @@ class WriteBatchHandlerJniCallback : public WriteBatch::Handler { private: JNIEnv* m_env; - jobject m_jWriteBatchHandler; jbyteArray sliceToJArray(const Slice& s); jmethodID m_jPutMethodId; jmethodID m_jMergeMethodId; diff --git a/java/src/main/java/org/rocksdb/AbstractCompactionFilter.java b/java/src/main/java/org/rocksdb/AbstractCompactionFilter.java index 976401fba..2f0d4f3ca 100644 --- a/java/src/main/java/org/rocksdb/AbstractCompactionFilter.java +++ b/java/src/main/java/org/rocksdb/AbstractCompactionFilter.java @@ -14,6 +14,35 @@ package org.rocksdb; public abstract class AbstractCompactionFilter> extends RocksObject { + public static class Context { + private final boolean fullCompaction; + private final boolean manualCompaction; + + public Context(final boolean fullCompaction, final boolean manualCompaction) { + this.fullCompaction = fullCompaction; + this.manualCompaction = manualCompaction; + } + + /** + * Does this compaction run include all data files + * + * @return true if this is a full compaction run + */ + public boolean isFullCompaction() { + return fullCompaction; + } + + /** + * Is this compaction requested by the client, + * or is it occurring as an automatic compaction process + * + * @return true if the compaction was initiated by the client + */ + public boolean isManualCompaction() { + return manualCompaction; + } + } + protected AbstractCompactionFilter(final long nativeHandle) { super(nativeHandle); } diff --git a/java/src/main/java/org/rocksdb/AbstractCompactionFilterFactory.java b/java/src/main/java/org/rocksdb/AbstractCompactionFilterFactory.java new file mode 100644 index 000000000..b970263eb --- /dev/null +++ b/java/src/main/java/org/rocksdb/AbstractCompactionFilterFactory.java @@ -0,0 +1,75 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +package org.rocksdb; + +/** + * Each compaction will create a new {@link AbstractCompactionFilter} + * allowing the application to know about different compactions + * + * @param The concrete type of the compaction filter + */ +public abstract class AbstractCompactionFilterFactory> + extends RocksCallbackObject { + + public AbstractCompactionFilterFactory() { + super(null); + } + + @Override + protected long initializeNative(final long... nativeParameterHandles) { + return createNewCompactionFilterFactory0(); + } + + /** + * Called from JNI, see compaction_filter_factory_jnicallback.cc + * + * @param fullCompaction {@link AbstractCompactionFilter.Context#fullCompaction} + * @param manualCompaction {@link AbstractCompactionFilter.Context#manualCompaction} + * + * @return native handle of the CompactionFilter + */ + private long createCompactionFilter(final boolean fullCompaction, + final boolean manualCompaction) { + final T filter = createCompactionFilter( + new AbstractCompactionFilter.Context(fullCompaction, manualCompaction)); + + // CompactionFilterFactory::CreateCompactionFilter returns a std::unique_ptr + // which therefore has ownership of the underlying native object + filter.disOwnNativeHandle(); + + return filter.nativeHandle_; + } + + /** + * Create a new compaction filter + * + * @param context The context describing the need for a new compaction filter + * + * @return A new instance of {@link AbstractCompactionFilter} + */ + public abstract T createCompactionFilter( + final AbstractCompactionFilter.Context context); + + /** + * A name which identifies this compaction filter + * + * The name will be printed to the LOG file on start up for diagnosis + */ + public abstract String name(); + + /** + * We override {@link RocksCallbackObject#disposeInternal()} + * as disposing of a rocksdb::AbstractCompactionFilterFactory requires + * a slightly different approach as it is a std::shared_ptr + */ + @Override + protected void disposeInternal() { + disposeInternal(nativeHandle_); + } + + private native long createNewCompactionFilterFactory0(); + private native void disposeInternal(final long handle); +} diff --git a/java/src/main/java/org/rocksdb/AbstractComparator.java b/java/src/main/java/org/rocksdb/AbstractComparator.java index 0fc4a19df..00484236c 100644 --- a/java/src/main/java/org/rocksdb/AbstractComparator.java +++ b/java/src/main/java/org/rocksdb/AbstractComparator.java @@ -15,10 +15,10 @@ package org.rocksdb; * @see org.rocksdb.DirectComparator */ public abstract class AbstractComparator> - extends AbstractImmutableNativeReference { + extends RocksCallbackObject { - protected AbstractComparator() { - super(true); + protected AbstractComparator(final ComparatorOptions copt) { + super(copt.nativeHandle_); } /** @@ -87,20 +87,4 @@ public abstract class AbstractComparator> public String findShortSuccessor(final String key) { return null; } - - /** - * Deletes underlying C++ comparator pointer. - * - * Note that this function should be called only after all - * RocksDB instances referencing the comparator are closed. - * Otherwise an undefined behavior will occur. - */ - @Override - protected void disposeInternal() { - disposeInternal(getNativeHandle()); - } - - protected abstract long getNativeHandle(); - - private native void disposeInternal(final long handle); } diff --git a/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java b/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java index 6665cc493..e28a1d80d 100644 --- a/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java +++ b/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java @@ -130,7 +130,8 @@ public class ColumnFamilyOptions extends RocksObject public ColumnFamilyOptions setComparator( final AbstractComparator> comparator) { assert (isOwningHandle()); - setComparatorHandle(nativeHandle_, comparator.getNativeHandle()); + setComparatorHandle(nativeHandle_, comparator.nativeHandle_, + comparator instanceof DirectComparator); comparator_ = comparator; return this; } @@ -153,6 +154,22 @@ public class ColumnFamilyOptions extends RocksObject return this; } + /** + * A single CompactionFilter instance to call into during compaction. + * Allows an application to modify/delete a key-value during background + * compaction. + * + * If the client requires a new compaction filter to be used for different + * compaction runs, it can specify call + * {@link #setCompactionFilterFactory(AbstractCompactionFilterFactory)} + * instead. + * + * The client should specify only set one of the two. + * {@link #setCompactionFilter(AbstractCompactionFilter)} takes precedence + * over {@link #setCompactionFilterFactory(AbstractCompactionFilterFactory)} + * if the client specifies both. + */ + //TODO(AR) need to set a note on the concurrency of the compaction filter used from this method public ColumnFamilyOptions setCompactionFilter( final AbstractCompactionFilter> compactionFilter) { @@ -161,6 +178,22 @@ public class ColumnFamilyOptions extends RocksObject return this; } + /** + * This is a factory that provides {@link AbstractCompactionFilter} objects + * which allow an application to modify/delete a key-value during background + * compaction. + * + * A new filter will be created on each compaction run. If multithreaded + * compaction is being used, each created CompactionFilter will only be used + * from a single thread and so does not need to be thread-safe. + */ + public ColumnFamilyOptions setCompactionFilterFactory(final AbstractCompactionFilterFactory> compactionFilterFactory) { + assert (isOwningHandle()); + setCompactionFilterFactoryHandle(nativeHandle_, compactionFilterFactory.nativeHandle_); + compactionFilterFactory_ = compactionFilterFactory; + return this; + } + @Override public ColumnFamilyOptions setWriteBufferSize(final long writeBufferSize) { assert(isOwningHandle()); @@ -761,11 +794,13 @@ public class ColumnFamilyOptions extends RocksObject long memtableMemoryBudget); private native void setComparatorHandle(long handle, int builtinComparator); private native void setComparatorHandle(long optHandle, - long comparatorHandle); + long comparatorHandle, boolean isDirect); private native void setMergeOperatorName(long handle, String name); private native void setMergeOperator(long handle, long mergeOperatorHandle); private native void setCompactionFilterHandle(long handle, long compactionFilterHandle); + private native void setCompactionFilterFactoryHandle(long handle, + long compactionFilterFactoryHandle); private native void setWriteBufferSize(long handle, long writeBufferSize) throws IllegalArgumentException; private native long writeBufferSize(long handle); @@ -903,6 +938,8 @@ public class ColumnFamilyOptions extends RocksObject private TableFormatConfig tableFormatConfig_; private AbstractComparator> comparator_; private AbstractCompactionFilter> compactionFilter_; + AbstractCompactionFilterFactory> + compactionFilterFactory_; private CompactionOptionsUniversal compactionOptionsUniversal_; private CompactionOptionsFIFO compactionOptionsFIFO_; private CompressionOptions compressionOptions_; diff --git a/java/src/main/java/org/rocksdb/Comparator.java b/java/src/main/java/org/rocksdb/Comparator.java index 817e00fd2..ec5f4652d 100644 --- a/java/src/main/java/org/rocksdb/Comparator.java +++ b/java/src/main/java/org/rocksdb/Comparator.java @@ -16,16 +16,13 @@ package org.rocksdb; */ public abstract class Comparator extends AbstractComparator { - private final long nativeHandle_; - public Comparator(final ComparatorOptions copt) { - super(); - this.nativeHandle_ = createNewComparator0(copt.nativeHandle_); + super(copt); } @Override - protected final long getNativeHandle() { - return nativeHandle_; + protected long initializeNative(final long... nativeParameterHandles) { + return createNewComparator0(nativeParameterHandles[0]); } private native long createNewComparator0(final long comparatorOptionsHandle); diff --git a/java/src/main/java/org/rocksdb/DirectComparator.java b/java/src/main/java/org/rocksdb/DirectComparator.java index 4c37dfd56..347eb2644 100644 --- a/java/src/main/java/org/rocksdb/DirectComparator.java +++ b/java/src/main/java/org/rocksdb/DirectComparator.java @@ -16,16 +16,13 @@ package org.rocksdb; */ public abstract class DirectComparator extends AbstractComparator { - private final long nativeHandle_; - public DirectComparator(final ComparatorOptions copt) { - super(); - this.nativeHandle_ = createNewDirectComparator0(copt.nativeHandle_); + super(copt); } @Override - protected final long getNativeHandle() { - return nativeHandle_; + protected long initializeNative(final long... nativeParameterHandles) { + return createNewDirectComparator0(nativeParameterHandles[0]); } private native long createNewDirectComparator0( diff --git a/java/src/main/java/org/rocksdb/Logger.java b/java/src/main/java/org/rocksdb/Logger.java index 902125929..00a5d5674 100644 --- a/java/src/main/java/org/rocksdb/Logger.java +++ b/java/src/main/java/org/rocksdb/Logger.java @@ -35,9 +35,10 @@ package org.rocksdb; * {@link org.rocksdb.InfoLogLevel#FATAL_LEVEL}. *

*/ -public abstract class Logger extends AbstractImmutableNativeReference { +public abstract class Logger extends RocksCallbackObject { - final long nativeHandle_; + private final static long WITH_OPTIONS = 0; + private final static long WITH_DBOPTIONS = 1; /** *

AbstractLogger constructor.

@@ -49,8 +50,8 @@ public abstract class Logger extends AbstractImmutableNativeReference { * @param options {@link org.rocksdb.Options} instance. */ public Logger(final Options options) { - super(true); - this.nativeHandle_ = createNewLoggerOptions(options.nativeHandle_); + super(options.nativeHandle_, WITH_OPTIONS); + } /** @@ -63,8 +64,18 @@ public abstract class Logger extends AbstractImmutableNativeReference { * @param dboptions {@link org.rocksdb.DBOptions} instance. */ public Logger(final DBOptions dboptions) { - super(true); - this.nativeHandle_ = createNewLoggerDbOptions(dboptions.nativeHandle_); + super(dboptions.nativeHandle_, WITH_DBOPTIONS); + } + + @Override + protected long initializeNative(long... nativeParameterHandles) { + if(nativeParameterHandles[1] == WITH_OPTIONS) { + return createNewLoggerOptions(nativeParameterHandles[0]); + } else if(nativeParameterHandles[1] == WITH_DBOPTIONS) { + return createNewLoggerDbOptions(nativeParameterHandles[0]); + } else { + throw new IllegalArgumentException(); + } } /** @@ -89,17 +100,6 @@ public abstract class Logger extends AbstractImmutableNativeReference { protected abstract void log(InfoLogLevel infoLogLevel, String logMsg); - /** - * Deletes underlying C++ slice pointer. - * Note that this function should be called only after all - * RocksDB instances referencing the slice are closed. - * Otherwise an undefined behavior will occur. - */ - @Override - protected void disposeInternal() { - disposeInternal(nativeHandle_); - } - protected native long createNewLoggerOptions( long options); protected native long createNewLoggerDbOptions( @@ -107,5 +107,16 @@ public abstract class Logger extends AbstractImmutableNativeReference { protected native void setInfoLogLevel(long handle, byte infoLogLevel); protected native byte infoLogLevel(long handle); + + /** + * We override {@link RocksCallbackObject#disposeInternal()} + * as disposing of a rocksdb::LoggerJniCallback requires + * a slightly different approach as it is a std::shared_ptr + */ + @Override + protected void disposeInternal() { + disposeInternal(nativeHandle_); + } + private native void disposeInternal(final long handle); } diff --git a/java/src/main/java/org/rocksdb/Options.java b/java/src/main/java/org/rocksdb/Options.java index 8d2677824..3006304f3 100644 --- a/java/src/main/java/org/rocksdb/Options.java +++ b/java/src/main/java/org/rocksdb/Options.java @@ -169,7 +169,8 @@ public class Options extends RocksObject public Options setComparator( final AbstractComparator> comparator) { assert(isOwningHandle()); - setComparatorHandle(nativeHandle_, comparator.getNativeHandle()); + setComparatorHandle(nativeHandle_, comparator.nativeHandle_, + comparator instanceof DirectComparator); comparator_ = comparator; return this; } @@ -1733,7 +1734,7 @@ public class Options extends RocksObject long memtableMemoryBudget); private native void setComparatorHandle(long handle, int builtinComparator); private native void setComparatorHandle(long optHandle, - long comparatorHandle); + long comparatorHandle, boolean isDirect); private native void setMergeOperatorName( long handle, String name); private native void setMergeOperator( diff --git a/java/src/main/java/org/rocksdb/RocksCallbackObject.java b/java/src/main/java/org/rocksdb/RocksCallbackObject.java new file mode 100644 index 000000000..a662f78fd --- /dev/null +++ b/java/src/main/java/org/rocksdb/RocksCallbackObject.java @@ -0,0 +1,50 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +package org.rocksdb; + +/** + * RocksCallbackObject is similar to {@link RocksObject} but varies + * in its construction as it is designed for Java objects which have functions + * which are called from C++ via JNI. + * + * RocksCallbackObject is the base-class any RocksDB classes that acts as a + * callback from some underlying underlying native C++ {@code rocksdb} object. + * + * The use of {@code RocksObject} should always be preferred over + * {@link RocksCallbackObject} if callbacks are not required. + */ +public abstract class RocksCallbackObject extends + AbstractImmutableNativeReference { + + protected final long nativeHandle_; + + protected RocksCallbackObject(final long... nativeParameterHandles) { + super(true); + this.nativeHandle_ = initializeNative(nativeParameterHandles); + } + + /** + * Construct the Native C++ object which will callback + * to our object methods + * + * @param nativeParameterHandles An array of native handles for any parameter + * objects that are needed during construction + * + * @return The native handle of the C++ object which will callback to us + */ + protected abstract long initializeNative( + final long... nativeParameterHandles); + + /** + * Deletes underlying C++ native callback object pointer + */ + @Override + protected void disposeInternal() { + disposeInternal(nativeHandle_); + } + + private native void disposeInternal(final long handle); +} diff --git a/java/src/main/java/org/rocksdb/SstFileWriter.java b/java/src/main/java/org/rocksdb/SstFileWriter.java index 5f35f0f61..57879f94b 100644 --- a/java/src/main/java/org/rocksdb/SstFileWriter.java +++ b/java/src/main/java/org/rocksdb/SstFileWriter.java @@ -30,7 +30,8 @@ public class SstFileWriter extends RocksObject { public SstFileWriter(final EnvOptions envOptions, final Options options, final AbstractComparator> comparator) { super(newSstFileWriter( - envOptions.nativeHandle_, options.nativeHandle_, comparator.getNativeHandle())); + envOptions.nativeHandle_, options.nativeHandle_, comparator.nativeHandle_, + comparator instanceof DirectComparator)); } /** @@ -224,7 +225,7 @@ public void put(final byte[] key, final byte[] value) private native static long newSstFileWriter( final long envOptionsHandle, final long optionsHandle, - final long userComparatorHandle); + final long userComparatorHandle, final boolean isDirect); private native static long newSstFileWriter(final long envOptionsHandle, final long optionsHandle); diff --git a/java/src/main/java/org/rocksdb/WriteBatch.java b/java/src/main/java/org/rocksdb/WriteBatch.java index 272e9b4cd..2f7d0f12a 100644 --- a/java/src/main/java/org/rocksdb/WriteBatch.java +++ b/java/src/main/java/org/rocksdb/WriteBatch.java @@ -112,11 +112,14 @@ public class WriteBatch extends AbstractWriteBatch { * Handler callback for iterating over the contents of a batch. */ public static abstract class Handler - extends AbstractImmutableNativeReference { - private final long nativeHandle_; + extends RocksCallbackObject { public Handler() { - super(true); - this.nativeHandle_ = createNewHandler0(); + super(null); + } + + @Override + protected long initializeNative(final long... nativeParameterHandles) { + return createNewHandler0(); } public abstract void put(byte[] key, byte[] value); @@ -139,15 +142,6 @@ public class WriteBatch extends AbstractWriteBatch { return true; } - /** - * Deletes underlying C++ handler pointer. - */ - @Override - protected void disposeInternal() { - disposeInternal(nativeHandle_); - } - private native long createNewHandler0(); - private native void disposeInternal(final long handle); } } diff --git a/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java b/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java index fdf89b279..f3d49c92e 100644 --- a/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java +++ b/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java @@ -60,8 +60,8 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { final AbstractComparator> fallbackIndexComparator, final int reservedBytes, final boolean overwriteKey) { - super(newWriteBatchWithIndex(fallbackIndexComparator.getNativeHandle(), - reservedBytes, overwriteKey)); + super(newWriteBatchWithIndex(fallbackIndexComparator.nativeHandle_, + fallbackIndexComparator instanceof DirectComparator, reservedBytes, overwriteKey)); } /** @@ -263,7 +263,7 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { private native static long newWriteBatchWithIndex(); private native static long newWriteBatchWithIndex(final boolean overwriteKey); private native static long newWriteBatchWithIndex( - final long fallbackIndexComparatorHandle, final int reservedBytes, + final long fallbackIndexComparatorHandle, final boolean isDirect, final int reservedBytes, final boolean overwriteKey); private native long iterator0(final long handle); private native long iterator1(final long handle, final long cfHandle); diff --git a/java/src/test/java/org/rocksdb/CompactionFilterFactoryTest.java b/java/src/test/java/org/rocksdb/CompactionFilterFactoryTest.java new file mode 100644 index 000000000..e90307b0d --- /dev/null +++ b/java/src/test/java/org/rocksdb/CompactionFilterFactoryTest.java @@ -0,0 +1,78 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +package org.rocksdb; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CompactionFilterFactoryTest { + + @Rule + public TemporaryFolder dbFolder = new TemporaryFolder(); + + @Test + public void columnFamilyOptions_setCompactionFilterFactory() + throws RocksDBException { + try(final DBOptions options = new DBOptions() + .setCreateIfMissing(true) + .setCreateMissingColumnFamilies(true); + final RemoveEmptyValueCompactionFilterFactory compactionFilterFactory + = new RemoveEmptyValueCompactionFilterFactory(); + final ColumnFamilyOptions new_cf_opts + = new ColumnFamilyOptions() + .setCompactionFilterFactory(compactionFilterFactory)) { + + final List cfNames = Arrays.asList( + new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY), + new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts)); + + final List cfHandles = new ArrayList<>(); + + try (final RocksDB rocksDb = RocksDB.open(options, + dbFolder.getRoot().getAbsolutePath(), cfNames, cfHandles); + ) { + try { + final byte[] key1 = "key1".getBytes(); + final byte[] key2 = "key2".getBytes(); + + final byte[] value1 = "value1".getBytes(); + final byte[] value2 = new byte[0]; + + rocksDb.put(cfHandles.get(1), key1, value1); + rocksDb.put(cfHandles.get(1), key2, value2); + + rocksDb.compactRange(cfHandles.get(1)); + + assertThat(rocksDb.get(cfHandles.get(1), key1)).isEqualTo(value1); + assertThat(rocksDb.keyMayExist(cfHandles.get(1), key2, new StringBuilder())).isFalse(); + } finally { + for (final ColumnFamilyHandle cfHandle : cfHandles) { + cfHandle.close(); + } + } + } + } + } + + private static class RemoveEmptyValueCompactionFilterFactory extends AbstractCompactionFilterFactory { + @Override + public RemoveEmptyValueCompactionFilter createCompactionFilter(final AbstractCompactionFilter.Context context) { + return new RemoveEmptyValueCompactionFilter(); + } + + @Override + public String name() { + return "RemoveEmptyValueCompactionFilterFactory"; + } + } +} diff --git a/java/src/test/java/org/rocksdb/util/BytewiseComparatorTest.java b/java/src/test/java/org/rocksdb/util/BytewiseComparatorTest.java index d1352008a..8149a4800 100644 --- a/java/src/test/java/org/rocksdb/util/BytewiseComparatorTest.java +++ b/java/src/test/java/org/rocksdb/util/BytewiseComparatorTest.java @@ -41,13 +41,18 @@ public class BytewiseComparatorTest { final Path dbDir = Files.createTempDirectory("comparator_db_test"); try(final RocksDB db = openDatabase(dbDir, BuiltinComparator.BYTEWISE_COMPARATOR)) { + final Random rnd = new Random(rand_seed); - doRandomIterationTest( - db, - toJavaComparator(new BytewiseComparator(new ComparatorOptions())), - rnd, - 8, 100, 3 - ); + try(final ComparatorOptions copt2 = new ComparatorOptions(); + final Comparator comparator2 = new BytewiseComparator(copt2)) { + final java.util.Comparator jComparator = toJavaComparator(comparator2); + doRandomIterationTest( + db, + jComparator, + rnd, + 8, 100, 3 + ); + } } finally { removeData(dbDir); } @@ -63,15 +68,21 @@ public class BytewiseComparatorTest { throws IOException, RocksDBException { for(int rand_seed = 301; rand_seed < 306; rand_seed++) { final Path dbDir = Files.createTempDirectory("comparator_db_test"); - try(final RocksDB db = openDatabase(dbDir, new BytewiseComparator( - new ComparatorOptions()))) { + try(final ComparatorOptions copt = new ComparatorOptions(); + final Comparator comparator = new BytewiseComparator(copt); + final RocksDB db = openDatabase(dbDir, comparator)) { + final Random rnd = new Random(rand_seed); - doRandomIterationTest( - db, - toJavaComparator(new BytewiseComparator(new ComparatorOptions())), - rnd, - 8, 100, 3 - ); + try(final ComparatorOptions copt2 = new ComparatorOptions(); + final Comparator comparator2 = new BytewiseComparator(copt2)) { + final java.util.Comparator jComparator = toJavaComparator(comparator2); + doRandomIterationTest( + db, + jComparator, + rnd, + 8, 100, 3 + ); + } } finally { removeData(dbDir); } @@ -89,15 +100,18 @@ public class BytewiseComparatorTest { final Path dbDir = Files.createTempDirectory("comparator_db_test"); try(final RocksDB db = openDatabase(dbDir, BuiltinComparator.BYTEWISE_COMPARATOR)) { + final Random rnd = new Random(rand_seed); - doRandomIterationTest( - db, - toJavaComparator(new DirectBytewiseComparator( - new ComparatorOptions()) - ), - rnd, - 8, 100, 3 - ); + try(final ComparatorOptions copt2 = new ComparatorOptions(); + final DirectComparator comparator2 = new DirectBytewiseComparator(copt2)) { + final java.util.Comparator jComparator = toJavaComparator(comparator2); + doRandomIterationTest( + db, + jComparator, + rnd, + 8, 100, 3 + ); + } } finally { removeData(dbDir); } @@ -113,17 +127,21 @@ public class BytewiseComparatorTest { throws IOException, RocksDBException { for(int rand_seed = 301; rand_seed < 306; rand_seed++) { final Path dbDir = Files.createTempDirectory("comparator_db_test"); - try(final RocksDB db = openDatabase(dbDir, new DirectBytewiseComparator( - new ComparatorOptions()))) { + try (final ComparatorOptions copt = new ComparatorOptions(); + final DirectComparator comparator = new DirectBytewiseComparator(copt); + final RocksDB db = openDatabase(dbDir, comparator)) { + final Random rnd = new Random(rand_seed); - doRandomIterationTest( - db, - toJavaComparator(new DirectBytewiseComparator( - new ComparatorOptions()) - ), - rnd, - 8, 100, 3 - ); + try(final ComparatorOptions copt2 = new ComparatorOptions(); + final DirectComparator comparator2 = new DirectBytewiseComparator(copt2)) { + final java.util.Comparator jComparator = toJavaComparator(comparator2); + doRandomIterationTest( + db, + jComparator, + rnd, + 8, 100, 3 + ); + } } finally { removeData(dbDir); } @@ -141,15 +159,18 @@ public class BytewiseComparatorTest { final Path dbDir = Files.createTempDirectory("comparator_db_test"); try(final RocksDB db = openDatabase(dbDir, BuiltinComparator.REVERSE_BYTEWISE_COMPARATOR)) { + final Random rnd = new Random(rand_seed); - doRandomIterationTest( - db, - toJavaComparator( - new ReverseBytewiseComparator(new ComparatorOptions()) - ), - rnd, - 8, 100, 3 - ); + try(final ComparatorOptions copt2 = new ComparatorOptions(); + final Comparator comparator2 = new ReverseBytewiseComparator(copt2)) { + final java.util.Comparator jComparator = toJavaComparator(comparator2); + doRandomIterationTest( + db, + jComparator, + rnd, + 8, 100, 3 + ); + } } finally { removeData(dbDir); } @@ -163,20 +184,23 @@ public class BytewiseComparatorTest { @Test public void java_vs_java_reverseBytewiseComparator() throws IOException, RocksDBException { - for(int rand_seed = 301; rand_seed < 306; rand_seed++) { final Path dbDir = Files.createTempDirectory("comparator_db_test"); - try(final RocksDB db = openDatabase(dbDir, new ReverseBytewiseComparator( - new ComparatorOptions()))) { + try (final ComparatorOptions copt = new ComparatorOptions(); + final Comparator comparator = new ReverseBytewiseComparator(copt); + final RocksDB db = openDatabase(dbDir, comparator)) { + final Random rnd = new Random(rand_seed); - doRandomIterationTest( - db, - toJavaComparator( - new ReverseBytewiseComparator(new ComparatorOptions()) - ), - rnd, - 8, 100, 3 - ); + try(final ComparatorOptions copt2 = new ComparatorOptions(); + final Comparator comparator2 = new ReverseBytewiseComparator(copt2)) { + final java.util.Comparator jComparator = toJavaComparator(comparator2); + doRandomIterationTest( + db, + jComparator, + rnd, + 8, 100, 3 + ); + } } finally { removeData(dbDir); } diff --git a/src.mk b/src.mk index ee5135830..a4e404c4f 100644 --- a/src.mk +++ b/src.mk @@ -376,6 +376,8 @@ JNI_NATIVE_SOURCES = \ java/rocksjni/clock_cache.cc \ java/rocksjni/columnfamilyhandle.cc \ java/rocksjni/compaction_filter.cc \ + java/rocksjni/compaction_filter_factory.cc \ + java/rocksjni/compaction_filter_factory_jnicallback.cc \ java/rocksjni/compaction_options_fifo.cc \ java/rocksjni/compaction_options_universal.cc \ java/rocksjni/comparator.cc \ @@ -386,6 +388,7 @@ JNI_NATIVE_SOURCES = \ java/rocksjni/ingest_external_file_options.cc \ java/rocksjni/filter.cc \ java/rocksjni/iterator.cc \ + java/rocksjni/jnicallback.cc \ java/rocksjni/loggerjnicallback.cc \ java/rocksjni/lru_cache.cc \ java/rocksjni/memtablejni.cc \ @@ -397,6 +400,7 @@ JNI_NATIVE_SOURCES = \ java/rocksjni/cassandra_compactionfilterjni.cc \ java/rocksjni/cassandra_value_operator.cc \ java/rocksjni/restorejni.cc \ + java/rocksjni/rocks_callback_object.cc \ java/rocksjni/rocksjni.cc \ java/rocksjni/rocksdb_exception_test.cc \ java/rocksjni/slice.cc \