Skip to content

Commit

Permalink
Java FilterPolicy: Add Filter::CreateFromString to Java (#387)
Browse files Browse the repository at this point in the history
This PR adds the ability to create a Filter (FilterPolicy) in Java via the CreateFromString methodology. This change allows the other filter policies (such as Ribbon Filter and plugin filters) to be created in Java using the C++ infrastructure.
  • Loading branch information
mrambacher authored Apr 19, 2023
1 parent 6f4d068 commit 5817370
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 3 deletions.
52 changes: 51 additions & 1 deletion java/rocksjni/config_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,24 @@ void Java_org_rocksdb_ConfigOptions_disposeInternal(JNIEnv *, jobject,
* Method: newConfigOptions
* Signature: ()J
*/
jlong Java_org_rocksdb_ConfigOptions_newConfigOptions(JNIEnv *, jclass) {
jlong Java_org_rocksdb_ConfigOptions_newConfigOptions__(JNIEnv *, jclass) {
auto *cfg_opt = new ROCKSDB_NAMESPACE::ConfigOptions();
return GET_CPLUSPLUS_POINTER(cfg_opt);
}

/*
* Class: org_rocksdb_ConfigOptions
* Method: newConfigOptions
* Signature: (ZZ)J
*/
jlong Java_org_rocksdb_ConfigOptions_newConfigOptions__ZZ(
JNIEnv *, jclass, jboolean unknown, jboolean unsupported) {
auto *cfg_opt = new ROCKSDB_NAMESPACE::ConfigOptions();
cfg_opt->ignore_unknown_options = static_cast<bool>(unknown);
cfg_opt->ignore_unsupported_options = static_cast<bool>(unsupported);
return GET_CPLUSPLUS_POINTER(cfg_opt);
}

/*
* Class: org_rocksdb_ConfigOptions
* Method: setDelimiter
Expand Down Expand Up @@ -65,6 +78,43 @@ void Java_org_rocksdb_ConfigOptions_setIgnoreUnknownOptions(JNIEnv *, jclass,
cfg_opt->ignore_unknown_options = static_cast<bool>(b);
}

/*
* Class: org_rocksdb_ConfigOptions
* Method: setIgnoreUnsupportedOptions
* Signature: (JZ)V
*/
void Java_org_rocksdb_ConfigOptions_setIgnoreUnsupportedOptions(JNIEnv *,
jclass,
jlong handle,
jboolean b) {
auto *cfg_opt = reinterpret_cast<ROCKSDB_NAMESPACE::ConfigOptions *>(handle);
cfg_opt->ignore_unsupported_options = static_cast<bool>(b);
}

/*
* Class: org_rocksdb_ConfigOptions
* Method: setInvokePrepareOptions
* Signature: (JZ)V
*/
void Java_org_rocksdb_ConfigOptions_setInvokePrepareOptions(JNIEnv *, jclass,
jlong handle,
jboolean b) {
auto *cfg_opt = reinterpret_cast<ROCKSDB_NAMESPACE::ConfigOptions *>(handle);
cfg_opt->invoke_prepare_options = static_cast<bool>(b);
}

/*
* Class: org_rocksdb_ConfigOptions
* Method: setMutableOptionsOnly
* Signature: (JZ)V
*/
void Java_org_rocksdb_ConfigOptions_setMutableOptionsOnly(JNIEnv *, jclass,
jlong handle,
jboolean b) {
auto *cfg_opt = reinterpret_cast<ROCKSDB_NAMESPACE::ConfigOptions *>(handle);
cfg_opt->mutable_options_only = static_cast<bool>(b);
}

/*
* Class: org_rocksdb_ConfigOptions
* Method: setInputStringsEscaped
Expand Down
50 changes: 50 additions & 0 deletions java/rocksjni/filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,56 @@
#include "rocksjni/cplusplus_to_java_convert.h"
#include "rocksjni/portal.h"

/*
* Class: org_rocksdb_Filter
* Method: createFilterFromString
* Signature: (Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL
Java_org_rocksdb_Filter_createFilterFromString__Ljava_lang_String_2(JNIEnv* env,
jclass,
jstring s) {
return ROCKSDB_NAMESPACE::CustomizableJni::createSharedFromString<
const ROCKSDB_NAMESPACE::FilterPolicy, ROCKSDB_NAMESPACE::FilterPolicy>(
env, s);
}

/*
* Class: org_rocksdb_Filter
* Method: createFilterFromString
* Signature: (JLjava/lang/String;)J
*/
JNIEXPORT jlong JNICALL
Java_org_rocksdb_Filter_createFilterFromString__JLjava_lang_String_2(
JNIEnv* env, jclass, jlong handle, jstring s) {
return ROCKSDB_NAMESPACE::CustomizableJni::createSharedFromString<
const ROCKSDB_NAMESPACE::FilterPolicy, ROCKSDB_NAMESPACE::FilterPolicy>(
env, handle, s);
}

/*
* Class: org_rocksdb_Filter
* Method: getId
* Signature: (J)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_org_rocksdb_Filter_getId(JNIEnv* env, jobject,
jlong jhandle) {
return ROCKSDB_NAMESPACE::CustomizableJni::getIdFromShared<
const ROCKSDB_NAMESPACE::FilterPolicy>(env, jhandle);
}

/*
* Class: org_rocksdb_Filter
* Method: isInstanceOf
* Signature: (J)Z
*/
JNIEXPORT jboolean JNICALL Java_org_rocksdb_Filter_isInstanceOf(JNIEnv* env,
jobject,
jlong jhandle,
jstring s) {
return ROCKSDB_NAMESPACE::CustomizableJni::isSharedInstanceOf<
const ROCKSDB_NAMESPACE::FilterPolicy>(env, jhandle, s);
}
/*
* Class: org_rocksdb_BloomFilter
* Method: createBloomFilter
Expand Down
91 changes: 91 additions & 0 deletions java/rocksjni/portal.h
Original file line number Diff line number Diff line change
Expand Up @@ -8702,5 +8702,96 @@ class FileOperationInfoJni : public JavaClass {
"(Ljava/lang/String;JJJJLorg/rocksdb/Status;)V");
}
};

// Class used to manage Customizable objects and their associated methods.
class CustomizableJni : public JavaClass {
public:
// Creates a new shared<R> via T::CreateFromString using the input
// ConfigOptions and options string.
template <typename R, typename T>
static jlong createSharedFromString(
const ROCKSDB_NAMESPACE::ConfigOptions& config, JNIEnv* env, jstring s) {
static const int kStatusError = -2;
static const int kArgumentError = -3;
const char* opts_str = env->GetStringUTFChars(s, nullptr);
if (opts_str == nullptr) {
// exception thrown: OutOfMemoryError
return kArgumentError;
}
std::shared_ptr<R>* result = new std::shared_ptr<R>();
auto status = T::CreateFromString(config, opts_str, result);
env->ReleaseStringUTFChars(s, opts_str);
if (status.ok()) {
return GET_CPLUSPLUS_POINTER(result);
} else {
delete result;
ROCKSDB_NAMESPACE::RocksDBExceptionJni::ThrowNew(env, status);
return kStatusError;
}
}

// Creates a new shared<R> via T::CreateFromString using the input options
// string. This signature ignores unsupported and unknown options and invokes
// prepare options
template <typename R, typename T>
static jlong createSharedFromString(JNIEnv* env, jstring s) {
ROCKSDB_NAMESPACE::ConfigOptions cfg_opts;
// Since this method is new in Java and does not need to follow any
// historical behavior, set the options to not ignore any errors and
// to invoke prepare options.
cfg_opts.ignore_unsupported_options = false;
cfg_opts.ignore_unknown_options = false;
cfg_opts.invoke_prepare_options = true;
return createSharedFromString<R, T>(cfg_opts, env, s);
}

// Creates a new shared<T> via T::CreateFromString using the input options
// string. This signature ignores unsupported and unknown options and invokes
// prepare options
template <typename T>
static jlong createSharedFromString(JNIEnv* env, jstring s) {
return createSharedFromString<T, T>(env, s);
}

// Creates a new shared<R> via T::CreateFromString using the input
// ConfigOptions handle and options string.
template <typename R, typename T>
static jlong createSharedFromString(JNIEnv* env, jlong handle, jstring s) {
auto* cfg_opts =
reinterpret_cast<ROCKSDB_NAMESPACE::ConfigOptions*>(handle);
return createSharedFromString<R, T>(*cfg_opts, env, s);
}

// Creates a new shared<T> via T::CreateFromString using the input
// ConfigOptions handle and options string.
template <typename T>
static jlong createSharedFromString(JNIEnv* env, jlong handle, jstring s) {
return createSharedFromString<T, T>(env, handle, s);
}

// Invokes and returns GetId on the shared<T> Customizable from the input
// handle
template <typename T>
static jstring getIdFromShared(JNIEnv* env, jlong handle) {
auto custom = reinterpret_cast<std::shared_ptr<T>*>(handle);
return env->NewStringUTF((*custom)->GetId().c_str());
}

// Returns true if the shared<T> Customizable handle is an InstanceOf the
// input string.
template <typename T>
static jboolean isSharedInstanceOf(JNIEnv* env, jlong handle, jstring s) {
const char* name = env->GetStringUTFChars(s, nullptr);
if (name == nullptr) {
// exception thrown: OutOfMemoryError
return false;
}
auto custom = reinterpret_cast<std::shared_ptr<T>*>(handle);
auto result = static_cast<jboolean>((*custom)->IsInstanceOf(name));
env->ReleaseStringUTFChars(s, name);
return result;
}
};

} // namespace ROCKSDB_NAMESPACE
#endif // JAVA_ROCKSJNI_PORTAL_H_
30 changes: 29 additions & 1 deletion java/src/main/java/org/rocksdb/ConfigOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,21 @@ public class ConfigOptions extends RocksObject {
}

/**
* Construct with default Options
* Construct with default ConfigOptions
*/
public ConfigOptions() {
super(newConfigOptions());
}

/**
* Constructs a ConfigOptions with the input values
* @param ignore_unknown_options Sets the options property to the input value
* @param ignore_unsupported_options Sets the options property to the input value
*/
public ConfigOptions(boolean ignore_unknown_options, boolean ignore_unsupported_options) {
super(newConfigOptions(ignore_unknown_options, ignore_unsupported_options));
}

public ConfigOptions setDelimiter(final String delimiter) {
setDelimiter(nativeHandle_, delimiter);
return this;
Expand All @@ -27,6 +36,21 @@ public ConfigOptions setIgnoreUnknownOptions(final boolean ignore) {
return this;
}

public ConfigOptions setIgnoreUnsupportedOptions(final boolean ignore) {
setIgnoreUnsupportedOptions(nativeHandle_, ignore);
return this;
}

public ConfigOptions setInvokePrepareOptions(final boolean prepare) {
setInvokePrepareOptions(nativeHandle_, prepare);
return this;
}

public ConfigOptions setMutableOptionsOnly(final boolean only) {
setMutableOptionsOnly(nativeHandle_, only);
return this;
}

public ConfigOptions setEnv(final Env env) {
setEnv(nativeHandle_, env.nativeHandle_);
return this;
Expand All @@ -45,9 +69,13 @@ public ConfigOptions setSanityLevel(final SanityLevel level) {
@Override protected final native void disposeInternal(final long handle);

private native static long newConfigOptions();
private native static long newConfigOptions(boolean unknown, boolean unsupported);
private native static void setEnv(final long handle, final long envHandle);
private native static void setDelimiter(final long handle, final String delimiter);
private native static void setIgnoreUnknownOptions(final long handle, final boolean ignore);
private native static void setIgnoreUnsupportedOptions(final long handle, final boolean ignore);
private native static void setInvokePrepareOptions(final long handle, final boolean prepare);
private native static void setMutableOptionsOnly(final long handle, final boolean only);
private native static void setInputStringsEscaped(final long handle, final boolean escaped);
private native static void setSanityLevel(final long handle, final byte level);
}
41 changes: 40 additions & 1 deletion java/src/main/java/org/rocksdb/Filter.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,31 @@
* DB::Get() call.
*/
//TODO(AR) should be renamed FilterPolicy
public abstract class Filter extends RocksObject {
public class Filter extends RocksObject {
/**
* Creates a new FilterPolicy based on the input value string and returns the
* result. The value might be an ID, and ID with properties, or an old-style
* policy string. The value describes the FilterPolicy being created.
* For BloomFilters, value may be a ":"-delimited value of the form:
* "bloomfilter:[bits_per_key]", e.g. ""bloomfilter:4"
* The above string is equivalent to calling NewBloomFilterPolicy(4).
* Creates a new Filter based on the input opts string
* @param opts The input string stating the name of the policy and its parameters
*/
public static Filter createFromString(final String opts) throws RocksDBException {
return new Filter(createFilterFromString(opts));
}

/**
* Creates a new FilterPolicy based on the input value string and returns the
* result.
* @param cfgOpts Controls how the filter is created
* @param opts The input string stating the name of the policy and its parameters
*/
public static Filter createFromString(final ConfigOptions cfgOpts, final String opts)
throws RocksDBException {
return new Filter(createFilterFromString(cfgOpts.nativeHandle_, opts));
}

protected Filter(final long nativeHandle) {
super(nativeHandle);
Expand All @@ -31,6 +55,21 @@ protected void disposeInternal() {
disposeInternal(nativeHandle_);
}

public String getId() {
assert (isOwningHandle());
return getId(nativeHandle_);
}

public boolean isInstanceOf(String name) {
assert (isOwningHandle());
return isInstanceOf(nativeHandle_, name);
}

@Override
protected final native void disposeInternal(final long handle);
protected native static long createFilterFromString(final String opts) throws RocksDBException;
protected native static long createFilterFromString(final long cfgHandle, final String opts)
throws RocksDBException;
private native String getId(long handle);
private native boolean isInstanceOf(long handle, String name);
}
24 changes: 24 additions & 0 deletions java/src/test/java/org/rocksdb/FilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package org.rocksdb;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.ClassRule;
import org.junit.Test;

Expand Down Expand Up @@ -33,7 +35,29 @@ public void filter() {
try(final Filter bloomFilter = new BloomFilter(10, false)) {
blockConfig.setFilterPolicy(bloomFilter);
options.setTableFormatConfig(blockConfig);
assertThat(bloomFilter.isInstanceOf("bloomfilter")).isTrue();
assertThat(bloomFilter.isInstanceOf("ribbonfilter")).isFalse();
}
}
}

@Test
public void createFromString() throws RocksDBException {
final BlockBasedTableConfig blockConfig = new BlockBasedTableConfig();
try (final Options options = new Options()) {
try (final Filter filter = Filter.createFromString("ribbonfilter:20")) {
assertThat(filter.getId()).startsWith("ribbonfilter");
assertThat(filter.isInstanceOf("ribbonfilter")).isTrue();
assertThat(filter.isInstanceOf("bloomfilter")).isFalse();
blockConfig.setFilterPolicy(filter);
options.setTableFormatConfig(blockConfig);
}
}
}

@Test(expected = RocksDBException.class)
public void createUnknownFromString() throws RocksDBException {
try (final Filter filter = Filter.createFromString("unknown")) {
}
}
}
Loading

0 comments on commit 5817370

Please sign in to comment.