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: 0774465940ee99001a78906e4fed4ef57068ad5cmain
parent
8dd0a7e11a
commit
560e984995
@ -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 <jni.h> |
||||
#include <memory> |
||||
|
||||
#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<rocksdb::CompactionFilterFactoryJniCallback>(cff); |
||||
return reinterpret_cast<jlong>(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<std::shared_ptr<rocksdb::CompactionFilterFactoryJniCallback> *>(jhandle); |
||||
delete ptr_sptr_cff; |
||||
} |
@ -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<CompactionFilter> 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<jboolean>(context.is_full_compaction), |
||||
static_cast<jboolean>(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<CompactionFilter*>(addr_compaction_filter); |
||||
|
||||
releaseJniEnv(attached_thread); |
||||
|
||||
return std::unique_ptr<CompactionFilter>(cff); |
||||
} |
||||
|
||||
} // namespace rocksdb
|
@ -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 <jni.h> |
||||
#include <memory> |
||||
|
||||
#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<CompactionFilter> CreateCompactionFilter( |
||||
const CompactionFilter::Context& context); |
||||
virtual const char* Name() const; |
||||
|
||||
private: |
||||
std::unique_ptr<const char[]> m_name; |
||||
jmethodID m_jcreate_compaction_filter_methodid; |
||||
}; |
||||
|
||||
} //namespace rocksdb
|
||||
|
||||
#endif // JAVA_ROCKSJNI_COMPACTION_FILTER_FACTORY_JNICALLBACK_H_
|
@ -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 <assert.h> |
||||
#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
|
@ -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 <jni.h> |
||||
|
||||
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_
|
@ -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 <jni.h> |
||||
|
||||
#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<rocksdb::JniCallback*>(handle); |
||||
} |
@ -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 <T> The concrete type of the compaction filter |
||||
*/ |
||||
public abstract class AbstractCompactionFilterFactory<T extends AbstractCompactionFilter<?>> |
||||
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); |
||||
} |
@ -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); |
||||
} |
@ -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<ColumnFamilyDescriptor> cfNames = Arrays.asList( |
||||
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY), |
||||
new ColumnFamilyDescriptor("new_cf".getBytes(), new_cf_opts)); |
||||
|
||||
final List<ColumnFamilyHandle> 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<RemoveEmptyValueCompactionFilter> { |
||||
@Override |
||||
public RemoveEmptyValueCompactionFilter createCompactionFilter(final AbstractCompactionFilter.Context context) { |
||||
return new RemoveEmptyValueCompactionFilter(); |
||||
} |
||||
|
||||
@Override |
||||
public String name() { |
||||
return "RemoveEmptyValueCompactionFilterFactory"; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue