diff --git a/java/rocksjni/config_options.cc b/java/rocksjni/config_options.cc index 55a9cbb663..088c3631fe 100644 --- a/java/rocksjni/config_options.cc +++ b/java/rocksjni/config_options.cc @@ -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(unknown); + cfg_opt->ignore_unsupported_options = static_cast(unsupported); + return GET_CPLUSPLUS_POINTER(cfg_opt); +} + /* * Class: org_rocksdb_ConfigOptions * Method: setEnv @@ -78,6 +91,43 @@ void Java_org_rocksdb_ConfigOptions_setIgnoreUnknownOptions(JNIEnv *, jclass, cfg_opt->ignore_unknown_options = static_cast(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(handle); + cfg_opt->ignore_unsupported_options = static_cast(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(handle); + cfg_opt->invoke_prepare_options = static_cast(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(handle); + cfg_opt->mutable_options_only = static_cast(b); +} + /* * Class: org_rocksdb_ConfigOptions * Method: setInputStringsEscaped diff --git a/java/rocksjni/filter.cc b/java/rocksjni/filter.cc index ed22016d23..65f7e3af53 100644 --- a/java/rocksjni/filter.cc +++ b/java/rocksjni/filter.cc @@ -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 diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index 16120b0370..a176ee6788 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -8750,5 +8750,95 @@ class CompactRangeOptionsTimestampJni : public JavaClass { } }; +// Class used to manage Customizable objects and their associated methods. +class CustomizableJni : public JavaClass { + public: + // Creates a new shared via T::CreateFromString using the input + // ConfigOptions and options string. + template + 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* result = new std::shared_ptr(); + 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 via T::CreateFromString using the input options + // string. This signature ignores unsupported and unknown options and invokes + // prepare options + template + 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(cfg_opts, env, s); + } + + // Creates a new shared via T::CreateFromString using the input options + // string. This signature ignores unsupported and unknown options and invokes + // prepare options + template + static jlong createSharedFromString(JNIEnv* env, jstring s) { + return createSharedFromString(env, s); + } + + // Creates a new shared via T::CreateFromString using the input + // ConfigOptions handle and options string. + template + static jlong createSharedFromString(JNIEnv* env, jlong handle, jstring s) { + auto* cfg_opts = + reinterpret_cast(handle); + return createSharedFromString(*cfg_opts, env, s); + } + + // Creates a new shared via T::CreateFromString using the input + // ConfigOptions handle and options string. + template + static jlong createSharedFromString(JNIEnv* env, jlong handle, jstring s) { + return createSharedFromString(env, handle, s); + } + + // Invokes and returns GetId on the shared Customizable from the input + // handle + template + static jstring getIdFromShared(JNIEnv* env, jlong handle) { + auto custom = reinterpret_cast*>(handle); + return env->NewStringUTF((*custom)->GetId().c_str()); + } + + // Returns true if the shared Customizable handle is an InstanceOf the + // input string. + template + 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*>(handle); + auto result = static_cast((*custom)->IsInstanceOf(name)); + env->ReleaseStringUTFChars(s, name); + return result; + } +}; + } // namespace ROCKSDB_NAMESPACE #endif // JAVA_ROCKSJNI_PORTAL_H_ diff --git a/java/src/main/java/org/rocksdb/ConfigOptions.java b/java/src/main/java/org/rocksdb/ConfigOptions.java index 026f8b01d9..efa055547c 100644 --- a/java/src/main/java/org/rocksdb/ConfigOptions.java +++ b/java/src/main/java/org/rocksdb/ConfigOptions.java @@ -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; @@ -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; @@ -45,9 +69,13 @@ public ConfigOptions setSanityLevel(final SanityLevel level) { @Override protected final native void disposeInternal(final long handle); private static native long newConfigOptions(); + private native static long newConfigOptions(boolean unknown, boolean unsupported); private static native void setEnv(final long handle, final long envHandle); private static native void setDelimiter(final long handle, final String delimiter); private static native 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 static native void setInputStringsEscaped(final long handle, final boolean escaped); private static native void setSanityLevel(final long handle, final byte level); } diff --git a/java/src/main/java/org/rocksdb/Filter.java b/java/src/main/java/org/rocksdb/Filter.java index 7f490cf594..27b877931c 100644 --- a/java/src/main/java/org/rocksdb/Filter.java +++ b/java/src/main/java/org/rocksdb/Filter.java @@ -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); @@ -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); } diff --git a/java/src/test/java/org/rocksdb/FilterTest.java b/java/src/test/java/org/rocksdb/FilterTest.java index dc5c19fbc6..a4a6143193 100644 --- a/java/src/test/java/org/rocksdb/FilterTest.java +++ b/java/src/test/java/org/rocksdb/FilterTest.java @@ -5,6 +5,8 @@ package org.rocksdb; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.ClassRule; import org.junit.Test; @@ -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")) { + } + } } diff --git a/plugin/speedb/java/src/test/java/org/rocksdb/SpeedbFilterTest.java b/plugin/speedb/java/src/test/java/org/rocksdb/SpeedbFilterTest.java new file mode 100644 index 0000000000..f024f027ac --- /dev/null +++ b/plugin/speedb/java/src/test/java/org/rocksdb/SpeedbFilterTest.java @@ -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). + +package org.rocksdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.ClassRule; +import org.junit.Test; + +public class SpeedbFilterTest { + @ClassRule + public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE = + new RocksNativeLibraryResource(); + @Test + public void createFromString() throws RocksDBException { + final BlockBasedTableConfig blockConfig = new BlockBasedTableConfig(); + try (final Options options = new Options()) { + try (final Filter filter = Filter.createFromString("speedb.PairedBloomFilter:20")) { + assertThat(filter.isInstanceOf("speedb_paired_bloom_filter")).isTrue(); + assertThat(filter.isInstanceOf("speedb.PairedBloomFilter")).isTrue(); + assertThat(filter.isInstanceOf("bloomfilter")).isFalse(); + blockConfig.setFilterPolicy(filter); + options.setTableFormatConfig(blockConfig); + } + try (final Filter filter = Filter.createFromString("speedb_paired_bloom_filter:20")) { + assertThat(filter.isInstanceOf("speedb_paired_bloom_filter")).isTrue(); + assertThat(filter.isInstanceOf("speedb.PairedBloomFilter")).isTrue(); + assertThat(filter.isInstanceOf("bloomfilter")).isFalse(); + blockConfig.setFilterPolicy(filter); + options.setTableFormatConfig(blockConfig); + } + } + } +} diff --git a/plugin/speedb/speedb.mk b/plugin/speedb/speedb.mk index a19cfd6967..dd52fb88c4 100644 --- a/plugin/speedb/speedb.mk +++ b/plugin/speedb/speedb.mk @@ -27,3 +27,5 @@ speedb_HEADERS = \ speedb_TESTS = \ speedb_customizable_test.cc \ paired_filter/speedb_db_bloom_filter_test.cc \ + +speedb_JAVA_TESTS = org.rocksdb.SpeedbFilterTest \