From c6d464a9da7291e776b5a017f0a5d33d61f2518b Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Mon, 27 Feb 2017 16:26:12 -0800 Subject: [PATCH] Fixed various memory leaks and Java 8 JNI Compatibility Summary: I have manually audited the entire RocksJava code base. Sorry for the large pull-request, I have broken it down into many small atomic commits though. My initial intention was to fix the warnings that appear when running RocksJava on Java 8 with `-Xcheck:jni`, for example when running `make jtest` you would see many errors similar to: ``` WARNING in native method: JNI call made without checking exceptions when required to from CallObjectMethod WARNING in native method: JNI call made without checking exceptions when required to from CallVoidMethod WARNING in native method: JNI call made without checking exceptions when required to from CallStaticVoidMethod ... ``` A few of those warnings still remain, however they seem to come directly from the JVM and are not directly related to RocksJava; I am in contact with the OpenJDK hostpot-dev mailing list about these - http://mail.openjdk.java.net/pipermail/hotspot-dev/2017-February/025981.html. As a result of fixing these, I realised we were not r Closes https://github.com/facebook/rocksdb/pull/1890 Differential Revision: D4591758 Pulled By: siying fbshipit-source-id: 7f7fdf4 --- java/Makefile | 16 +- .../org/rocksdb/benchmark/DbBenchmark.java | 42 +- java/rocksjni/backupablejni.cc | 42 +- java/rocksjni/backupenginejni.cc | 40 +- java/rocksjni/checkpoint.cc | 17 +- java/rocksjni/columnfamilyhandle.cc | 5 +- java/rocksjni/compaction_filter.cc | 4 +- java/rocksjni/comparator.cc | 4 +- java/rocksjni/comparatorjnicallback.cc | 284 ++- java/rocksjni/comparatorjnicallback.h | 3 +- java/rocksjni/env.cc | 4 +- java/rocksjni/env_options.cc | 11 +- java/rocksjni/external_sst_file_info.cc | 48 +- java/rocksjni/filter.cc | 17 +- java/rocksjni/iterator.cc | 26 +- java/rocksjni/loggerjnicallback.cc | 267 +- java/rocksjni/loggerjnicallback.h | 4 +- java/rocksjni/merge_operator.cc | 25 +- java/rocksjni/options.cc | 343 ++- java/rocksjni/portal.h | 2169 ++++++++++++++--- java/rocksjni/ratelimiterjni.cc | 65 +- java/rocksjni/restorejni.cc | 4 +- java/rocksjni/rocksjni.cc | 789 ++++-- java/rocksjni/slice.cc | 132 +- java/rocksjni/sst_file_writerjni.cc | 11 +- java/rocksjni/statistics.cc | 26 +- java/rocksjni/transaction_log.cc | 9 +- java/rocksjni/ttl.cc | 105 +- java/rocksjni/write_batch.cc | 17 +- java/rocksjni/write_batch_test.cc | 10 + java/rocksjni/write_batch_with_index.cc | 62 +- java/rocksjni/writebatchhandlerjnicallback.cc | 167 +- .../main/java/RocksDBColumnFamilySample.java | 9 +- java/samples/src/main/java/RocksDBSample.java | 58 +- .../main/java/org/rocksdb/AbstractSlice.java | 14 + .../java/org/rocksdb/ColumnFamilyOptions.java | 2 +- java/src/main/java/org/rocksdb/DBOptions.java | 13 - .../java/org/rocksdb/DBOptionsInterface.java | 12 - .../main/java/org/rocksdb/DirectSlice.java | 49 +- .../src/main/java/org/rocksdb/EnvOptions.java | 12 +- .../org/rocksdb/GenericRateLimiterConfig.java | 68 - .../main/java/org/rocksdb/MergeOperator.java | 6 +- .../rocksdb/MutableColumnFamilyOptions.java | 4 + java/src/main/java/org/rocksdb/Options.java | 13 +- .../java/org/rocksdb/RateLimiterConfig.java | 26 - java/src/main/java/org/rocksdb/RocksDB.java | 31 +- .../java/org/rocksdb/RocksMutableObject.java | 18 + java/src/main/java/org/rocksdb/Slice.java | 30 +- .../org/rocksdb/StringAppendOperator.java | 10 +- .../org/rocksdb/TransactionLogIterator.java | 2 +- .../java/org/rocksdb/WBWIRocksIterator.java | 24 +- .../java/org/rocksdb/WriteBatchWithIndex.java | 14 + .../java/org/rocksdb/ColumnFamilyTest.java | 5 +- .../test/java/org/rocksdb/DBOptionsTest.java | 18 +- .../java/org/rocksdb/DirectSliceTest.java | 23 +- .../test/java/org/rocksdb/EnvOptionsTest.java | 20 +- .../java/org/rocksdb/KeyMayExistTest.java | 8 +- java/src/test/java/org/rocksdb/MergeTest.java | 79 +- .../test/java/org/rocksdb/OptionsTest.java | 43 +- .../java/org/rocksdb/RateLimiterTest.java | 49 + .../test/java/org/rocksdb/RocksDBTest.java | 13 +- java/src/test/java/org/rocksdb/SliceTest.java | 19 + .../org/rocksdb/WriteBatchWithIndexTest.java | 10 +- .../org/rocksdb/test/RocksJunitRunner.java | 18 +- 64 files changed, 4083 insertions(+), 1405 deletions(-) delete mode 100644 java/src/main/java/org/rocksdb/GenericRateLimiterConfig.java delete mode 100644 java/src/main/java/org/rocksdb/RateLimiterConfig.java create mode 100644 java/src/test/java/org/rocksdb/RateLimiterTest.java diff --git a/java/Makefile b/java/Makefile index 98d2ac276..5725c9920 100644 --- a/java/Makefile +++ b/java/Makefile @@ -18,7 +18,6 @@ NATIVE_JAVA_CLASSES = org.rocksdb.AbstractCompactionFilter\ org.rocksdb.ExternalSstFileInfo\ org.rocksdb.FlushOptions\ org.rocksdb.Filter\ - org.rocksdb.GenericRateLimiterConfig\ org.rocksdb.HashLinkedListMemTableConfig\ org.rocksdb.HashSkipListMemTableConfig\ org.rocksdb.Logger\ @@ -91,6 +90,7 @@ JAVA_TESTS = org.rocksdb.BackupableDBOptionsTest\ org.rocksdb.NativeLibraryLoaderTest\ org.rocksdb.OptionsTest\ org.rocksdb.PlainTableConfigTest\ + org.rocksdb.RateLimiterTest\ org.rocksdb.ReadOnlyTest\ org.rocksdb.ReadOptionsTest\ org.rocksdb.RocksDBTest\ @@ -136,6 +136,14 @@ JAVA_TESTCLASSPATH = $(JAVA_JUNIT_JAR):$(JAVA_HAMCR_JAR):$(JAVA_MOCKITO_JAR):$(J MVN_LOCAL = ~/.m2/repository +# Set the default JAVA_ARGS to "" for DEBUG_LEVEL=0 +JAVA_ARGS? = + +# When debugging add -Xcheck:jni to the java args +ifneq ($(DEBUG_LEVEL),0) + JAVA_ARGS = -ea -Xcheck:jni +endif + clean: $(AM_V_at)rm -rf include/* $(AM_V_at)rm -rf test-libs/ @@ -164,7 +172,7 @@ sample: java $(AM_V_at)javac -cp $(MAIN_CLASSES) -d $(SAMPLES_MAIN_CLASSES) $(SAMPLES_MAIN_SRC)/RocksDBSample.java $(AM_V_at)@rm -rf /tmp/rocksdbjni $(AM_V_at)@rm -rf /tmp/rocksdbjni_not_found - java -ea -Xcheck:jni -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) RocksDBSample /tmp/rocksdbjni + java $(JAVA_ARGS) -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) RocksDBSample /tmp/rocksdbjni $(AM_V_at)@rm -rf /tmp/rocksdbjni $(AM_V_at)@rm -rf /tmp/rocksdbjni_not_found @@ -172,7 +180,7 @@ column_family_sample: java $(AM_V_GEN)mkdir -p $(SAMPLES_MAIN_CLASSES) $(AM_V_at)javac -cp $(MAIN_CLASSES) -d $(SAMPLES_MAIN_CLASSES) $(SAMPLES_MAIN_SRC)/RocksDBColumnFamilySample.java $(AM_V_at)@rm -rf /tmp/rocksdbjni - java -ea -Xcheck:jni -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) RocksDBColumnFamilySample /tmp/rocksdbjni + java $(JAVA_ARGS) -Djava.library.path=target -cp $(MAIN_CLASSES):$(SAMPLES_MAIN_CLASSES) RocksDBColumnFamilySample /tmp/rocksdbjni $(AM_V_at)@rm -rf /tmp/rocksdbjni resolve_test_deps: @@ -194,7 +202,7 @@ java_test: java resolve_test_deps test: java java_test run_test run_test: - java -ea -Djava.library.path=target -cp "$(MAIN_CLASSES):$(TEST_CLASSES):$(JAVA_TESTCLASSPATH):target/*" org.rocksdb.test.RocksJunitRunner $(JAVA_TESTS) + java $(JAVA_ARGS) -Djava.library.path=target -cp "$(MAIN_CLASSES):$(TEST_CLASSES):$(JAVA_TESTCLASSPATH):target/*" org.rocksdb.test.RocksJunitRunner $(JAVA_TESTS) db_bench: java $(AM_V_GEN)mkdir -p $(BENCHMARK_MAIN_CLASSES) diff --git a/java/benchmark/src/main/java/org/rocksdb/benchmark/DbBenchmark.java b/java/benchmark/src/main/java/org/rocksdb/benchmark/DbBenchmark.java index 20601a95f..1708587da 100644 --- a/java/benchmark/src/main/java/org/rocksdb/benchmark/DbBenchmark.java +++ b/java/benchmark/src/main/java/org/rocksdb/benchmark/DbBenchmark.java @@ -572,7 +572,7 @@ public class DbBenchmark { (Integer)flags_.get(Flag.num_levels)); options.setTargetFileSizeBase( (Integer)flags_.get(Flag.target_file_size_base)); - options.setTargetFileSizeMultiplier((Double) flags_.get(Flag.target_file_size_multiplier)); + options.setTargetFileSizeMultiplier((Integer)flags_.get(Flag.target_file_size_multiplier)); options.setMaxBytesForLevelBase( (Integer)flags_.get(Flag.max_bytes_for_level_base)); options.setMaxBytesForLevelMultiplier((Double) flags_.get(Flag.max_bytes_for_level_multiplier)); @@ -588,12 +588,10 @@ public class DbBenchmark { (Double)flags_.get(Flag.hard_rate_limit)); options.setRateLimitDelayMaxMilliseconds( (Integer)flags_.get(Flag.rate_limit_delay_max_milliseconds)); - options.setMaxGrandparentOverlapFactor( - (Integer)flags_.get(Flag.max_grandparent_overlap_factor)); + options.setMaxCompactionBytes( + (Long) flags_.get(Flag.max_compaction_bytes)); options.setDisableAutoCompactions( (Boolean)flags_.get(Flag.disable_auto_compactions)); - options.setSourceCompactionFactor( - (Integer)flags_.get(Flag.source_compaction_factor)); options.setMaxSuccessiveMerges( (Integer)flags_.get(Flag.max_successive_merges)); options.setWalTtlSeconds((Long)flags_.get(Flag.wal_ttl_seconds)); @@ -978,7 +976,7 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - write_buffer_size(4 * SizeUnit.MB, + write_buffer_size(4L * SizeUnit.MB, "Number of bytes to buffer in memtable before compacting\n" + "\t(initialized to default value by 'main'.)") { @Override public Object parseValue(String value) { @@ -1056,7 +1054,7 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - numdistinct(1000, + numdistinct(1000L, "Number of distinct keys to use. Used in RandomWithVerify to\n" + "\tread/write on fewer keys so that gets are more likely to find the\n" + "\tkey and puts are more likely to update the same key.") { @@ -1064,7 +1062,7 @@ public class DbBenchmark { return Long.parseLong(value); } }, - merge_keys(-1, + merge_keys(-1L, "Number of distinct keys to use for MergeRandom and\n" + "\tReadRandomMergeRandom.\n" + "\tIf negative, there will be FLAGS_num keys.") { @@ -1169,7 +1167,7 @@ public class DbBenchmark { return Long.parseLong(value); } }, - compressed_cache_size(-1, + compressed_cache_size(-1L, "Number of bytes to use as a cache of compressed data.") { @Override public Object parseValue(String value) { return Long.parseLong(value); @@ -1188,7 +1186,7 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - memtable_bloom_size_ratio(0, "Ratio of memtable used by the bloom filter.\n" + memtable_bloom_size_ratio(0.0d, "Ratio of memtable used by the bloom filter.\n" + "\t0 means no bloom filter.") { @Override public Object parseValue(String value) { return Double.parseDouble(value); @@ -1212,7 +1210,7 @@ public class DbBenchmark { return parseBoolean(value); } }, - writes(-1,"Number of write operations to do. If negative, do\n" + + writes(-1L, "Number of write operations to do. If negative, do\n" + "\t--num reads.") { @Override public Object parseValue(String value) { return Long.parseLong(value); @@ -1255,7 +1253,7 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - max_bytes_for_level_multiplier(10, + max_bytes_for_level_multiplier(10.0d, "A multiplier to compute max bytes for level-N (N >= 2)") { @Override public Object parseValue(String value) { return Double.parseDouble(value); @@ -1337,7 +1335,7 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - stats_interval(0,"Stats are reported every N operations when\n" + + stats_interval(0L, "Stats are reported every N operations when\n" + "\tthis is greater than zero. When 0 the interval grows over time.") { @Override public Object parseValue(String value) { return Long.parseLong(value); @@ -1354,12 +1352,12 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - soft_rate_limit(0.0,"") { + soft_rate_limit(0.0d,"") { @Override public Object parseValue(String value) { return Double.parseDouble(value); } }, - hard_rate_limit(0.0,"When not equal to 0 this make threads\n" + + hard_rate_limit(0.0d,"When not equal to 0 this make threads\n" + "\tsleep at each stats reporting interval until the compaction\n" + "\tscore for all levels is less than or equal to this value.") { @Override public Object parseValue(String value) { @@ -1373,11 +1371,10 @@ public class DbBenchmark { return Integer.parseInt(value); } }, - max_grandparent_overlap_factor(10,"Control maximum bytes of\n" + - "\toverlaps in grandparent (i.e., level+2) before we stop building a\n" + - "\tsingle file in a level->level+1 compaction.") { + max_compaction_bytes(0L, "Limit number of bytes in one compaction to be lower than this\n" + + "\threshold. But it's not guaranteed.") { @Override public Object parseValue(String value) { - return Integer.parseInt(value); + return Long.parseLong(value); } }, readonly(false,"Run read only benchmarks.") { @@ -1390,13 +1387,6 @@ public class DbBenchmark { return parseBoolean(value); } }, - source_compaction_factor(1,"Cap the size of data in level-K for\n" + - "\ta compaction run that compacts Level-K with Level-(K+1) (for\n" + - "\tK >= 1)") { - @Override public Object parseValue(String value) { - return Integer.parseInt(value); - } - }, wal_ttl_seconds(0L,"Set the TTL for the WAL Files in seconds.") { @Override public Object parseValue(String value) { return Long.parseLong(value); diff --git a/java/rocksjni/backupablejni.cc b/java/rocksjni/backupablejni.cc index d9976386c..31bbcfd29 100644 --- a/java/rocksjni/backupablejni.cc +++ b/java/rocksjni/backupablejni.cc @@ -27,8 +27,12 @@ */ jlong Java_org_rocksdb_BackupableDBOptions_newBackupableDBOptions( JNIEnv* env, jclass jcls, jstring jpath) { - const char* cpath = env->GetStringUTFChars(jpath, NULL); - auto bopt = new rocksdb::BackupableDBOptions(cpath); + const char* cpath = env->GetStringUTFChars(jpath, nullptr); + if(cpath == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + auto* bopt = new rocksdb::BackupableDBOptions(cpath); env->ReleaseStringUTFChars(jpath, cpath); return reinterpret_cast(bopt); } @@ -40,7 +44,7 @@ jlong Java_org_rocksdb_BackupableDBOptions_newBackupableDBOptions( */ jstring Java_org_rocksdb_BackupableDBOptions_backupDir( JNIEnv* env, jobject jopt, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return env->NewStringUTF(bopt->backup_dir.c_str()); } @@ -51,7 +55,7 @@ jstring Java_org_rocksdb_BackupableDBOptions_backupDir( */ void Java_org_rocksdb_BackupableDBOptions_setShareTableFiles( JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->share_table_files = flag; } @@ -62,7 +66,7 @@ void Java_org_rocksdb_BackupableDBOptions_setShareTableFiles( */ jboolean Java_org_rocksdb_BackupableDBOptions_shareTableFiles( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->share_table_files; } @@ -73,7 +77,7 @@ jboolean Java_org_rocksdb_BackupableDBOptions_shareTableFiles( */ void Java_org_rocksdb_BackupableDBOptions_setSync( JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->sync = flag; } @@ -84,7 +88,7 @@ void Java_org_rocksdb_BackupableDBOptions_setSync( */ jboolean Java_org_rocksdb_BackupableDBOptions_sync( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->sync; } @@ -95,7 +99,7 @@ jboolean Java_org_rocksdb_BackupableDBOptions_sync( */ void Java_org_rocksdb_BackupableDBOptions_setDestroyOldData( JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->destroy_old_data = flag; } @@ -106,7 +110,7 @@ void Java_org_rocksdb_BackupableDBOptions_setDestroyOldData( */ jboolean Java_org_rocksdb_BackupableDBOptions_destroyOldData( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->destroy_old_data; } @@ -117,7 +121,7 @@ jboolean Java_org_rocksdb_BackupableDBOptions_destroyOldData( */ void Java_org_rocksdb_BackupableDBOptions_setBackupLogFiles( JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->backup_log_files = flag; } @@ -128,7 +132,7 @@ void Java_org_rocksdb_BackupableDBOptions_setBackupLogFiles( */ jboolean Java_org_rocksdb_BackupableDBOptions_backupLogFiles( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->backup_log_files; } @@ -139,7 +143,7 @@ jboolean Java_org_rocksdb_BackupableDBOptions_backupLogFiles( */ void Java_org_rocksdb_BackupableDBOptions_setBackupRateLimit( JNIEnv* env, jobject jobj, jlong jhandle, jlong jbackup_rate_limit) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->backup_rate_limit = jbackup_rate_limit; } @@ -150,7 +154,7 @@ void Java_org_rocksdb_BackupableDBOptions_setBackupRateLimit( */ jlong Java_org_rocksdb_BackupableDBOptions_backupRateLimit( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->backup_rate_limit; } @@ -161,7 +165,7 @@ jlong Java_org_rocksdb_BackupableDBOptions_backupRateLimit( */ void Java_org_rocksdb_BackupableDBOptions_setRestoreRateLimit( JNIEnv* env, jobject jobj, jlong jhandle, jlong jrestore_rate_limit) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->restore_rate_limit = jrestore_rate_limit; } @@ -172,7 +176,7 @@ void Java_org_rocksdb_BackupableDBOptions_setRestoreRateLimit( */ jlong Java_org_rocksdb_BackupableDBOptions_restoreRateLimit( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->restore_rate_limit; } @@ -183,7 +187,7 @@ jlong Java_org_rocksdb_BackupableDBOptions_restoreRateLimit( */ void Java_org_rocksdb_BackupableDBOptions_setShareFilesWithChecksum( JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); bopt->share_files_with_checksum = flag; } @@ -194,7 +198,7 @@ void Java_org_rocksdb_BackupableDBOptions_setShareFilesWithChecksum( */ jboolean Java_org_rocksdb_BackupableDBOptions_shareFilesWithChecksum( JNIEnv* env, jobject jobj, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); + auto* bopt = reinterpret_cast(jhandle); return bopt->share_files_with_checksum; } @@ -205,7 +209,7 @@ jboolean Java_org_rocksdb_BackupableDBOptions_shareFilesWithChecksum( */ void Java_org_rocksdb_BackupableDBOptions_disposeInternal( JNIEnv* env, jobject jopt, jlong jhandle) { - auto bopt = reinterpret_cast(jhandle); - assert(bopt); + auto* bopt = reinterpret_cast(jhandle); + assert(bopt != nullptr); delete bopt; } diff --git a/java/rocksjni/backupenginejni.cc b/java/rocksjni/backupenginejni.cc index a796d6e5b..5cf39b32a 100644 --- a/java/rocksjni/backupenginejni.cc +++ b/java/rocksjni/backupenginejni.cc @@ -82,11 +82,15 @@ jintArray Java_org_rocksdb_BackupEngine_getCorruptedBackups( backup_engine->GetCorruptedBackups(&backup_ids); // store backupids in int array std::vector int_backup_ids(backup_ids.begin(), backup_ids.end()); + // Store ints in java array - jintArray ret_backup_ids; // Its ok to loose precision here (64->32) jsize ret_backup_ids_size = static_cast(backup_ids.size()); - ret_backup_ids = env->NewIntArray(ret_backup_ids_size); + jintArray ret_backup_ids = env->NewIntArray(ret_backup_ids_size); + if(ret_backup_ids == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } env->SetIntArrayRegion(ret_backup_ids, 0, ret_backup_ids_size, int_backup_ids.data()); return ret_backup_ids; @@ -155,14 +159,24 @@ void Java_org_rocksdb_BackupEngine_restoreDbFromBackup( JNIEnv* env, jobject jbe, jlong jbe_handle, jint jbackup_id, jstring jdb_dir, jstring jwal_dir, jlong jrestore_options_handle) { auto* backup_engine = reinterpret_cast(jbe_handle); - const char* db_dir = env->GetStringUTFChars(jdb_dir, 0); - const char* wal_dir = env->GetStringUTFChars(jwal_dir, 0); + const char* db_dir = env->GetStringUTFChars(jdb_dir, nullptr); + if(db_dir == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + const char* wal_dir = env->GetStringUTFChars(jwal_dir, nullptr); + if(wal_dir == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jdb_dir, db_dir); + return; + } auto* restore_options = reinterpret_cast(jrestore_options_handle); auto status = backup_engine->RestoreDBFromBackup( static_cast(jbackup_id), db_dir, wal_dir, *restore_options); + env->ReleaseStringUTFChars(jwal_dir, wal_dir); env->ReleaseStringUTFChars(jdb_dir, db_dir); @@ -182,13 +196,23 @@ void Java_org_rocksdb_BackupEngine_restoreDbFromLatestBackup( JNIEnv* env, jobject jbe, jlong jbe_handle, jstring jdb_dir, jstring jwal_dir, jlong jrestore_options_handle) { auto* backup_engine = reinterpret_cast(jbe_handle); - const char* db_dir = env->GetStringUTFChars(jdb_dir, 0); - const char* wal_dir = env->GetStringUTFChars(jwal_dir, 0); + const char* db_dir = env->GetStringUTFChars(jdb_dir, nullptr); + if(db_dir == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + const char* wal_dir = env->GetStringUTFChars(jwal_dir, nullptr); + if(wal_dir == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jdb_dir, db_dir); + return; + } auto* restore_options = reinterpret_cast(jrestore_options_handle); auto status = backup_engine->RestoreDBFromLatestBackup(db_dir, wal_dir, *restore_options); + env->ReleaseStringUTFChars(jwal_dir, wal_dir); env->ReleaseStringUTFChars(jdb_dir, db_dir); @@ -206,5 +230,7 @@ void Java_org_rocksdb_BackupEngine_restoreDbFromLatestBackup( */ void Java_org_rocksdb_BackupEngine_disposeInternal( JNIEnv* env, jobject jbe, jlong jbe_handle) { - delete reinterpret_cast(jbe_handle); + auto* be = reinterpret_cast(jbe_handle); + assert(be != nullptr); + delete be; } diff --git a/java/rocksjni/checkpoint.cc b/java/rocksjni/checkpoint.cc index 45f0fde6b..9832d1a93 100644 --- a/java/rocksjni/checkpoint.cc +++ b/java/rocksjni/checkpoint.cc @@ -22,7 +22,7 @@ */ jlong Java_org_rocksdb_Checkpoint_newCheckpoint(JNIEnv* env, jclass jclazz, jlong jdb_handle) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); rocksdb::Checkpoint* checkpoint; rocksdb::Checkpoint::Create(db, &checkpoint); return reinterpret_cast(checkpoint); @@ -35,8 +35,8 @@ jlong Java_org_rocksdb_Checkpoint_newCheckpoint(JNIEnv* env, */ void Java_org_rocksdb_Checkpoint_disposeInternal(JNIEnv* env, jobject jobj, jlong jhandle) { - auto checkpoint = reinterpret_cast(jhandle); - assert(checkpoint); + auto* checkpoint = reinterpret_cast(jhandle); + assert(checkpoint != nullptr); delete checkpoint; } @@ -48,13 +48,20 @@ void Java_org_rocksdb_Checkpoint_disposeInternal(JNIEnv* env, jobject jobj, void Java_org_rocksdb_Checkpoint_createCheckpoint( JNIEnv* env, jobject jobj, jlong jcheckpoint_handle, jstring jcheckpoint_path) { - auto checkpoint = reinterpret_cast( - jcheckpoint_handle); const char* checkpoint_path = env->GetStringUTFChars( jcheckpoint_path, 0); + if(checkpoint_path == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + + auto* checkpoint = reinterpret_cast( + jcheckpoint_handle); rocksdb::Status s = checkpoint->CreateCheckpoint( checkpoint_path); + env->ReleaseStringUTFChars(jcheckpoint_path, checkpoint_path); + if (!s.ok()) { rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } diff --git a/java/rocksjni/columnfamilyhandle.cc b/java/rocksjni/columnfamilyhandle.cc index 2a874b1d9..1fb850774 100644 --- a/java/rocksjni/columnfamilyhandle.cc +++ b/java/rocksjni/columnfamilyhandle.cc @@ -20,6 +20,7 @@ */ void Java_org_rocksdb_ColumnFamilyHandle_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - auto it = reinterpret_cast(handle); - delete it; + auto* cfh = reinterpret_cast(handle); + assert(cfh != nullptr); + delete cfh; } diff --git a/java/rocksjni/compaction_filter.cc b/java/rocksjni/compaction_filter.cc index 6cc6c7732..77ee4d5b5 100644 --- a/java/rocksjni/compaction_filter.cc +++ b/java/rocksjni/compaction_filter.cc @@ -20,6 +20,8 @@ */ void Java_org_rocksdb_AbstractCompactionFilter_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* cf = reinterpret_cast(handle); + assert(cf != nullptr); + delete cf; } // diff --git a/java/rocksjni/comparator.cc b/java/rocksjni/comparator.cc index dd11f10e4..aeeb607ab 100644 --- a/java/rocksjni/comparator.cc +++ b/java/rocksjni/comparator.cc @@ -27,7 +27,9 @@ */ void Java_org_rocksdb_AbstractComparator_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* bcjc = reinterpret_cast(handle); + assert(bcjc != nullptr); + delete bcjc; } // diff --git a/java/rocksjni/comparatorjnicallback.cc b/java/rocksjni/comparatorjnicallback.cc index 57ee0f95c..ab868c85d 100644 --- a/java/rocksjni/comparatorjnicallback.cc +++ b/java/rocksjni/comparatorjnicallback.cc @@ -17,35 +17,60 @@ BaseComparatorJniCallback::BaseComparatorJniCallback( 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 __attribute__((unused)) = env->GetJavaVM(&m_jvm); - assert(rs == JNI_OK); + 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 jmethodID jNameMethodId = AbstractComparatorJni::getNameMethodId(env); + if(jNameMethodId == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return; + } jstring jsName = (jstring)env->CallObjectMethod(m_jComparator, jNameMethodId); - m_name = JniUtil::copyString(env, jsName); // also releases jsName + if(env->ExceptionCheck()) { + // exception thrown + return; + } + jboolean has_exception = JNI_FALSE; + m_name = JniUtil::copyString(env, jsName, + &has_exception); // also releases jsName + if (has_exception == JNI_TRUE) { + // exception thrown + return; + } m_jCompareMethodId = AbstractComparatorJni::getCompareMethodId(env); + if(m_jCompareMethodId == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return; + } + m_jFindShortestSeparatorMethodId = AbstractComparatorJni::getFindShortestSeparatorMethodId(env); + if(m_jFindShortestSeparatorMethodId == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return; + } + m_jFindShortSuccessorMethodId = AbstractComparatorJni::getFindShortSuccessorMethodId(env); -} - -/** - * Attach/Get a JNIEnv for the current native thread - */ -JNIEnv* BaseComparatorJniCallback::getJniEnv() const { - JNIEnv *env; - jint rs __attribute__((unused)) = - m_jvm->AttachCurrentThread(reinterpret_cast(&env), NULL); - assert(rs == JNI_OK); - return env; + if(m_jFindShortSuccessorMethodId == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return; + } } const char* BaseComparatorJniCallback::Name() const { @@ -53,22 +78,50 @@ const char* BaseComparatorJniCallback::Name() const { } int BaseComparatorJniCallback::Compare(const Slice& a, const Slice& b) const { - JNIEnv* m_env = getJniEnv(); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &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(); - AbstractSliceJni::setHandle(m_env, m_jSliceA, &a, JNI_FALSE); - AbstractSliceJni::setHandle(m_env, m_jSliceB, &b, JNI_FALSE); + bool pending_exception = + AbstractSliceJni::setHandle(env, m_jSliceA, &a, JNI_FALSE); + if(pending_exception) { + if(env->ExceptionCheck()) { + // exception thrown from setHandle or descendant + env->ExceptionDescribe(); // print out exception to stderr + } + JniUtil::releaseJniEnv(m_jvm, attached_thread); + return 0; + } + + pending_exception = + AbstractSliceJni::setHandle(env, m_jSliceB, &b, JNI_FALSE); + if(pending_exception) { + if(env->ExceptionCheck()) { + // exception thrown from setHandle or descendant + env->ExceptionDescribe(); // print out exception to stderr + } + JniUtil::releaseJniEnv(m_jvm, attached_thread); + return 0; + } + jint result = - m_env->CallIntMethod(m_jComparator, m_jCompareMethodId, m_jSliceA, + env->CallIntMethod(m_jComparator, m_jCompareMethodId, m_jSliceA, m_jSliceB); mtx_compare->Unlock(); - m_jvm->DetachCurrentThread(); + if(env->ExceptionCheck()) { + // exception thrown from CallIntMethod + env->ExceptionDescribe(); // print out exception to stderr + result = 0; // we could not get a result from java callback so use 0 + } + + JniUtil::releaseJniEnv(m_jvm, attached_thread); return result; } @@ -79,32 +132,80 @@ void BaseComparatorJniCallback::FindShortestSeparator( return; } - JNIEnv* m_env = getJniEnv(); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); const char* startUtf = start->c_str(); - jstring jsStart = m_env->NewStringUTF(startUtf); + jstring jsStart = env->NewStringUTF(startUtf); + if(jsStart == nullptr) { + // unable to construct string + if(env->ExceptionCheck()) { + env->ExceptionDescribe(); // print out exception to stderr + } + JniUtil::releaseJniEnv(m_jvm, 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); + 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(); - AbstractSliceJni::setHandle(m_env, m_jSliceLimit, &limit, JNI_FALSE); + bool pending_exception = + AbstractSliceJni::setHandle(env, m_jSliceLimit, &limit, JNI_FALSE); + if(pending_exception) { + if(env->ExceptionCheck()) { + // exception thrown from setHandle or descendant + env->ExceptionDescribe(); // print out exception to stderr + } + if(jsStart != nullptr) { + env->DeleteLocalRef(jsStart); + } + JniUtil::releaseJniEnv(m_jvm, attached_thread); + return; + } + jstring jsResultStart = - (jstring)m_env->CallObjectMethod(m_jComparator, + (jstring)env->CallObjectMethod(m_jComparator, m_jFindShortestSeparatorMethodId, jsStart, m_jSliceLimit); mtx_findShortestSeparator->Unlock(); - m_env->DeleteLocalRef(jsStart); + if(env->ExceptionCheck()) { + // exception thrown from CallObjectMethod + env->ExceptionDescribe(); // print out exception to stderr + env->DeleteLocalRef(jsStart); + JniUtil::releaseJniEnv(m_jvm, attached_thread); + return; + } + + env->DeleteLocalRef(jsStart); if (jsResultStart != nullptr) { // update start with result - *start = - JniUtil::copyString(m_env, jsResultStart); // also releases jsResultStart + jboolean has_exception = JNI_FALSE; + std::string result = 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); + return; + } + + *start = result; } - m_jvm->DetachCurrentThread(); + JniUtil::releaseJniEnv(m_jvm, attached_thread); } void BaseComparatorJniCallback::FindShortSuccessor(std::string* key) const { @@ -112,34 +213,69 @@ void BaseComparatorJniCallback::FindShortSuccessor(std::string* key) const { return; } - JNIEnv* m_env = getJniEnv(); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); const char* keyUtf = key->c_str(); - jstring jsKey = m_env->NewStringUTF(keyUtf); + jstring jsKey = env->NewStringUTF(keyUtf); + if(jsKey == nullptr) { + // unable to construct string + if(env->ExceptionCheck()) { + env->ExceptionDescribe(); // print out exception to stderr + } + JniUtil::releaseJniEnv(m_jvm, 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); + return; + } jstring jsResultKey = - (jstring)m_env->CallObjectMethod(m_jComparator, + (jstring)env->CallObjectMethod(m_jComparator, m_jFindShortSuccessorMethodId, jsKey); - m_env->DeleteLocalRef(jsKey); + if(env->ExceptionCheck()) { + // exception thrown from CallObjectMethod + env->ExceptionDescribe(); // print out exception to stderr + env->DeleteLocalRef(jsKey); + JniUtil::releaseJniEnv(m_jvm, attached_thread); + return; + } + + env->DeleteLocalRef(jsKey); if (jsResultKey != nullptr) { // updates key with result, also releases jsResultKey. - *key = JniUtil::copyString(m_env, jsResultKey); + jboolean has_exception = JNI_FALSE; + std::string result = JniUtil::copyString(env, jsResultKey, &has_exception); + if (has_exception == JNI_TRUE) { + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); // print out exception to stderr + } + JniUtil::releaseJniEnv(m_jvm, attached_thread); + return; + } + + *key = result; } - m_jvm->DetachCurrentThread(); + JniUtil::releaseJniEnv(m_jvm, attached_thread); } BaseComparatorJniCallback::~BaseComparatorJniCallback() { - JNIEnv* m_env = getJniEnv(); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); - m_env->DeleteGlobalRef(m_jComparator); + if(m_jComparator != nullptr) { + env->DeleteGlobalRef(m_jComparator); + } - // Note: do not need to explicitly detach, as this function is effectively - // called from the Java class's disposeInternal method, and so already - // has an attached thread, getJniEnv above is just a no-op Attach to get - // the env jvm->DetachCurrentThread(); + JniUtil::releaseJniEnv(m_jvm, attached_thread); } ComparatorJniCallback::ComparatorJniCallback( @@ -147,15 +283,42 @@ ComparatorJniCallback::ComparatorJniCallback( const ComparatorJniCallbackOptions* copt) : BaseComparatorJniCallback(env, jComparator, copt) { m_jSliceA = env->NewGlobalRef(SliceJni::construct0(env)); + if(m_jSliceA == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + m_jSliceB = env->NewGlobalRef(SliceJni::construct0(env)); + if(m_jSliceB == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + m_jSliceLimit = env->NewGlobalRef(SliceJni::construct0(env)); + if(m_jSliceLimit == nullptr) { + // exception thrown: OutOfMemoryError + return; + } } ComparatorJniCallback::~ComparatorJniCallback() { - JNIEnv* m_env = getJniEnv(); - m_env->DeleteGlobalRef(m_jSliceA); - m_env->DeleteGlobalRef(m_jSliceB); - m_env->DeleteGlobalRef(m_jSliceLimit); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); + + if(m_jSliceA != nullptr) { + env->DeleteGlobalRef(m_jSliceA); + } + + if(m_jSliceB != nullptr) { + env->DeleteGlobalRef(m_jSliceB); + } + + if(m_jSliceLimit != nullptr) { + env->DeleteGlobalRef(m_jSliceLimit); + } + + JniUtil::releaseJniEnv(m_jvm, attached_thread); } DirectComparatorJniCallback::DirectComparatorJniCallback( @@ -163,14 +326,41 @@ DirectComparatorJniCallback::DirectComparatorJniCallback( const ComparatorJniCallbackOptions* copt) : BaseComparatorJniCallback(env, jComparator, copt) { m_jSliceA = env->NewGlobalRef(DirectSliceJni::construct0(env)); + if(m_jSliceA == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + m_jSliceB = env->NewGlobalRef(DirectSliceJni::construct0(env)); + if(m_jSliceB == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + m_jSliceLimit = env->NewGlobalRef(DirectSliceJni::construct0(env)); + if(m_jSliceLimit == nullptr) { + // exception thrown: OutOfMemoryError + return; + } } DirectComparatorJniCallback::~DirectComparatorJniCallback() { - JNIEnv* m_env = getJniEnv(); - m_env->DeleteGlobalRef(m_jSliceA); - m_env->DeleteGlobalRef(m_jSliceB); - m_env->DeleteGlobalRef(m_jSliceLimit); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); + + if(m_jSliceA != nullptr) { + env->DeleteGlobalRef(m_jSliceA); + } + + if(m_jSliceB != nullptr) { + env->DeleteGlobalRef(m_jSliceB); + } + + if(m_jSliceLimit != nullptr) { + env->DeleteGlobalRef(m_jSliceLimit); + } + + JniUtil::releaseJniEnv(m_jvm, attached_thread); } } // namespace rocksdb diff --git a/java/rocksjni/comparatorjnicallback.h b/java/rocksjni/comparatorjnicallback.h index 821a91e45..ede129261 100644 --- a/java/rocksjni/comparatorjnicallback.h +++ b/java/rocksjni/comparatorjnicallback.h @@ -61,7 +61,6 @@ class BaseComparatorJniCallback : public Comparator { port::Mutex* mtx_compare; // used for synchronisation in findShortestSeparator method port::Mutex* mtx_findShortestSeparator; - JavaVM* m_jvm; jobject m_jComparator; std::string m_name; jmethodID m_jCompareMethodId; @@ -69,7 +68,7 @@ class BaseComparatorJniCallback : public Comparator { jmethodID m_jFindShortSuccessorMethodId; protected: - JNIEnv* getJniEnv() const; + JavaVM* m_jvm; jobject m_jSliceA; jobject m_jSliceB; jobject m_jSliceLimit; diff --git a/java/rocksjni/env.cc b/java/rocksjni/env.cc index a58f54ea7..2826c5c89 100644 --- a/java/rocksjni/env.cc +++ b/java/rocksjni/env.cc @@ -75,5 +75,7 @@ jlong Java_org_rocksdb_RocksMemEnv_createMemEnv( */ void Java_org_rocksdb_RocksMemEnv_disposeInternal( JNIEnv* env, jobject jobj, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* e = reinterpret_cast(jhandle); + assert(e != nullptr); + delete e; } diff --git a/java/rocksjni/env_options.cc b/java/rocksjni/env_options.cc index cc2dc6f1d..b5e612ad0 100644 --- a/java/rocksjni/env_options.cc +++ b/java/rocksjni/env_options.cc @@ -44,7 +44,9 @@ jlong Java_org_rocksdb_EnvOptions_newEnvOptions(JNIEnv *env, jclass jcls) { */ void Java_org_rocksdb_EnvOptions_disposeInternal(JNIEnv *env, jobject jobj, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* eo = reinterpret_cast(jhandle); + assert(eo != nullptr); + delete eo; } /* @@ -288,7 +290,8 @@ jlong Java_org_rocksdb_EnvOptions_writableFileMaxBufferSize(JNIEnv *env, void Java_org_rocksdb_EnvOptions_setRateLimiter(JNIEnv *env, jobject jobj, jlong jhandle, jlong rl_handle) { - auto *rate_limiter = reinterpret_cast(rl_handle); - auto *env_opt = reinterpret_cast(jhandle); - env_opt->rate_limiter = rate_limiter; + auto* sptr_rate_limiter = + reinterpret_cast *>(rl_handle); + auto* env_opt = reinterpret_cast(jhandle); + env_opt->rate_limiter = sptr_rate_limiter->get(); } diff --git a/java/rocksjni/external_sst_file_info.cc b/java/rocksjni/external_sst_file_info.cc index d5ea27eee..ded618052 100644 --- a/java/rocksjni/external_sst_file_info.cc +++ b/java/rocksjni/external_sst_file_info.cc @@ -31,17 +31,35 @@ jlong Java_org_rocksdb_ExternalSstFileInfo_newExternalSstFileInfo__Ljava_lang_St JNIEnv *env, jclass jcls, jstring jfile_path, jstring jsmallest_key, jstring jlargest_key, jlong jsequence_number, jlong jfile_size, jint jnum_entries, jint jversion) { - const char *file_path = env->GetStringUTFChars(jfile_path, NULL); - const char *smallest_key = env->GetStringUTFChars(jsmallest_key, NULL); - const char *largest_key = env->GetStringUTFChars(jlargest_key, NULL); + const char *file_path = env->GetStringUTFChars(jfile_path, nullptr); + if(file_path == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + const char *smallest_key = env->GetStringUTFChars(jsmallest_key, nullptr); + if(smallest_key == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jfile_path, file_path); + return 0; + } + const char *largest_key = env->GetStringUTFChars(jlargest_key, nullptr); + if(largest_key == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jsmallest_key, smallest_key); + env->ReleaseStringUTFChars(jfile_path, file_path); + return 0; + } + auto *external_sst_file_info = new rocksdb::ExternalSstFileInfo( file_path, smallest_key, largest_key, static_cast(jsequence_number), static_cast(jfile_size), static_cast(jnum_entries), static_cast(jversion)); - env->ReleaseStringUTFChars(jfile_path, file_path); - env->ReleaseStringUTFChars(jsmallest_key, smallest_key); + env->ReleaseStringUTFChars(jlargest_key, largest_key); + env->ReleaseStringUTFChars(jsmallest_key, smallest_key); + env->ReleaseStringUTFChars(jfile_path, file_path); + return reinterpret_cast(external_sst_file_info); } @@ -55,7 +73,11 @@ void Java_org_rocksdb_ExternalSstFileInfo_setFilePath(JNIEnv *env, jobject jobj, jstring jfile_path) { auto *external_sst_file_info = reinterpret_cast(jhandle); - const char *file_path = env->GetStringUTFChars(jfile_path, NULL); + const char *file_path = env->GetStringUTFChars(jfile_path, nullptr); + if(file_path == nullptr) { + // exception thrown: OutOfMemoryError + return; + } external_sst_file_info->file_path = file_path; env->ReleaseStringUTFChars(jfile_path, file_path); } @@ -81,7 +103,11 @@ void Java_org_rocksdb_ExternalSstFileInfo_setSmallestKey( JNIEnv *env, jobject jobj, jlong jhandle, jstring jsmallest_key) { auto *external_sst_file_info = reinterpret_cast(jhandle); - const char *smallest_key = env->GetStringUTFChars(jsmallest_key, NULL); + const char *smallest_key = env->GetStringUTFChars(jsmallest_key, nullptr); + if(smallest_key == nullptr) { + // exception thrown: OutOfMemoryError + return; + } external_sst_file_info->smallest_key = smallest_key; env->ReleaseStringUTFChars(jsmallest_key, smallest_key); } @@ -111,6 +137,10 @@ void Java_org_rocksdb_ExternalSstFileInfo_setLargestKey(JNIEnv *env, auto *external_sst_file_info = reinterpret_cast(jhandle); const char *largest_key = env->GetStringUTFChars(jlargest_key, NULL); + if(largest_key == nullptr) { + // exception thrown: OutOfMemoryError + return; + } external_sst_file_info->largest_key = largest_key; env->ReleaseStringUTFChars(jlargest_key, largest_key); } @@ -238,5 +268,7 @@ jint Java_org_rocksdb_ExternalSstFileInfo_version(JNIEnv *env, jobject jobj, void Java_org_rocksdb_ExternalSstFileInfo_disposeInternal(JNIEnv *env, jobject jobj, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* esfi = reinterpret_cast(jhandle); + assert(esfi != nullptr); + delete esfi; } diff --git a/java/rocksjni/filter.cc b/java/rocksjni/filter.cc index 96ef9856b..2bc84b438 100644 --- a/java/rocksjni/filter.cc +++ b/java/rocksjni/filter.cc @@ -24,12 +24,10 @@ jlong Java_org_rocksdb_BloomFilter_createNewBloomFilter( JNIEnv* env, jclass jcls, jint bits_per_key, jboolean use_block_base_builder) { - auto* fp = const_cast( - rocksdb::NewBloomFilterPolicy(bits_per_key, use_block_base_builder)); - auto* pFilterPolicy = - new std::shared_ptr; - *pFilterPolicy = std::shared_ptr(fp); - return reinterpret_cast(pFilterPolicy); + auto* sptr_filter = + new std::shared_ptr( + rocksdb::NewBloomFilterPolicy(bits_per_key, use_block_base_builder)); + return reinterpret_cast(sptr_filter); } /* @@ -39,8 +37,7 @@ jlong Java_org_rocksdb_BloomFilter_createNewBloomFilter( */ void Java_org_rocksdb_Filter_disposeInternal( JNIEnv* env, jobject jobj, jlong jhandle) { - - std::shared_ptr *handle = - reinterpret_cast *>(jhandle); - handle->reset(); + auto* handle = + reinterpret_cast *>(jhandle); + delete handle; // delete std::shared_ptr } diff --git a/java/rocksjni/iterator.cc b/java/rocksjni/iterator.cc index c5e64adfb..43f4b027f 100644 --- a/java/rocksjni/iterator.cc +++ b/java/rocksjni/iterator.cc @@ -21,7 +21,8 @@ */ void Java_org_rocksdb_RocksIterator_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - auto it = reinterpret_cast(handle); + auto* it = reinterpret_cast(handle); + assert(it != nullptr); delete it; } @@ -83,11 +84,16 @@ void Java_org_rocksdb_RocksIterator_prev0( void Java_org_rocksdb_RocksIterator_seek0( JNIEnv* env, jobject jobj, jlong handle, jbyteArray jtarget, jint jtarget_len) { - auto it = reinterpret_cast(handle); - jbyte* target = env->GetByteArrayElements(jtarget, 0); + jbyte* target = env->GetByteArrayElements(jtarget, nullptr); + if(target == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + rocksdb::Slice target_slice( reinterpret_cast(target), jtarget_len); + auto* it = reinterpret_cast(handle); it->Seek(target_slice); env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT); @@ -100,7 +106,7 @@ void Java_org_rocksdb_RocksIterator_seek0( */ void Java_org_rocksdb_RocksIterator_status0( JNIEnv* env, jobject jobj, jlong handle) { - auto it = reinterpret_cast(handle); + auto* it = reinterpret_cast(handle); rocksdb::Status s = it->status(); if (s.ok()) { @@ -117,10 +123,14 @@ void Java_org_rocksdb_RocksIterator_status0( */ jbyteArray Java_org_rocksdb_RocksIterator_key0( JNIEnv* env, jobject jobj, jlong handle) { - auto it = reinterpret_cast(handle); + auto* it = reinterpret_cast(handle); rocksdb::Slice key_slice = it->key(); jbyteArray jkey = env->NewByteArray(static_cast(key_slice.size())); + if(jkey == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } env->SetByteArrayRegion(jkey, 0, static_cast(key_slice.size()), reinterpret_cast(key_slice.data())); return jkey; @@ -133,11 +143,15 @@ jbyteArray Java_org_rocksdb_RocksIterator_key0( */ jbyteArray Java_org_rocksdb_RocksIterator_value0( JNIEnv* env, jobject jobj, jlong handle) { - auto it = reinterpret_cast(handle); + auto* it = reinterpret_cast(handle); rocksdb::Slice value_slice = it->value(); jbyteArray jkeyValue = env->NewByteArray(static_cast(value_slice.size())); + if(jkeyValue == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } env->SetByteArrayRegion(jkeyValue, 0, static_cast(value_slice.size()), reinterpret_cast(value_slice.data())); return jkeyValue; diff --git a/java/rocksjni/loggerjnicallback.cc b/java/rocksjni/loggerjnicallback.cc index 42e97015e..74e6eb9ac 100644 --- a/java/rocksjni/loggerjnicallback.cc +++ b/java/rocksjni/loggerjnicallback.cc @@ -10,53 +10,106 @@ #include "rocksjni/loggerjnicallback.h" #include "rocksjni/portal.h" +#include +#include namespace rocksdb { LoggerJniCallback::LoggerJniCallback( JNIEnv* env, jobject jlogger) { - const jint rs __attribute__((unused)) = env->GetJavaVM(&m_jvm); - assert(rs == JNI_OK); + // 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; + } // 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 + return; + } jobject jdebug_level = InfoLogLevelJni::DEBUG_LEVEL(env); - assert(jdebug_level != nullptr); + if(jdebug_level == nullptr) { + // exception thrown: NoSuchFieldError, ExceptionInInitializerError + // or OutOfMemoryError + return; + } m_jdebug_level = env->NewGlobalRef(jdebug_level); + if(m_jdebug_level == nullptr) { + // exception thrown: OutOfMemoryError + return; + } jobject jinfo_level = InfoLogLevelJni::INFO_LEVEL(env); - assert(jinfo_level != nullptr); + if(jinfo_level == nullptr) { + // exception thrown: NoSuchFieldError, ExceptionInInitializerError + // or OutOfMemoryError + return; + } m_jinfo_level = env->NewGlobalRef(jinfo_level); + if(m_jinfo_level == nullptr) { + // exception thrown: OutOfMemoryError + return; + } jobject jwarn_level = InfoLogLevelJni::WARN_LEVEL(env); - assert(jwarn_level != nullptr); + if(jwarn_level == nullptr) { + // exception thrown: NoSuchFieldError, ExceptionInInitializerError + // or OutOfMemoryError + return; + } m_jwarn_level = env->NewGlobalRef(jwarn_level); + if(m_jwarn_level == nullptr) { + // exception thrown: OutOfMemoryError + return; + } jobject jerror_level = InfoLogLevelJni::ERROR_LEVEL(env); - assert(jerror_level != nullptr); + if(jerror_level == nullptr) { + // exception thrown: NoSuchFieldError, ExceptionInInitializerError + // or OutOfMemoryError + return; + } m_jerror_level = env->NewGlobalRef(jerror_level); + if(m_jerror_level == nullptr) { + // exception thrown: OutOfMemoryError + return; + } jobject jfatal_level = InfoLogLevelJni::FATAL_LEVEL(env); - assert(jfatal_level != nullptr); + if(jfatal_level == nullptr) { + // exception thrown: NoSuchFieldError, ExceptionInInitializerError + // or OutOfMemoryError + return; + } m_jfatal_level = env->NewGlobalRef(jfatal_level); + if(m_jfatal_level == nullptr) { + // exception thrown: OutOfMemoryError + return; + } jobject jheader_level = InfoLogLevelJni::HEADER_LEVEL(env); - assert(jheader_level != nullptr); + if(jheader_level == nullptr) { + // exception thrown: NoSuchFieldError, ExceptionInInitializerError + // or OutOfMemoryError + return; + } m_jheader_level = env->NewGlobalRef(jheader_level); -} - -/** - * Get JNIEnv for current native thread - */ -JNIEnv* LoggerJniCallback::getJniEnv() const { - JNIEnv *env; - jint rs __attribute__((unused)) = - m_jvm->AttachCurrentThread(reinterpret_cast(&env), NULL); - assert(rs == JNI_OK); - return env; + if(m_jheader_level == nullptr) { + // exception thrown: OutOfMemoryError + return; + } } void LoggerJniCallback::Logv(const char* format, va_list ap) { @@ -94,69 +147,96 @@ void LoggerJniCallback::Logv(const InfoLogLevel log_level, break; } - // We try twice: the first time with a fixed-size stack allocated buffer, - // and the second time with a much larger dynamically allocated buffer. - char buffer[500]; - for (int iter = 0; iter < 2; iter++) { - char* base; - int bufsize; - if (iter == 0) { - bufsize = sizeof(buffer); - base = buffer; - } else { - bufsize = 30000; - base = new char[bufsize]; - } - char* p = base; - char* limit = base + bufsize; - // Print the message - if (p < limit) { - va_list backup_ap; - va_copy(backup_ap, ap); - p += vsnprintf(p, limit - p, format, backup_ap); - va_end(backup_ap); - } - // Truncate to available space if necessary - if (p >= limit) { - if (iter == 0) { - continue; // Try again with larger buffer - } else { - p = limit - 1; - } - } - assert(p < limit); - *p++ = '\0'; - - JNIEnv* env = getJniEnv(); + assert(format != nullptr); + assert(ap != nullptr); + const std::unique_ptr msg = format_str(format, ap); - // pass java string to callback handler - env->CallVoidMethod( - m_jLogger, - m_jLogMethodId, - jlog_level, - env->NewStringUTF(base)); + // pass msg to java callback handler + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); - if (base != buffer) { - delete[] base; + jstring jmsg = env->NewStringUTF(msg.get()); + if(jmsg == nullptr) { + // unable to construct string + if(env->ExceptionCheck()) { + env->ExceptionDescribe(); // print out exception to stderr } - break; + JniUtil::releaseJniEnv(m_jvm, 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); + return; + } + + env->CallVoidMethod(m_jLogger, 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); + return; } - m_jvm->DetachCurrentThread(); + + env->DeleteLocalRef(jmsg); + JniUtil::releaseJniEnv(m_jvm, attached_thread); } } +std::unique_ptr LoggerJniCallback::format_str(const char* format, va_list ap) const { + va_list ap_copy; + + va_copy(ap_copy, ap); + const size_t required = vsnprintf(nullptr, 0, format, ap_copy) + 1; // Extra space for '\0' + va_end(ap_copy); + + std::unique_ptr buf(new char[required]); + + va_copy(ap_copy, ap); + vsnprintf(buf.get(), required, format, ap_copy); + va_end(ap_copy); + + return buf; +} + LoggerJniCallback::~LoggerJniCallback() { - JNIEnv* env = getJniEnv(); - env->DeleteGlobalRef(m_jLogger); + jboolean attached_thread = JNI_FALSE; + JNIEnv* env = JniUtil::getJniEnv(m_jvm, &attached_thread); + assert(env != nullptr); + + if(m_jLogger != nullptr) { + env->DeleteGlobalRef(m_jLogger); + } + + if(m_jdebug_level != nullptr) { + env->DeleteGlobalRef(m_jdebug_level); + } + + if(m_jinfo_level != nullptr) { + env->DeleteGlobalRef(m_jinfo_level); + } + + if(m_jwarn_level != nullptr) { + env->DeleteGlobalRef(m_jwarn_level); + } + + if(m_jerror_level != nullptr) { + env->DeleteGlobalRef(m_jerror_level); + } + + if(m_jfatal_level != nullptr) { + env->DeleteGlobalRef(m_jfatal_level); + } - env->DeleteGlobalRef(m_jdebug_level); - env->DeleteGlobalRef(m_jinfo_level); - env->DeleteGlobalRef(m_jwarn_level); - env->DeleteGlobalRef(m_jerror_level); - env->DeleteGlobalRef(m_jfatal_level); - env->DeleteGlobalRef(m_jheader_level); + if(m_jheader_level != nullptr) { + env->DeleteGlobalRef(m_jheader_level); + } - m_jvm->DetachCurrentThread(); + JniUtil::releaseJniEnv(m_jvm, attached_thread); } } // namespace rocksdb @@ -168,15 +248,14 @@ LoggerJniCallback::~LoggerJniCallback() { */ jlong Java_org_rocksdb_Logger_createNewLoggerOptions( JNIEnv* env, jobject jobj, jlong joptions) { - rocksdb::LoggerJniCallback* c = - new rocksdb::LoggerJniCallback(env, jobj); + auto* sptr_logger = new std::shared_ptr( + new rocksdb::LoggerJniCallback(env, jobj)); + // set log level - c->SetInfoLogLevel(reinterpret_cast - (joptions)->info_log_level); - std::shared_ptr *pLoggerJniCallback = - new std::shared_ptr; - *pLoggerJniCallback = std::shared_ptr(c); - return reinterpret_cast(pLoggerJniCallback); + auto* options = reinterpret_cast(joptions); + sptr_logger->get()->SetInfoLogLevel(options->info_log_level); + + return reinterpret_cast(sptr_logger); } /* @@ -186,15 +265,14 @@ jlong Java_org_rocksdb_Logger_createNewLoggerOptions( */ jlong Java_org_rocksdb_Logger_createNewLoggerDbOptions( JNIEnv* env, jobject jobj, jlong jdb_options) { - rocksdb::LoggerJniCallback* c = - new rocksdb::LoggerJniCallback(env, jobj); + auto* sptr_logger = new std::shared_ptr( + new rocksdb::LoggerJniCallback(env, jobj)); + // set log level - c->SetInfoLogLevel(reinterpret_cast - (jdb_options)->info_log_level); - std::shared_ptr *pLoggerJniCallback = - new std::shared_ptr; - *pLoggerJniCallback = std::shared_ptr(c); - return reinterpret_cast(pLoggerJniCallback); + auto* db_options = reinterpret_cast(jdb_options); + sptr_logger->get()->SetInfoLogLevel(db_options->info_log_level); + + return reinterpret_cast(sptr_logger); } /* @@ -204,9 +282,10 @@ jlong Java_org_rocksdb_Logger_createNewLoggerDbOptions( */ void Java_org_rocksdb_Logger_setInfoLogLevel( JNIEnv* env, jobject jobj, jlong jhandle, jbyte jlog_level) { - std::shared_ptr *handle = + auto* handle = reinterpret_cast *>(jhandle); - (*handle)->SetInfoLogLevel(static_cast(jlog_level)); + handle->get()-> + SetInfoLogLevel(static_cast(jlog_level)); } /* @@ -216,9 +295,9 @@ void Java_org_rocksdb_Logger_setInfoLogLevel( */ jbyte Java_org_rocksdb_Logger_infoLogLevel( JNIEnv* env, jobject jobj, jlong jhandle) { - std::shared_ptr *handle = + auto* handle = reinterpret_cast *>(jhandle); - return static_cast((*handle)->GetInfoLogLevel()); + return static_cast(handle->get()->GetInfoLogLevel()); } /* @@ -228,7 +307,7 @@ jbyte Java_org_rocksdb_Logger_infoLogLevel( */ void Java_org_rocksdb_Logger_disposeInternal( JNIEnv* env, jobject jobj, jlong jhandle) { - std::shared_ptr *handle = + auto* handle = reinterpret_cast *>(jhandle); - handle->reset(); + delete handle; // delete std::shared_ptr } diff --git a/java/rocksjni/loggerjnicallback.h b/java/rocksjni/loggerjnicallback.h index 9a7605d24..cf9030e51 100644 --- a/java/rocksjni/loggerjnicallback.h +++ b/java/rocksjni/loggerjnicallback.h @@ -10,6 +10,7 @@ #define JAVA_ROCKSJNI_LOGGERJNICALLBACK_H_ #include +#include #include #include "port/port.h" #include "rocksdb/env.h" @@ -32,8 +33,6 @@ namespace rocksdb { virtual void Logv(const InfoLogLevel log_level, const char* format, va_list ap); - protected: - JNIEnv* getJniEnv() const; private: JavaVM* m_jvm; jobject m_jLogger; @@ -44,6 +43,7 @@ namespace rocksdb { jobject m_jerror_level; jobject m_jfatal_level; jobject m_jheader_level; + std::unique_ptr format_str(const char* format, va_list ap) const; }; } // namespace rocksdb diff --git a/java/rocksjni/merge_operator.cc b/java/rocksjni/merge_operator.cc index 68fe9b635..eb753ae42 100644 --- a/java/rocksjni/merge_operator.cc +++ b/java/rocksjni/merge_operator.cc @@ -25,13 +25,24 @@ /* * Class: org_rocksdb_StringAppendOperator - * Method: newMergeOperatorHandle + * Method: newSharedStringAppendOperator * Signature: ()J */ -jlong Java_org_rocksdb_StringAppendOperator_newMergeOperatorHandleImpl -(JNIEnv* env, jobject jobj) { - std::shared_ptr *op = - new std::shared_ptr(); - *op = rocksdb::MergeOperators::CreateFromStringId("stringappend"); - return reinterpret_cast(op); +jlong Java_org_rocksdb_StringAppendOperator_newSharedStringAppendOperator +(JNIEnv* env, jclass jclazz) { + auto* sptr_string_append_op = new std::shared_ptr( + rocksdb::MergeOperators::CreateFromStringId("stringappend")); + return reinterpret_cast(sptr_string_append_op); +} + +/* + * Class: org_rocksdb_StringAppendOperator + * Method: disposeInternal + * Signature: (J)V + */ +void Java_org_rocksdb_StringAppendOperator_disposeInternal( + JNIEnv* env, jobject jobj, jlong jhandle) { + auto* sptr_string_append_op = + reinterpret_cast* >(jhandle); + delete sptr_string_append_op; // delete std::shared_ptr } diff --git a/java/rocksjni/options.cc b/java/rocksjni/options.cc index 117b2bc4e..cf9f30199 100644 --- a/java/rocksjni/options.cc +++ b/java/rocksjni/options.cc @@ -39,7 +39,7 @@ * Signature: ()J */ jlong Java_org_rocksdb_Options_newOptions__(JNIEnv* env, jclass jcls) { - rocksdb::Options* op = new rocksdb::Options(); + auto* op = new rocksdb::Options(); return reinterpret_cast(op); } @@ -53,7 +53,7 @@ jlong Java_org_rocksdb_Options_newOptions__JJ(JNIEnv* env, jclass jcls, auto* dbOpt = reinterpret_cast(jdboptions); auto* cfOpt = reinterpret_cast( jcfoptions); - rocksdb::Options* op = new rocksdb::Options(*dbOpt, *cfOpt); + auto* op = new rocksdb::Options(*dbOpt, *cfOpt); return reinterpret_cast(op); } @@ -64,7 +64,9 @@ jlong Java_org_rocksdb_Options_newOptions__JJ(JNIEnv* env, jclass jcls, */ void Java_org_rocksdb_Options_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* op = reinterpret_cast(handle); + assert(op != nullptr); + delete op; } /* @@ -157,10 +159,16 @@ void Java_org_rocksdb_Options_setComparatorHandle__JJ( */ void Java_org_rocksdb_Options_setMergeOperatorName( JNIEnv* env, jobject jobj, jlong jhandle, jstring jop_name) { - auto options = reinterpret_cast(jhandle); - const char* op_name = env->GetStringUTFChars(jop_name, 0); + const char* op_name = env->GetStringUTFChars(jop_name, nullptr); + if(op_name == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + + auto* options = reinterpret_cast(jhandle); options->merge_operator = rocksdb::MergeOperators::CreateFromStringId( op_name); + env->ReleaseStringUTFChars(jop_name, op_name); } @@ -231,7 +239,7 @@ void Java_org_rocksdb_Options_createStatistics( */ jlong Java_org_rocksdb_Options_statisticsPtr( JNIEnv* env, jobject jobj, jlong jOptHandle) { - auto st = reinterpret_cast(jOptHandle)->statistics.get(); + auto* st = reinterpret_cast(jOptHandle)->statistics.get(); return reinterpret_cast(st); } @@ -381,7 +389,11 @@ jstring Java_org_rocksdb_Options_dbLogDir( */ void Java_org_rocksdb_Options_setDbLogDir( JNIEnv* env, jobject jobj, jlong jhandle, jstring jdb_log_dir) { - const char* log_dir = env->GetStringUTFChars(jdb_log_dir, 0); + const char* log_dir = env->GetStringUTFChars(jdb_log_dir, nullptr); + if(log_dir == nullptr) { + // exception thrown: OutOfMemoryError + return; + } reinterpret_cast(jhandle)->db_log_dir.assign(log_dir); env->ReleaseStringUTFChars(jdb_log_dir, log_dir); } @@ -404,7 +416,11 @@ jstring Java_org_rocksdb_Options_walDir( */ void Java_org_rocksdb_Options_setWalDir( JNIEnv* env, jobject jobj, jlong jhandle, jstring jwal_dir) { - const char* wal_dir = env->GetStringUTFChars(jwal_dir, 0); + const char* wal_dir = env->GetStringUTFChars(jwal_dir, nullptr); + if(wal_dir == nullptr) { + // exception thrown: OutOfMemoryError + return; + } reinterpret_cast(jhandle)->wal_dir.assign(wal_dir); env->ReleaseStringUTFChars(jwal_dir, wal_dir); } @@ -494,8 +510,7 @@ void Java_org_rocksdb_Options_setMaxSubcompactions( */ jint Java_org_rocksdb_Options_maxSubcompactions( JNIEnv* env, jobject jobj, jlong jhandle) { - return reinterpret_cast(jhandle) - ->max_subcompactions; + return reinterpret_cast(jhandle)->max_subcompactions; } /* @@ -641,7 +656,7 @@ jlong Java_org_rocksdb_Options_maxManifestFileSize( */ jstring Java_org_rocksdb_Options_memTableFactoryName( JNIEnv* env, jobject jobj, jlong jhandle) { - auto opt = reinterpret_cast(jhandle); + auto* opt = reinterpret_cast(jhandle); rocksdb::MemTableRepFactory* tf = opt->memtable_factory.get(); // Should never be nullptr. @@ -677,17 +692,6 @@ void Java_org_rocksdb_Options_setMemTableFactory( reinterpret_cast(jfactory_handle)); } -/* - * Class: org_rocksdb_Options - * Method: setOldRateLimiter - * Signature: (JJ)V - */ -void Java_org_rocksdb_Options_setOldRateLimiter( - JNIEnv* env, jobject jobj, jlong jhandle, jlong jrate_limiter_handle) { - reinterpret_cast(jhandle)->rate_limiter.reset( - reinterpret_cast(jrate_limiter_handle)); -} - /* * Class: org_rocksdb_Options * Method: setRateLimiter @@ -1144,7 +1148,7 @@ jlong Java_org_rocksdb_Options_writeThreadSlowYieldUsec( */ jstring Java_org_rocksdb_Options_tableFactoryName( JNIEnv* env, jobject jobj, jlong jhandle) { - auto opt = reinterpret_cast(jhandle); + auto* opt = reinterpret_cast(jhandle); rocksdb::TableFactory* tf = opt->table_factory.get(); // Should never be nullptr. @@ -1224,46 +1228,78 @@ jbyte Java_org_rocksdb_Options_compressionType( return reinterpret_cast(jhandle)->compression; } -/* - * Helper method to convert a Java list to a CompressionType - * vector. - */ -std::vector rocksdb_compression_vector_helper( - JNIEnv* env, jbyteArray jcompressionLevels) { - std::vector compressionLevels; +/** + * Helper method to convert a Java byte array of compression levels + * to a C++ vector of rocksdb::CompressionType + * + * @param env A pointer to the Java environment + * @param jcompression_levels A reference to a java byte array + * where each byte indicates a compression level + * + * @return A unique_ptr to the vector, or unique_ptr(nullptr) if a JNI exception occurs + */ +std::unique_ptr> rocksdb_compression_vector_helper( + JNIEnv* env, jbyteArray jcompression_levels) { + jsize len = env->GetArrayLength(jcompression_levels); + jbyte* jcompression_level = + env->GetByteArrayElements(jcompression_levels, nullptr); + if(jcompression_level == nullptr) { + // exception thrown: OutOfMemoryError + return std::unique_ptr>(); + } + + auto* compression_levels = new std::vector(); + std::unique_ptr> uptr_compression_levels(compression_levels); - jsize len = env->GetArrayLength(jcompressionLevels); - jbyte* jcompressionLevel = env->GetByteArrayElements(jcompressionLevels, - NULL); - for(int i = 0; i < len; i++) { - jbyte jcl; - jcl = jcompressionLevel[i]; - compressionLevels.push_back(static_cast(jcl)); + for(jsize i = 0; i < len; i++) { + jbyte jcl = jcompression_level[i]; + compression_levels->push_back(static_cast(jcl)); } - env->ReleaseByteArrayElements(jcompressionLevels, jcompressionLevel, + + env->ReleaseByteArrayElements(jcompression_levels, jcompression_level, JNI_ABORT); - return compressionLevels; + return uptr_compression_levels; } -/* - * Helper method to convert a CompressionType vector to a Java - * List. +/** + * Helper method to convert a C++ vector of rocksdb::CompressionType + * to a Java byte array of compression levels + * + * @param env A pointer to the Java environment + * @param jcompression_levels A reference to a java byte array + * where each byte indicates a compression level + * + * @return A jbytearray or nullptr if an exception occurs */ jbyteArray rocksdb_compression_list_helper(JNIEnv* env, - std::vector compressionLevels) { - std::unique_ptr jbuf = - std::unique_ptr(new jbyte[compressionLevels.size()]); - for (std::vector::size_type i = 0; - i != compressionLevels.size(); i++) { - jbuf[i] = compressionLevels[i]; + std::vector compression_levels) { + const size_t len = compression_levels.size(); + jbyte* jbuf = new jbyte[len]; + + for (size_t i = 0; i < len; i++) { + jbuf[i] = compression_levels[i]; } + // insert in java array - jbyteArray jcompressionLevels = env->NewByteArray( - static_cast(compressionLevels.size())); - env->SetByteArrayRegion(jcompressionLevels, 0, - static_cast(compressionLevels.size()), jbuf.get()); - return jcompressionLevels; + jbyteArray jcompression_levels = env->NewByteArray(static_cast(len)); + if(jcompression_levels == nullptr) { + // exception thrown: OutOfMemoryError + delete [] jbuf; + return nullptr; + } + env->SetByteArrayRegion(jcompression_levels, 0, static_cast(len), + jbuf); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jcompression_levels); + delete [] jbuf; + return nullptr; + } + + delete [] jbuf; + + return jcompression_levels; } /* @@ -1274,10 +1310,14 @@ jbyteArray rocksdb_compression_list_helper(JNIEnv* env, void Java_org_rocksdb_Options_setCompressionPerLevel( JNIEnv* env, jobject jobj, jlong jhandle, jbyteArray jcompressionLevels) { - auto* options = reinterpret_cast(jhandle); - std::vector compressionLevels = + auto uptr_compression_levels = rocksdb_compression_vector_helper(env, jcompressionLevels); - options->compression_per_level = compressionLevels; + if(!uptr_compression_levels) { + // exception occurred + return; + } + auto* options = reinterpret_cast(jhandle); + options->compression_per_level = *(uptr_compression_levels.get()); } /* @@ -1946,7 +1986,6 @@ jlong Java_org_rocksdb_Options_memtableHugePageSize( void Java_org_rocksdb_Options_setMemtableHugePageSize( JNIEnv* env, jobject jobj, jlong jhandle, jlong jmemtable_huge_page_size) { - rocksdb::Status s = rocksdb::check_if_jlong_fits_size_t( jmemtable_huge_page_size); if (s.ok()) { @@ -2083,8 +2122,9 @@ void Java_org_rocksdb_Options_setLevel0StopWritesTrigger( */ jintArray Java_org_rocksdb_Options_maxBytesForLevelMultiplierAdditional( JNIEnv* env, jobject jobj, jlong jhandle) { - auto mbflma = reinterpret_cast( - jhandle)->max_bytes_for_level_multiplier_additional; + auto mbflma = + reinterpret_cast(jhandle)-> + max_bytes_for_level_multiplier_additional; const size_t size = mbflma.size(); @@ -2095,7 +2135,19 @@ jintArray Java_org_rocksdb_Options_maxBytesForLevelMultiplierAdditional( jsize jlen = static_cast(size); jintArray result = env->NewIntArray(jlen); + if(result == nullptr) { + // exception thrown: OutOfMemoryError + delete [] additionals; + return nullptr; + } + env->SetIntArrayRegion(result, 0, jlen, additionals); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(result); + delete [] additionals; + return nullptr; + } delete [] additionals; @@ -2112,12 +2164,20 @@ void Java_org_rocksdb_Options_setMaxBytesForLevelMultiplierAdditional( jintArray jmax_bytes_for_level_multiplier_additional) { jsize len = env->GetArrayLength(jmax_bytes_for_level_multiplier_additional); jint *additionals = - env->GetIntArrayElements(jmax_bytes_for_level_multiplier_additional, 0); + env->GetIntArrayElements(jmax_bytes_for_level_multiplier_additional, nullptr); + if(additionals == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + auto* opt = reinterpret_cast(jhandle); opt->max_bytes_for_level_multiplier_additional.clear(); for (jsize i = 0; i < len; i++) { opt->max_bytes_for_level_multiplier_additional.push_back(static_cast(additionals[i])); } + + env->ReleaseIntArrayElements(jmax_bytes_for_level_multiplier_additional, + additionals, JNI_ABORT); } /* @@ -2153,7 +2213,7 @@ void Java_org_rocksdb_Options_setParanoidFileChecks( */ jlong Java_org_rocksdb_ColumnFamilyOptions_newColumnFamilyOptions( JNIEnv* env, jclass jcls) { - rocksdb::ColumnFamilyOptions* op = new rocksdb::ColumnFamilyOptions(); + auto* op = new rocksdb::ColumnFamilyOptions(); return reinterpret_cast(op); } @@ -2164,14 +2224,20 @@ jlong Java_org_rocksdb_ColumnFamilyOptions_newColumnFamilyOptions( */ jlong Java_org_rocksdb_ColumnFamilyOptions_getColumnFamilyOptionsFromProps( JNIEnv* env, jclass jclazz, jstring jopt_string) { - jlong ret_value = 0; - rocksdb::ColumnFamilyOptions* cf_options = - new rocksdb::ColumnFamilyOptions(); - const char* opt_string = env->GetStringUTFChars(jopt_string, 0); + const char* opt_string = env->GetStringUTFChars(jopt_string, nullptr); + if(opt_string == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + + auto* cf_options = new rocksdb::ColumnFamilyOptions(); rocksdb::Status status = rocksdb::GetColumnFamilyOptionsFromString( rocksdb::ColumnFamilyOptions(), opt_string, cf_options); + env->ReleaseStringUTFChars(jopt_string, opt_string); + // Check if ColumnFamilyOptions creation was possible. + jlong ret_value = 0; if (status.ok()) { ret_value = reinterpret_cast(cf_options); } else { @@ -2189,7 +2255,9 @@ jlong Java_org_rocksdb_ColumnFamilyOptions_getColumnFamilyOptionsFromProps( */ void Java_org_rocksdb_ColumnFamilyOptions_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* cfo = reinterpret_cast(handle); + assert(cfo != nullptr); + delete cfo; } /* @@ -2265,10 +2333,15 @@ void Java_org_rocksdb_ColumnFamilyOptions_setComparatorHandle__JJ( */ void Java_org_rocksdb_ColumnFamilyOptions_setMergeOperatorName( JNIEnv* env, jobject jobj, jlong jhandle, jstring jop_name) { - auto options = reinterpret_cast(jhandle); - const char* op_name = env->GetStringUTFChars(jop_name, 0); - options->merge_operator = rocksdb::MergeOperators::CreateFromStringId( - op_name); + auto* options = reinterpret_cast(jhandle); + const char* op_name = env->GetStringUTFChars(jop_name, nullptr); + if(op_name == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + + options->merge_operator = + rocksdb::MergeOperators::CreateFromStringId(op_name); env->ReleaseStringUTFChars(jop_name, op_name); } @@ -2364,7 +2437,7 @@ void Java_org_rocksdb_ColumnFamilyOptions_setMemTableFactory( */ jstring Java_org_rocksdb_ColumnFamilyOptions_memTableFactoryName( JNIEnv* env, jobject jobj, jlong jhandle) { - auto opt = reinterpret_cast(jhandle); + auto* opt = reinterpret_cast(jhandle); rocksdb::MemTableRepFactory* tf = opt->memtable_factory.get(); // Should never be nullptr. @@ -2418,7 +2491,7 @@ void Java_org_rocksdb_ColumnFamilyOptions_setTableFactory( */ jstring Java_org_rocksdb_ColumnFamilyOptions_tableFactoryName( JNIEnv* env, jobject jobj, jlong jhandle) { - auto opt = reinterpret_cast(jhandle); + auto* opt = reinterpret_cast(jhandle); rocksdb::TableFactory* tf = opt->table_factory.get(); // Should never be nullptr. @@ -2508,9 +2581,13 @@ void Java_org_rocksdb_ColumnFamilyOptions_setCompressionPerLevel( JNIEnv* env, jobject jobj, jlong jhandle, jbyteArray jcompressionLevels) { auto* options = reinterpret_cast(jhandle); - std::vector compressionLevels = + auto uptr_compression_levels = rocksdb_compression_vector_helper(env, jcompressionLevels); - options->compression_per_level = compressionLevels; + if(!uptr_compression_levels) { + // exception occurred + return; + } + options->compression_per_level = *(uptr_compression_levels.get()); } /* @@ -2520,9 +2597,9 @@ void Java_org_rocksdb_ColumnFamilyOptions_setCompressionPerLevel( */ jbyteArray Java_org_rocksdb_ColumnFamilyOptions_compressionPerLevel( JNIEnv* env, jobject jobj, jlong jhandle) { - auto* options = reinterpret_cast(jhandle); + auto* cf_options = reinterpret_cast(jhandle); return rocksdb_compression_list_helper(env, - options->compression_per_level); + cf_options->compression_per_level); } /* @@ -2668,7 +2745,7 @@ void Java_org_rocksdb_ColumnFamilyOptions_setLevelZeroStopWritesTrigger( */ jint Java_org_rocksdb_ColumnFamilyOptions_maxMemCompactionLevel( JNIEnv* env, jobject jobj, jlong jhandle) { - return 0; + return 0; // deprecated and intentionally not implemented, see the Java code } /* @@ -2677,7 +2754,9 @@ jint Java_org_rocksdb_ColumnFamilyOptions_maxMemCompactionLevel( * Signature: (JI)V */ void Java_org_rocksdb_ColumnFamilyOptions_setMaxMemCompactionLevel( - JNIEnv* env, jobject jobj, jlong jhandle, jint jmax_mem_compaction_level) {} + JNIEnv* env, jobject jobj, jlong jhandle, jint jmax_mem_compaction_level) { + // deprecated and intentionally not implemented, see the Java code +} /* * Class: org_rocksdb_ColumnFamilyOptions @@ -3308,9 +3387,19 @@ jintArray Java_org_rocksdb_ColumnFamilyOptions_maxBytesForLevelMultiplierAdditio } jsize jlen = static_cast(size); - jintArray result; - result = env->NewIntArray(jlen); + jintArray result = env->NewIntArray(jlen); + if(result == nullptr) { + // exception thrown: OutOfMemoryError + delete [] additionals; + return nullptr; + } env->SetIntArrayRegion(result, 0, jlen, additionals); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(result); + delete [] additionals; + return nullptr; + } delete [] additionals; @@ -3328,11 +3417,19 @@ void Java_org_rocksdb_ColumnFamilyOptions_setMaxBytesForLevelMultiplierAdditiona jsize len = env->GetArrayLength(jmax_bytes_for_level_multiplier_additional); jint *additionals = env->GetIntArrayElements(jmax_bytes_for_level_multiplier_additional, 0); + if(additionals == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + auto* cf_opt = reinterpret_cast(jhandle); cf_opt->max_bytes_for_level_multiplier_additional.clear(); for (jsize i = 0; i < len; i++) { cf_opt->max_bytes_for_level_multiplier_additional.push_back(static_cast(additionals[i])); } + + env->ReleaseIntArrayElements(jmax_bytes_for_level_multiplier_additional, + additionals, JNI_ABORT); } /* @@ -3369,7 +3466,7 @@ void Java_org_rocksdb_ColumnFamilyOptions_setParanoidFileChecks( */ jlong Java_org_rocksdb_DBOptions_newDBOptions(JNIEnv* env, jclass jcls) { - rocksdb::DBOptions* dbop = new rocksdb::DBOptions(); + auto* dbop = new rocksdb::DBOptions(); return reinterpret_cast(dbop); } @@ -3380,14 +3477,20 @@ jlong Java_org_rocksdb_DBOptions_newDBOptions(JNIEnv* env, */ jlong Java_org_rocksdb_DBOptions_getDBOptionsFromProps( JNIEnv* env, jclass jclazz, jstring jopt_string) { - jlong ret_value = 0; - rocksdb::DBOptions* db_options = - new rocksdb::DBOptions(); - const char* opt_string = env->GetStringUTFChars(jopt_string, 0); + const char* opt_string = env->GetStringUTFChars(jopt_string, nullptr); + if(opt_string == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + + auto* db_options = new rocksdb::DBOptions(); rocksdb::Status status = rocksdb::GetDBOptionsFromString( rocksdb::DBOptions(), opt_string, db_options); + env->ReleaseStringUTFChars(jopt_string, opt_string); + // Check if DBOptions creation was possible. + jlong ret_value = 0; if (status.ok()) { ret_value = reinterpret_cast(db_options); } else { @@ -3405,7 +3508,9 @@ jlong Java_org_rocksdb_DBOptions_getDBOptionsFromProps( */ void Java_org_rocksdb_DBOptions_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* dbo = reinterpret_cast(handle); + assert(dbo != nullptr); + delete dbo; } /* @@ -3505,17 +3610,6 @@ jboolean Java_org_rocksdb_DBOptions_paranoidChecks( return reinterpret_cast(jhandle)->paranoid_checks; } -/* - * Class: org_rocksdb_DBOptions - * Method: setOldRateLimiter - * Signature: (JJ)V - */ -void Java_org_rocksdb_DBOptions_setOldRateLimiter( - JNIEnv* env, jobject jobj, jlong jhandle, jlong jrate_limiter_handle) { - reinterpret_cast(jhandle)->rate_limiter.reset( - reinterpret_cast(jrate_limiter_handle)); -} - /* * Class: org_rocksdb_DBOptions * Method: setRateLimiter @@ -3626,7 +3720,7 @@ void Java_org_rocksdb_DBOptions_createStatistics( */ jlong Java_org_rocksdb_DBOptions_statisticsPtr( JNIEnv* env, jobject jobj, jlong jOptHandle) { - auto st = reinterpret_cast(jOptHandle)-> + auto* st = reinterpret_cast(jOptHandle)-> statistics.get(); return reinterpret_cast(st); } @@ -3659,7 +3753,12 @@ jboolean Java_org_rocksdb_DBOptions_useFsync( */ void Java_org_rocksdb_DBOptions_setDbLogDir( JNIEnv* env, jobject jobj, jlong jhandle, jstring jdb_log_dir) { - const char* log_dir = env->GetStringUTFChars(jdb_log_dir, 0); + const char* log_dir = env->GetStringUTFChars(jdb_log_dir, nullptr); + if(log_dir == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + reinterpret_cast(jhandle)->db_log_dir.assign(log_dir); env->ReleaseStringUTFChars(jdb_log_dir, log_dir); } @@ -4307,19 +4406,17 @@ jlong Java_org_rocksdb_DBOptions_writeThreadSlowYieldUsec( } void Java_org_rocksdb_DBOptions_setDelayedWriteRate( - JNIEnv* env, jobject jobj, jlong jhandle, jlong delay_write_rate){ - - reinterpret_cast(jhandle)-> - delayed_write_rate = static_cast(delay_write_rate); - - } + JNIEnv* env, jobject jobj, jlong jhandle, jlong delay_write_rate) { + reinterpret_cast(jhandle)->delayed_write_rate = + static_cast(delay_write_rate); +} - jlong Java_org_rocksdb_DBOptions_delayedWriteRate( - JNIEnv* env, jobject jobj, jlong jhandle){ +jlong Java_org_rocksdb_DBOptions_delayedWriteRate( + JNIEnv* env, jobject jobj, jlong jhandle) { + return reinterpret_cast(jhandle)-> + delayed_write_rate; +} - return reinterpret_cast(jhandle)-> - delayed_write_rate; - } ////////////////////////////////////////////////////////////////////////////// // rocksdb::WriteOptions @@ -4330,7 +4427,7 @@ void Java_org_rocksdb_DBOptions_setDelayedWriteRate( */ jlong Java_org_rocksdb_WriteOptions_newWriteOptions( JNIEnv* env, jclass jcls) { - rocksdb::WriteOptions* op = new rocksdb::WriteOptions(); + auto* op = new rocksdb::WriteOptions(); return reinterpret_cast(op); } @@ -4341,7 +4438,8 @@ jlong Java_org_rocksdb_WriteOptions_newWriteOptions( */ void Java_org_rocksdb_WriteOptions_disposeInternal( JNIEnv* env, jobject jwrite_options, jlong jhandle) { - auto write_options = reinterpret_cast(jhandle); + auto* write_options = reinterpret_cast(jhandle); + assert(write_options != nullptr); delete write_options; } @@ -4395,8 +4493,8 @@ jboolean Java_org_rocksdb_WriteOptions_disableWAL( */ jlong Java_org_rocksdb_ReadOptions_newReadOptions( JNIEnv* env, jclass jcls) { - auto read_opt = new rocksdb::ReadOptions(); - return reinterpret_cast(read_opt); + auto* read_options = new rocksdb::ReadOptions(); + return reinterpret_cast(read_options); } /* @@ -4406,7 +4504,9 @@ jlong Java_org_rocksdb_ReadOptions_newReadOptions( */ void Java_org_rocksdb_ReadOptions_disposeInternal( JNIEnv* env, jobject jobj, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* read_options = reinterpret_cast(jhandle); + assert(read_options != nullptr); + delete read_options; } /* @@ -4613,7 +4713,7 @@ void Java_org_rocksdb_ReadOptions_setReadTier( */ jlong Java_org_rocksdb_ComparatorOptions_newComparatorOptions( JNIEnv* env, jclass jcls) { - auto comparator_opt = new rocksdb::ComparatorJniCallbackOptions(); + auto* comparator_opt = new rocksdb::ComparatorJniCallbackOptions(); return reinterpret_cast(comparator_opt); } @@ -4646,7 +4746,10 @@ void Java_org_rocksdb_ComparatorOptions_setUseAdaptiveMutex( */ void Java_org_rocksdb_ComparatorOptions_disposeInternal( JNIEnv * env, jobject jobj, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* comparator_opt = + reinterpret_cast(jhandle); + assert(comparator_opt != nullptr); + delete comparator_opt; } ///////////////////////////////////////////////////////////////////// @@ -4659,7 +4762,7 @@ void Java_org_rocksdb_ComparatorOptions_disposeInternal( */ jlong Java_org_rocksdb_FlushOptions_newFlushOptions( JNIEnv* env, jclass jcls) { - auto flush_opt = new rocksdb::FlushOptions(); + auto* flush_opt = new rocksdb::FlushOptions(); return reinterpret_cast(flush_opt); } @@ -4692,5 +4795,7 @@ jboolean Java_org_rocksdb_FlushOptions_waitForFlush( */ void Java_org_rocksdb_FlushOptions_disposeInternal( JNIEnv * env, jobject jobj, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* flush_opt = reinterpret_cast(jhandle); + assert(flush_opt != nullptr); + delete flush_opt; } diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index d4408f497..e4b9f356d 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -41,10 +42,19 @@ inline Status check_if_jlong_fits_size_t(const jlong& jvalue) { return s; } -// Native class template -template class RocksDBNativeClass { +class JavaClass { public: - // Get the java class id + /** + * Gets and initializes a Java Class + * + * @param env A pointer to the Java environment + * @param jclazz_name The fully qualified JNI name of the Java Class + * e.g. "java/lang/String" + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env, const char* jclazz_name) { jclass jclazz = env->FindClass(jclazz_name); assert(jclazz != nullptr); @@ -52,52 +62,106 @@ template class RocksDBNativeClass { } }; +// Native class template +template class RocksDBNativeClass : public JavaClass { +}; + // Native class template for sub-classes of RocksMutableObject template class NativeRocksMutableObject : public RocksDBNativeClass { public: + /** + * Gets the Java Method ID for the + * RocksMutableObject#setNativeHandle(long, boolean) method + * + * @param env A pointer to the Java environment + * @return The Java Method ID or nullptr the RocksMutableObject class cannot + * be accessed, or if one of the NoSuchMethodError, + * ExceptionInInitializerError or OutOfMemoryError exceptions is thrown + */ static jmethodID getSetNativeHandleMethod(JNIEnv* env) { + static jclass jclazz = DERIVED::getJClass(env); + if(jclazz == nullptr) { + return nullptr; + } + static jmethodID mid = env->GetMethodID( - DERIVED::getJClass(env), "setNativeHandle", "(JZ)V"); + jclazz, "setNativeHandle", "(JZ)V"); assert(mid != nullptr); return mid; } - // Pass the pointer to the java side. - static void setHandle(JNIEnv* env, jobject jobj, PTR ptr, + /** + * Sets the C++ object pointer handle in the Java object + * + * @param env A pointer to the Java environment + * @param jobj The Java object on which to set the pointer handle + * @param ptr The C++ object pointer + * @param java_owns_handle JNI_TRUE if ownership of the C++ object is + * managed by the Java object + * + * @return true if a Java exception is pending, false otherwise + */ + static bool setHandle(JNIEnv* env, jobject jobj, PTR ptr, jboolean java_owns_handle) { - env->CallVoidMethod(jobj, getSetNativeHandleMethod(env), - reinterpret_cast(ptr), java_owns_handle); + assert(jobj != nullptr); + static jmethodID mid = getSetNativeHandleMethod(env); + if(mid == nullptr) { + return true; // signal exception + } + + env->CallVoidMethod(jobj, mid, reinterpret_cast(ptr), java_owns_handle); + if(env->ExceptionCheck()) { + return true; // signal exception + } + + return false; } }; // Java Exception template -template class RocksDBJavaException { +template class JavaException : public JavaClass { public: - // Get the java class id - static jclass getJClass(JNIEnv* env, const char* jclazz_name) { - jclass jclazz = env->FindClass(jclazz_name); - assert(jclazz != nullptr); - return jclazz; - } + /** + * Create and throw a java exception with the provided message + * + * @param env A pointer to the Java environment + * @param msg The message for the exception + * + * @return true if an exception was thrown, false otherwise + */ + static bool ThrowNew(JNIEnv* env, const std::string& msg) { + jclass jclazz = DERIVED::getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + std::cerr << "JavaException::ThrowNew - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); + } - // Create and throw a java exception with the provided message - static void ThrowNew(JNIEnv* env, const std::string& msg) { - jstring jmsg = env->NewStringUTF(msg.c_str()); - // get the constructor id of org.rocksdb.RocksDBException - static jmethodID mid = env->GetMethodID( - DERIVED::getJClass(env), "", "(Ljava/lang/String;)V"); - assert(mid != nullptr); + const jint rs = env->ThrowNew(jclazz, msg.c_str()); + if(rs != JNI_OK) { + // exception could not be thrown + std::cerr << "JavaException::ThrowNew - Fatal: could not throw exception!" << std::endl; + return env->ExceptionCheck(); + } - env->Throw((jthrowable)env->NewObject(DERIVED::getJClass(env), mid, jmsg)); + return true; } }; // The portal class for org.rocksdb.RocksDB class RocksDBJni : public RocksDBNativeClass { public: - // Get the java class id of org.rocksdb.RocksDB. + /** + * Get the Java Class org.rocksdb.RocksDB + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, "org/rocksdb/RocksDB"); } @@ -106,25 +170,72 @@ class RocksDBJni : public RocksDBNativeClass { // The portal class for org.rocksdb.Status class StatusJni : public RocksDBNativeClass { public: - // Get the java class id of org.rocksdb.Status. + /** + * Get the Java Class org.rocksdb.Status + * + * @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/Status"); } - // Create a new org.rocksdb.Status with the same properties as the - // provided C++ rocksdb::Status object + /** + * Create a new Java org.rocksdb.Status object with the same properties as + * the provided C++ rocksdb::Status object + * + * @param env A pointer to the Java environment + * @param status The rocksdb::Status object + * + * @return A reference to a Java org.rocksdb.Status object, or nullptr + * if an an exception occurs + */ static jobject construct(JNIEnv* env, const Status& status) { - static jmethodID mid = - env->GetMethodID(getJClass(env), "", "(BBLjava/lang/String;)V"); - assert(mid != nullptr); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + jmethodID mid = + env->GetMethodID(jclazz, "", "(BBLjava/lang/String;)V"); + if(mid == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return nullptr; + } + // convert the Status state for Java jstring jstate = nullptr; if (status.getState() != nullptr) { const char* const state = status.getState(); jstate = env->NewStringUTF(state); + if(env->ExceptionCheck()) { + if(jstate != nullptr) { + env->DeleteLocalRef(jstate); + } + return nullptr; + } + } + + jobject jstatus = + env->NewObject(jclazz, mid, toJavaStatusCode(status.code()), + toJavaStatusSubCode(status.subcode()), jstate); + if(env->ExceptionCheck()) { + // exception occurred + if(jstate != nullptr) { + env->DeleteLocalRef(jstate); + } + return nullptr; + } + + if(jstate != nullptr) { + env->DeleteLocalRef(jstate); } - return env->NewObject(getJClass(env), mid, toJavaStatusCode(status.code()), - toJavaStatusSubCode(status.subcode()), jstate); + + return jstatus; } // Returns the equivalent org.rocksdb.Status.Code for the provided @@ -186,76 +297,253 @@ class StatusJni : public RocksDBNativeClass { // The portal class for org.rocksdb.RocksDBException class RocksDBExceptionJni : - public RocksDBJavaException { + public JavaException { public: - // Get the java class id of java.lang.IllegalArgumentException + /** + * Get the Java Class org.rocksdb.RocksDBException + * + * @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 RocksDBJavaException::getJClass(env, - "org/rocksdb/RocksDBException"); - } + return JavaException::getJClass(env, "org/rocksdb/RocksDBException"); + } + + /** + * Create and throw a Java RocksDBException with the provided message + * + * @param env A pointer to the Java environment + * @param msg The message for the exception + * + * @return true if an exception was thrown, false otherwise + */ + static bool ThrowNew(JNIEnv* env, const std::string& msg) { + return JavaException::ThrowNew(env, msg); + } + + /** + * Create and throw a Java RocksDBException with the provided status + * + * If s.ok() == true, then this function will not throw any exception. + * + * @param env A pointer to the Java environment + * @param s The status for the exception + * + * @return true if an exception was thrown, false otherwise + */ + static bool ThrowNew(JNIEnv* env, const Status& s) { + assert(!s.ok()); + if (s.ok()) { + return false; + } - static void ThrowNew(JNIEnv* env, const std::string& msg) { - RocksDBJavaException::ThrowNew(env, msg); - } + // get the RocksDBException class + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + std::cerr << "RocksDBExceptionJni::ThrowNew/class - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); + } - static void ThrowNew(JNIEnv* env, const Status& s) { - if (s.ok()) { - return; + // get the constructor of org.rocksdb.RocksDBException + jmethodID mid = + env->GetMethodID(jclazz, "", "(Lorg/rocksdb/Status;)V"); + if(mid == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + std::cerr << "RocksDBExceptionJni::ThrowNew/cstr - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); } + // get the Java status object jobject jstatus = StatusJni::construct(env, s); + if(jstatus == nullptr) { + // exception occcurred + std::cerr << "RocksDBExceptionJni::ThrowNew/StatusJni - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); + } - // get the constructor id of org.rocksdb.RocksDBException - static jmethodID mid = - env->GetMethodID(getJClass(env), "", "(Lorg/rocksdb/Status;)V"); - assert(mid != nullptr); + // construct the RocksDBException + jthrowable rocksdb_exception = reinterpret_cast(env->NewObject(jclazz, mid, jstatus)); + if(env->ExceptionCheck()) { + if(jstatus != nullptr) { + env->DeleteLocalRef(jstatus); + } + if(rocksdb_exception != nullptr) { + env->DeleteLocalRef(rocksdb_exception); + } + std::cerr << "RocksDBExceptionJni::ThrowNew/NewObject - Error: unexpected exception!" << std::endl; + return true; + } - env->Throw((jthrowable)env->NewObject(getJClass(env), mid, jstatus)); - } + // throw the RocksDBException + const jint rs = env->Throw(rocksdb_exception); + if(rs != JNI_OK) { + // exception could not be thrown + std::cerr << "RocksDBExceptionJni::ThrowNew - Fatal: could not throw exception!" << std::endl; + if(jstatus != nullptr) { + env->DeleteLocalRef(jstatus); + } + if(rocksdb_exception != nullptr) { + env->DeleteLocalRef(rocksdb_exception); + } + return env->ExceptionCheck(); + } - static void ThrowNew(JNIEnv* env, const std::string& msg, const Status& s) { + if(jstatus != nullptr) { + env->DeleteLocalRef(jstatus); + } + if(rocksdb_exception != nullptr) { + env->DeleteLocalRef(rocksdb_exception); + } + + return true; + } + + /** + * Create and throw a Java RocksDBException with the provided message + * and status + * + * If s.ok() == true, then this function will not throw any exception. + * + * @param env A pointer to the Java environment + * @param msg The message for the exception + * @param s The status for the exception + * + * @return true if an exception was thrown, false otherwise + */ + static bool ThrowNew(JNIEnv* env, const std::string& msg, const Status& s) { + assert(!s.ok()); if (s.ok()) { - return; + return false; + } + + // get the RocksDBException class + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + std::cerr << "RocksDBExceptionJni::ThrowNew/class - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); + } + + // get the constructor of org.rocksdb.RocksDBException + jmethodID mid = + env->GetMethodID(jclazz, "", "(Ljava/lang/String;Lorg/rocksdb/Status;)V"); + if(mid == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + std::cerr << "RocksDBExceptionJni::ThrowNew/cstr - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); } jstring jmsg = env->NewStringUTF(msg.c_str()); + if(jmsg == nullptr) { + // exception thrown: OutOfMemoryError + std::cerr << "RocksDBExceptionJni::ThrowNew/msg - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); + } + + // get the Java status object jobject jstatus = StatusJni::construct(env, s); + if(jstatus == nullptr) { + // exception occcurred + std::cerr << "RocksDBExceptionJni::ThrowNew/StatusJni - Error: unexpected exception!" << std::endl; + if(jmsg != nullptr) { + env->DeleteLocalRef(jmsg); + } + return env->ExceptionCheck(); + } - // get the constructor id of org.rocksdb.RocksDBException - static jmethodID mid = env->GetMethodID( - getJClass(env), "", "(Ljava/lang/String;Lorg/rocksdb/Status;)V"); - assert(mid != nullptr); + // construct the RocksDBException + jthrowable rocksdb_exception = reinterpret_cast(env->NewObject(jclazz, mid, jmsg, jstatus)); + if(env->ExceptionCheck()) { + if(jstatus != nullptr) { + env->DeleteLocalRef(jstatus); + } + if(jmsg != nullptr) { + env->DeleteLocalRef(jmsg); + } + if(rocksdb_exception != nullptr) { + env->DeleteLocalRef(rocksdb_exception); + } + std::cerr << "RocksDBExceptionJni::ThrowNew/NewObject - Error: unexpected exception!" << std::endl; + return true; + } - env->Throw((jthrowable)env->NewObject(getJClass(env), mid, jmsg, jstatus)); + // throw the RocksDBException + const jint rs = env->Throw(rocksdb_exception); + if(rs != JNI_OK) { + // exception could not be thrown + std::cerr << "RocksDBExceptionJni::ThrowNew - Fatal: could not throw exception!" << std::endl; + if(jstatus != nullptr) { + env->DeleteLocalRef(jstatus); + } + if(jmsg != nullptr) { + env->DeleteLocalRef(jmsg); + } + if(rocksdb_exception != nullptr) { + env->DeleteLocalRef(rocksdb_exception); + } + return env->ExceptionCheck(); + } + + if(jstatus != nullptr) { + env->DeleteLocalRef(jstatus); + } + if(jmsg != nullptr) { + env->DeleteLocalRef(jmsg); + } + if(rocksdb_exception != nullptr) { + env->DeleteLocalRef(rocksdb_exception); + } + + return true; } }; // The portal class for java.lang.IllegalArgumentException class IllegalArgumentExceptionJni : - public RocksDBJavaException { + public JavaException { public: - // Get the java class id of java.lang.IllegalArgumentException + /** + * Get the Java Class java.lang.IllegalArgumentException + * + * @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 RocksDBJavaException::getJClass(env, - "java/lang/IllegalArgumentException"); - } - - // Create and throw a IllegalArgumentException by converting the input - // Status. - // - // In case s.ok() is true, then this function will not throw any - // exception. - static void ThrowNew(JNIEnv* env, const Status& s) { + return JavaException::getJClass(env, "java/lang/IllegalArgumentException"); + } + + /** + * Create and throw a Java IllegalArgumentException with the provided status + * + * If s.ok() == true, then this function will not throw any exception. + * + * @param env A pointer to the Java environment + * @param s The status for the exception + * + * @return true if an exception was thrown, false otherwise + */ + static bool ThrowNew(JNIEnv* env, const Status& s) { + assert(!s.ok()); if (s.ok()) { - return; + return false; + } + + // get the IllegalArgumentException class + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + std::cerr << "IllegalArgumentExceptionJni::ThrowNew/class - Error: unexpected exception!" << std::endl; + return env->ExceptionCheck(); } - jstring msg = env->NewStringUTF(s.ToString().c_str()); - // get the constructor id of org.rocksdb.RocksDBException - static jmethodID mid = - env->GetMethodID(getJClass(env), "", "(Ljava/lang/String;)V"); - assert(mid != nullptr); - env->Throw((jthrowable)env->NewObject(getJClass(env), mid, msg)); + return JavaException::ThrowNew(env, s.ToString()); } }; @@ -264,6 +552,15 @@ class IllegalArgumentExceptionJni : class OptionsJni : public RocksDBNativeClass< rocksdb::Options*, OptionsJni> { public: + /** + * Get the Java Class org.rocksdb.Options + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, "org/rocksdb/Options"); } @@ -273,34 +570,74 @@ class OptionsJni : public RocksDBNativeClass< class DBOptionsJni : public RocksDBNativeClass< rocksdb::DBOptions*, DBOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.DBOptions + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, "org/rocksdb/DBOptions"); } }; -class ColumnFamilyDescriptorJni { +class ColumnFamilyDescriptorJni : public JavaClass { public: - // Get the java class id of org.rocksdb.ColumnFamilyDescriptor - static jclass getColumnFamilyDescriptorClass(JNIEnv* env) { - jclass jclazz = env->FindClass("org/rocksdb/ColumnFamilyDescriptor"); - assert(jclazz != nullptr); - return jclazz; + /** + * Get the Java Class org.rocksdb.ColumnFamilyDescriptor + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "org/rocksdb/ColumnFamilyDescriptor"); } - // Get the java method id of columnFamilyName + /** + * Get the Java Method: ColumnFamilyDescriptor#columnFamilyName + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getColumnFamilyNameMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getColumnFamilyDescriptorClass(env), - "columnFamilyName", "()[B"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "columnFamilyName", "()[B"); assert(mid != nullptr); return mid; } - // Get the java method id of columnFamilyOptions + /** + * Get the Java Method: ColumnFamilyDescriptor#columnFamilyOptions + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getColumnFamilyOptionsMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getColumnFamilyDescriptorClass(env), - "columnFamilyOptions", "()Lorg/rocksdb/ColumnFamilyOptions;"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "columnFamilyOptions", + "()Lorg/rocksdb/ColumnFamilyOptions;"); assert(mid != nullptr); return mid; } @@ -310,6 +647,15 @@ class ColumnFamilyDescriptorJni { class ColumnFamilyOptionsJni : public RocksDBNativeClass< rocksdb::ColumnFamilyOptions*, ColumnFamilyOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.ColumnFamilyOptions + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, "org/rocksdb/ColumnFamilyOptions"); @@ -320,9 +666,17 @@ class ColumnFamilyOptionsJni : public RocksDBNativeClass< class WriteOptionsJni : public RocksDBNativeClass< rocksdb::WriteOptions*, WriteOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.WriteOptions + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { - return RocksDBNativeClass::getJClass(env, - "org/rocksdb/WriteOptions"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteOptions"); } }; @@ -330,19 +684,35 @@ class WriteOptionsJni : public RocksDBNativeClass< class ReadOptionsJni : public RocksDBNativeClass< rocksdb::ReadOptions*, ReadOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.ReadOptions + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { - return RocksDBNativeClass::getJClass(env, - "org/rocksdb/ReadOptions"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/ReadOptions"); } }; -// The portal class for org.rocksdb.ReadOptions +// The portal class for org.rocksdb.WriteBatch class WriteBatchJni : public RocksDBNativeClass< rocksdb::WriteBatch*, WriteBatchJni> { public: + /** + * Get the Java Class org.rocksdb.WriteBatch + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { - return RocksDBNativeClass::getJClass(env, - "org/rocksdb/WriteBatch"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteBatch"); } }; @@ -351,47 +721,116 @@ class WriteBatchHandlerJni : public RocksDBNativeClass< const rocksdb::WriteBatchHandlerJniCallback*, WriteBatchHandlerJni> { public: + /** + * Get the Java Class org.rocksdb.WriteBatch.Handler + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, "org/rocksdb/WriteBatch$Handler"); } - // Get the java method `put` of org.rocksdb.WriteBatch.Handler. + /** + * Get the Java Method: WriteBatch.Handler#put + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getPutMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "put", "([B[B)V"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "put", "([B[B)V"); assert(mid != nullptr); return mid; } - // Get the java method `merge` of org.rocksdb.WriteBatch.Handler. + /** + * Get the Java Method: WriteBatch.Handler#merge + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getMergeMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "merge", "([B[B)V"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "merge", "([B[B)V"); assert(mid != nullptr); return mid; } - // Get the java method `delete` of org.rocksdb.WriteBatch.Handler. + /** + * Get the Java Method: WriteBatch.Handler#delete + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getDeleteMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "delete", "([B)V"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "delete", "([B)V"); assert(mid != nullptr); return mid; } - // Get the java method `logData` of org.rocksdb.WriteBatch.Handler. + /** + * Get the Java Method: WriteBatch.Handler#logData + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getLogDataMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "logData", "([B)V"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "logData", "([B)V"); assert(mid != nullptr); return mid; } - // Get the java method `shouldContinue` of org.rocksdb.WriteBatch.Handler. + /** + * Get the Java Method: WriteBatch.Handler#shouldContinue + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getContinueMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "shouldContinue", "()Z"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "shouldContinue", "()Z"); assert(mid != nullptr); return mid; } @@ -401,37 +840,92 @@ class WriteBatchHandlerJni : public RocksDBNativeClass< class WriteBatchWithIndexJni : public RocksDBNativeClass< rocksdb::WriteBatchWithIndex*, WriteBatchWithIndexJni> { public: + /** + * Get the Java Class org.rocksdb.WriteBatchWithIndex + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, - "org/rocksdb/WriteBatch"); + "org/rocksdb/WriteBatchWithIndex"); } }; -class HistogramDataJni { +// The portal class for org.rocksdb.HistogramData +class HistogramDataJni : public JavaClass { public: - static jmethodID getConstructorMethodId(JNIEnv* env, jclass jclazz) { + /** + * Get the Java Class org.rocksdb.HistogramData + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "org/rocksdb/HistogramData"); + } + + /** + * Get the Java Method: HistogramData constructor + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ + static jmethodID getConstructorMethodId(JNIEnv* env) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + static jmethodID mid = env->GetMethodID(jclazz, "", "(DDDDD)V"); assert(mid != nullptr); return mid; } }; -// The portal class for org.rocksdb.WriteBatchWithIndex +// The portal class for org.rocksdb.BackupableDBOptions class BackupableDBOptionsJni : public RocksDBNativeClass< rocksdb::BackupableDBOptions*, BackupableDBOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.BackupableDBOptions + * + * @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/BackupableDBOptions"); } }; +// The portal class for org.rocksdb.BackupEngine class BackupEngineJni : public RocksDBNativeClass< rocksdb::BackupEngine*, BackupEngineJni> { public: + /** + * Get the Java Class org.rocksdb.BackupableEngine + * + * @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/BackupEngine"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/BackupEngine"); } }; @@ -439,9 +933,17 @@ class BackupEngineJni : public RocksDBNativeClass< class IteratorJni : public RocksDBNativeClass< rocksdb::Iterator*, IteratorJni> { public: + /** + * Get the Java Class org.rocksdb.RocksIterator + * + * @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/RocksIterator"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/RocksIterator"); } }; @@ -449,9 +951,17 @@ class IteratorJni : public RocksDBNativeClass< class FilterJni : public RocksDBNativeClass< std::shared_ptr*, FilterJni> { public: + /** + * Get the Java Class org.rocksdb.Filter + * + * @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/Filter"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/Filter"); } }; @@ -459,6 +969,15 @@ class FilterJni : public RocksDBNativeClass< class ColumnFamilyHandleJni : public RocksDBNativeClass< rocksdb::ColumnFamilyHandle*, ColumnFamilyHandleJni> { public: + /** + * Get the Java Class org.rocksdb.ColumnFamilyHandle + * + * @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/ColumnFamilyHandle"); @@ -469,9 +988,17 @@ class ColumnFamilyHandleJni : public RocksDBNativeClass< class FlushOptionsJni : public RocksDBNativeClass< rocksdb::FlushOptions*, FlushOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.FlushOptions + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { - return RocksDBNativeClass::getJClass(env, - "org/rocksdb/FlushOptions"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/FlushOptions"); } }; @@ -479,9 +1006,17 @@ class FlushOptionsJni : public RocksDBNativeClass< class ComparatorOptionsJni : public RocksDBNativeClass< rocksdb::ComparatorJniCallbackOptions*, ComparatorOptionsJni> { public: + /** + * Get the Java Class org.rocksdb.ComparatorOptions + * + * @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/ComparatorOptions"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/ComparatorOptions"); } }; @@ -490,42 +1025,103 @@ class AbstractComparatorJni : public RocksDBNativeClass< const rocksdb::BaseComparatorJniCallback*, AbstractComparatorJni> { public: + /** + * Get the Java Class org.rocksdb.AbstractComparator + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getJClass(JNIEnv* env) { return RocksDBNativeClass::getJClass(env, "org/rocksdb/AbstractComparator"); } - // Get the java method `name` of org.rocksdb.Comparator. + /** + * Get the Java Method: Comparator#name + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getNameMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "name", "()Ljava/lang/String;"); + 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 `compare` of org.rocksdb.Comparator. + /** + * Get the Java Method: Comparator#compare + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getCompareMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID(getJClass(env), - "compare", - "(Lorg/rocksdb/AbstractSlice;Lorg/rocksdb/AbstractSlice;)I"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "compare", + "(Lorg/rocksdb/AbstractSlice;Lorg/rocksdb/AbstractSlice;)I"); assert(mid != nullptr); return mid; } - // Get the java method `findShortestSeparator` of org.rocksdb.Comparator. + /** + * Get the Java Method: Comparator#findShortestSeparator + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getFindShortestSeparatorMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID(getJClass(env), - "findShortestSeparator", - "(Ljava/lang/String;Lorg/rocksdb/AbstractSlice;)Ljava/lang/String;"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "findShortestSeparator", + "(Ljava/lang/String;Lorg/rocksdb/AbstractSlice;)Ljava/lang/String;"); assert(mid != nullptr); return mid; } - // Get the java method `findShortSuccessor` of org.rocksdb.Comparator. + /** + * Get the Java Method: Comparator#findShortSuccessor + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getFindShortSuccessorMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID(getJClass(env), - "findShortSuccessor", - "(Ljava/lang/String;)Ljava/lang/String;"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "findShortSuccessor", + "(Ljava/lang/String;)Ljava/lang/String;"); assert(mid != nullptr); return mid; } @@ -535,330 +1131,1262 @@ class AbstractComparatorJni : public RocksDBNativeClass< class AbstractSliceJni : public NativeRocksMutableObject< const rocksdb::Slice*, AbstractSliceJni> { public: + /** + * Get the Java Class org.rocksdb.AbstractSlice + * + * @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/AbstractSlice"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/AbstractSlice"); } }; -class SliceJni { +// The portal class for org.rocksdb.Slice +class SliceJni : public NativeRocksMutableObject< + const rocksdb::Slice*, AbstractSliceJni> { public: - // Get the java class id of org.rocksdb.Slice. + /** + * Get the Java Class org.rocksdb.Slice + * + * @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) { - jclass jclazz = env->FindClass("org/rocksdb/Slice"); - assert(jclazz != nullptr); - return jclazz; + return RocksDBNativeClass::getJClass(env, "org/rocksdb/Slice"); } + /** + * Constructs a Slice object + * + * @param env A pointer to the Java environment + * + * @return A reference to a Java Slice object, or a nullptr if an + * exception occurs + */ static jobject construct0(JNIEnv* env) { - static jmethodID mid = env->GetMethodID(getJClass(env), "", "()V"); - assert(mid != nullptr); - return env->NewObject(getJClass(env), mid); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "", "()V"); + if(mid == nullptr) { + // exception occurred accessing method + return nullptr; + } + + jobject jslice = env->NewObject(jclazz, mid); + if(env->ExceptionCheck()) { + return nullptr; + } + + return jslice; } }; -class DirectSliceJni { +// The portal class for org.rocksdb.DirectSlice +class DirectSliceJni : public NativeRocksMutableObject< + const rocksdb::Slice*, AbstractSliceJni> { public: - // Get the java class id of org.rocksdb.DirectSlice. + /** + * Get the Java Class org.rocksdb.DirectSlice + * + * @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) { - jclass jclazz = env->FindClass("org/rocksdb/DirectSlice"); - assert(jclazz != nullptr); - return jclazz; + return RocksDBNativeClass::getJClass(env, "org/rocksdb/DirectSlice"); } + /** + * Constructs a DirectSlice object + * + * @param env A pointer to the Java environment + * + * @return A reference to a Java DirectSlice object, or a nullptr if an + * exception occurs + */ static jobject construct0(JNIEnv* env) { - static jmethodID mid = env->GetMethodID(getJClass(env), "", "()V"); - assert(mid != nullptr); - return env->NewObject(getJClass(env), mid); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "", "()V"); + if(mid == nullptr) { + // exception occurred accessing method + return nullptr; + } + + jobject jdirect_slice = env->NewObject(jclazz, mid); + if(env->ExceptionCheck()) { + return nullptr; + } + + return jdirect_slice; } }; -class ListJni { +// The portal class for java.util.List +class ListJni : public JavaClass { public: - // Get the java class id of java.util.List. + /** + * Get the Java Class java.util.List + * + * @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 getListClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/util/List"); - assert(jclazz != nullptr); - return jclazz; - } - - // Get the java class id of java.util.ArrayList. + return JavaClass::getJClass(env, "java/util/List"); + } + + /** + * Get the Java Class java.util.ArrayList + * + * @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 getArrayListClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/util/ArrayList"); - assert(jclazz != nullptr); - return jclazz; - } - - // Get the java class id of java.util.Iterator. + return JavaClass::getJClass(env, "java/util/ArrayList"); + } + + /** + * Get the Java Class java.util.Iterator + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ static jclass getIteratorClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/util/Iterator"); - assert(jclazz != nullptr); - return jclazz; + return JavaClass::getJClass(env, "java/util/Iterator"); } - // Get the java method id of java.util.List.iterator(). + /** + * Get the Java Method: List#iterator + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getIteratorMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getListClass(env), "iterator", "()Ljava/util/Iterator;"); + jclass jlist_clazz = getListClass(env); + if(jlist_clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jlist_clazz, "iterator", "()Ljava/util/Iterator;"); assert(mid != nullptr); return mid; } - // Get the java method id of java.util.Iterator.hasNext(). + /** + * Get the Java Method: Iterator#hasNext + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getHasNextMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getIteratorClass(env), "hasNext", "()Z"); + jclass jiterator_clazz = getIteratorClass(env); + if(jiterator_clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jiterator_clazz, "hasNext", "()Z"); assert(mid != nullptr); return mid; } - // Get the java method id of java.util.Iterator.next(). + /** + * Get the Java Method: Iterator#next + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getNextMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getIteratorClass(env), "next", "()Ljava/lang/Object;"); + jclass jiterator_clazz = getIteratorClass(env); + if(jiterator_clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jiterator_clazz, "next", "()Ljava/lang/Object;"); assert(mid != nullptr); return mid; } - // Get the java method id of arrayList constructor. - static jmethodID getArrayListConstructorMethodId(JNIEnv* env, jclass jclazz) { - static jmethodID mid = env->GetMethodID( - jclazz, "", "(I)V"); + /** + * Get the Java Method: ArrayList constructor + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ + static jmethodID getArrayListConstructorMethodId(JNIEnv* env) { + jclass jarray_list_clazz = getArrayListClass(env); + if(jarray_list_clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + static jmethodID mid = + env->GetMethodID(jarray_list_clazz, "", "(I)V"); assert(mid != nullptr); return mid; } - // Get the java method id of java.util.List.add(). + /** + * Get the Java Method: List#add + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getListAddMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getListClass(env), "add", "(Ljava/lang/Object;)Z"); + jclass jlist_clazz = getListClass(env); + if(jlist_clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jlist_clazz, "add", "(Ljava/lang/Object;)Z"); assert(mid != nullptr); return mid; } }; -class ByteJni { +// The portal class for java.lang.Byte +class ByteJni : public JavaClass { public: - // Get the java class id of java.lang.Byte. - static jclass getByteClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/lang/Byte"); - assert(jclazz != nullptr); - return jclazz; + /** + * Get the Java Class java.lang.Byte + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "java/lang/Byte"); + } + + /** + * Get the Java Class byte[] + * + * @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 getArrayJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "[B"); + } + + /** + * Creates a new 2-dimensional Java Byte Array byte[][] + * + * @param env A pointer to the Java environment + * @param len The size of the first dimension + * + * @return A reference to the Java byte[][] or nullptr if an exception occurs + */ + static jobjectArray new2dByteArray(JNIEnv* env, const jsize len) { + jclass clazz = getArrayJClass(env); + if(clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + return env->NewObjectArray(len, clazz, nullptr); } - // Get the java method id of java.lang.Byte.byteValue. + /** + * Get the Java Method: Byte#byteValue + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ static jmethodID getByteValueMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getByteClass(env), "byteValue", "()B"); + jclass clazz = getJClass(env); + if(clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(clazz, "byteValue", "()B"); assert(mid != nullptr); return mid; } }; -class BackupInfoJni { - public: - // Get the java class id of org.rocksdb.BackupInfo. +// The portal class for java.lang.StringBuilder +class StringBuilderJni : public JavaClass { + public: + /** + * Get the Java Class java.lang.StringBuilder + * + * @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) { - jclass jclazz = env->FindClass("org/rocksdb/BackupInfo"); - assert(jclazz != nullptr); - return jclazz; + return JavaClass::getJClass(env, "java/lang/StringBuilder"); + } + + /** + * Get the Java Method: StringBuilder#append + * + * @param env A pointer to the Java environment + * + * @return The Java Method ID or nullptr if the class or method id could not + * be retieved + */ + static jmethodID getListAddMethodId(JNIEnv* env) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "append", + "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); + assert(mid != nullptr); + return mid; + } + + /** + * Appends a C-style string to a StringBuilder + * + * @param env A pointer to the Java environment + * @param jstring_builder Reference to a java.lang.StringBuilder + * @param c_str A C-style string to append to the StringBuilder + * + * @return A reference to the updated StringBuilder, or a nullptr if + * an exception occurs + */ + static jobject append(JNIEnv* env, jobject jstring_builder, + const char* c_str) { + jmethodID mid = getListAddMethodId(env); + if(mid == nullptr) { + // exception occurred accessing class or method + return nullptr; + } + + jstring new_value_str = env->NewStringUTF(c_str); + if(new_value_str == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + jobject jresult_string_builder = + env->CallObjectMethod(jstring_builder, mid, new_value_str); + if(env->ExceptionCheck()) { + // exception occurred + env->DeleteLocalRef(new_value_str); + return nullptr; + } + + return jresult_string_builder; } +}; +// The portal class for org.rocksdb.BackupInfo +class BackupInfoJni : public JavaClass { + public: + /** + * Get the Java Class org.rocksdb.BackupInfo + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "org/rocksdb/BackupInfo"); + } + + /** + * Constructs a BackupInfo object + * + * @param env A pointer to the Java environment + * @param backup_id id of the backup + * @param timestamp timestamp of the backup + * @param size size of the backup + * @param number_files number of files related to the backup + * + * @return A reference to a Java BackupInfo object, or a nullptr if an + * exception occurs + */ static jobject construct0(JNIEnv* env, uint32_t backup_id, int64_t timestamp, uint64_t size, uint32_t number_files) { - static jmethodID mid = env->GetMethodID(getJClass(env), "", - "(IJJI)V"); - assert(mid != nullptr); - return env->NewObject(getJClass(env), mid, - backup_id, timestamp, size, number_files); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = env->GetMethodID(jclazz, "", "(IJJI)V"); + if(mid == nullptr) { + // exception occurred accessing method + return nullptr; + } + + jobject jbackup_info = + env->NewObject(jclazz, mid, backup_id, timestamp, size, number_files); + if(env->ExceptionCheck()) { + return nullptr; + } + + return jbackup_info; } }; class BackupInfoListJni { public: + /** + * Converts a C++ std::vector object to + * a Java ArrayList object + * + * @param env A pointer to the Java environment + * @param backup_infos A vector of BackupInfo + * + * @return Either a reference to a Java ArrayList object, or a nullptr + * if an exception occurs + */ static jobject getBackupInfo(JNIEnv* env, std::vector backup_infos) { - jclass jclazz = env->FindClass("java/util/ArrayList"); - jmethodID mid = rocksdb::ListJni::getArrayListConstructorMethodId( - env, jclazz); - jobject jbackup_info_handle_list = env->NewObject(jclazz, mid, - backup_infos.size()); + jclass jarray_list_clazz = rocksdb::ListJni::getArrayListClass(env); + if(jarray_list_clazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + jmethodID cstr_mid = rocksdb::ListJni::getArrayListConstructorMethodId(env); + if(cstr_mid == nullptr) { + // exception occurred accessing method + return nullptr; + } + + jmethodID add_mid = rocksdb::ListJni::getListAddMethodId(env); + if(add_mid == nullptr) { + // exception occurred accessing method + return nullptr; + } + + // create java list + jobject jbackup_info_handle_list = + env->NewObject(jarray_list_clazz, cstr_mid, backup_infos.size()); + if(env->ExceptionCheck()) { + // exception occured constructing object + return nullptr; + } + // insert in java list - for (std::vector::size_type i = 0; - i != backup_infos.size(); i++) { - rocksdb::BackupInfo backup_info = backup_infos[i]; + auto end = backup_infos.end(); + for (auto it = backup_infos.begin(); it != end; ++it) { + auto backup_info = *it; + jobject obj = rocksdb::BackupInfoJni::construct0(env, backup_info.backup_id, backup_info.timestamp, backup_info.size, backup_info.number_files); - env->CallBooleanMethod(jbackup_info_handle_list, - rocksdb::ListJni::getListAddMethodId(env), obj); + if(env->ExceptionCheck()) { + // exception occured constructing object + if(obj != nullptr) { + env->DeleteLocalRef(obj); + } + if(jbackup_info_handle_list != nullptr) { + env->DeleteLocalRef(jbackup_info_handle_list); + } + return nullptr; + } + + jboolean rs = + env->CallBooleanMethod(jbackup_info_handle_list, add_mid, obj); + if(env->ExceptionCheck() || rs == JNI_FALSE) { + // exception occured calling method, or could not add + if(obj != nullptr) { + env->DeleteLocalRef(obj); + } + if(jbackup_info_handle_list != nullptr) { + env->DeleteLocalRef(jbackup_info_handle_list); + } + return nullptr; + } } + return jbackup_info_handle_list; } }; -class WBWIRocksIteratorJni { +// The portal class for org.rocksdb.WBWIRocksIterator +class WBWIRocksIteratorJni : public JavaClass { public: - // Get the java class id of org.rocksdb.WBWIRocksIterator. - static jclass getJClass(JNIEnv* env) { - static jclass jclazz = env->FindClass("org/rocksdb/WBWIRocksIterator"); - assert(jclazz != nullptr); - return jclazz; + /** + * Get the Java Class org.rocksdb.WBWIRocksIterator + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "org/rocksdb/WBWIRocksIterator"); + } + + /** + * Get the Java Field: WBWIRocksIterator#entry + * + * @param env A pointer to the Java environment + * + * @return The Java Field ID or nullptr if the class or field id could not + * be retieved + */ + static jfieldID getWriteEntryField(JNIEnv* env) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; } - static jfieldID getWriteEntryField(JNIEnv* env) { - static jfieldID fid = - env->GetFieldID(getJClass(env), "entry", - "Lorg/rocksdb/WBWIRocksIterator$WriteEntry;"); - assert(fid != nullptr); - return fid; + static jfieldID fid = + env->GetFieldID(jclazz, "entry", + "Lorg/rocksdb/WBWIRocksIterator$WriteEntry;"); + assert(fid != nullptr); + return fid; + } + + /** + * Gets the value of the WBWIRocksIterator#entry + * + * @param env A pointer to the Java environment + * @param jwbwi_rocks_iterator A reference to a WBWIIterator + * + * @return A reference to a Java WBWIRocksIterator.WriteEntry object, or + * a nullptr if an exception occurs + */ + static jobject getWriteEntry(JNIEnv* env, jobject jwbwi_rocks_iterator) { + assert(jwbwi_rocks_iterator != nullptr); + + jfieldID jwrite_entry_field = getWriteEntryField(env); + if(jwrite_entry_field == nullptr) { + // exception occurred accessing the field + return nullptr; } - static jobject getWriteEntry(JNIEnv* env, jobject jwbwi_rocks_iterator) { - jobject jwe = - env->GetObjectField(jwbwi_rocks_iterator, getWriteEntryField(env)); - assert(jwe != nullptr); - return jwe; - } + jobject jwe = env->GetObjectField(jwbwi_rocks_iterator, jwrite_entry_field); + assert(jwe != nullptr); + return jwe; + } }; -class WriteTypeJni { +// The portal class for org.rocksdb.WBWIRocksIterator.WriteType +class WriteTypeJni : public JavaClass { public: - // Get the PUT enum field of org.rocksdb.WBWIRocksIterator.WriteType + /** + * Get the PUT enum field value of WBWIRocksIterator.WriteType + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject PUT(JNIEnv* env) { return getEnum(env, "PUT"); } - // Get the MERGE enum field of org.rocksdb.WBWIRocksIterator.WriteType + /** + * Get the MERGE enum field value of WBWIRocksIterator.WriteType + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject MERGE(JNIEnv* env) { return getEnum(env, "MERGE"); } - // Get the DELETE enum field of org.rocksdb.WBWIRocksIterator.WriteType + /** + * Get the DELETE enum field value of WBWIRocksIterator.WriteType + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject DELETE(JNIEnv* env) { return getEnum(env, "DELETE"); } - // Get the LOG enum field of org.rocksdb.WBWIRocksIterator.WriteType + /** + * Get the LOG enum field value of WBWIRocksIterator.WriteType + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject LOG(JNIEnv* env) { return getEnum(env, "LOG"); } private: - // Get the java class id of org.rocksdb.WBWIRocksIterator.WriteType. - static jclass getJClass(JNIEnv* env) { - jclass jclazz = env->FindClass("org/rocksdb/WBWIRocksIterator$WriteType"); - assert(jclazz != nullptr); - return jclazz; + /** + * Get the Java Class org.rocksdb.WBWIRocksIterator.WriteType + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "org/rocksdb/WBWIRocksIterator$WriteType"); + } + + /** + * Get an enum field of org.rocksdb.WBWIRocksIterator.WriteType + * + * @param env A pointer to the Java environment + * @param name The name of the enum field + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ + static jobject getEnum(JNIEnv* env, const char name[]) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; } - // Get an enum field of org.rocksdb.WBWIRocksIterator.WriteType - static jobject getEnum(JNIEnv* env, const char name[]) { - jclass jclazz = getJClass(env); - jfieldID jfid = - env->GetStaticFieldID(jclazz, name, - "Lorg/rocksdb/WBWIRocksIterator$WriteType;"); - assert(jfid != nullptr); - return env->GetStaticObjectField(jclazz, jfid); + jfieldID jfid = + env->GetStaticFieldID(jclazz, name, + "Lorg/rocksdb/WBWIRocksIterator$WriteType;"); + if(env->ExceptionCheck()) { + // exception occured while getting field + return nullptr; + } else if(jfid == nullptr) { + return nullptr; } + + jobject jwrite_type = env->GetStaticObjectField(jclazz, jfid); + assert(jwrite_type != nullptr); + return jwrite_type; + } }; -class WriteEntryJni { +// The portal class for org.rocksdb.WBWIRocksIterator.WriteEntry +class WriteEntryJni : public JavaClass { public: - // Get the java class id of org.rocksdb.WBWIRocksIterator.WriteEntry. + /** + * Get the Java Class org.rocksdb.WBWIRocksIterator.WriteEntry + * + * @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) { - static jclass jclazz = - env->FindClass("org/rocksdb/WBWIRocksIterator$WriteEntry"); - assert(jclazz != nullptr); - return jclazz; + return JavaClass::getJClass(env, "org/rocksdb/WBWIRocksIterator$WriteEntry"); } }; -class InfoLogLevelJni { +// The portal class for org.rocksdb.InfoLogLevel +class InfoLogLevelJni : public JavaClass { public: - // Get the DEBUG_LEVEL enum field of org.rocksdb.InfoLogLevel + /** + * Get the DEBUG_LEVEL enum field value of InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject DEBUG_LEVEL(JNIEnv* env) { return getEnum(env, "DEBUG_LEVEL"); } - // Get the INFO_LEVEL enum field of org.rocksdb.InfoLogLevel + /** + * Get the INFO_LEVEL enum field value of InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject INFO_LEVEL(JNIEnv* env) { return getEnum(env, "INFO_LEVEL"); } - // Get the WARN_LEVEL enum field of org.rocksdb.InfoLogLevel + /** + * Get the WARN_LEVEL enum field value of InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject WARN_LEVEL(JNIEnv* env) { return getEnum(env, "WARN_LEVEL"); } - // Get the ERROR_LEVEL enum field of org.rocksdb.InfoLogLevel + /** + * Get the ERROR_LEVEL enum field value of InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject ERROR_LEVEL(JNIEnv* env) { return getEnum(env, "ERROR_LEVEL"); } - // Get the FATAL_LEVEL enum field of org.rocksdb.InfoLogLevel + /** + * Get the FATAL_LEVEL enum field value of InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject FATAL_LEVEL(JNIEnv* env) { return getEnum(env, "FATAL_LEVEL"); } - // Get the HEADER_LEVEL enum field of org.rocksdb.InfoLogLevel + /** + * Get the HEADER_LEVEL enum field value of InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ static jobject HEADER_LEVEL(JNIEnv* env) { return getEnum(env, "HEADER_LEVEL"); } private: - // Get the java class id of org.rocksdb.InfoLogLevel - static jclass getJClass(JNIEnv* env) { - jclass jclazz = env->FindClass("org/rocksdb/InfoLogLevel"); - assert(jclazz != nullptr); - return jclazz; + /** + * Get the Java Class org.rocksdb.InfoLogLevel + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, "org/rocksdb/InfoLogLevel"); + } + + /** + * Get an enum field of org.rocksdb.InfoLogLevel + * + * @param env A pointer to the Java environment + * @param name The name of the enum field + * + * @return A reference to the enum field value or a nullptr if + * the enum field value could not be retrieved + */ + static jobject getEnum(JNIEnv* env, const char name[]) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; } - // Get an enum field of org.rocksdb.InfoLogLevel - static jobject getEnum(JNIEnv* env, const char name[]) { - jclass jclazz = getJClass(env); - jfieldID jfid = - env->GetStaticFieldID(jclazz, name, - "Lorg/rocksdb/InfoLogLevel;"); - assert(jfid != nullptr); - return env->GetStaticObjectField(jclazz, jfid); + jfieldID jfid = + env->GetStaticFieldID(jclazz, name, "Lorg/rocksdb/InfoLogLevel;"); + if(env->ExceptionCheck()) { + // exception occured while getting field + return nullptr; + } else if(jfid == nullptr) { + return nullptr; } + + jobject jinfo_log_level = env->GetStaticObjectField(jclazz, jfid); + assert(jinfo_log_level != nullptr); + return jinfo_log_level; + } }; // The portal class for org.rocksdb.Logger class LoggerJni : public RocksDBNativeClass< std::shared_ptr*, LoggerJni> { public: + /** + * Get the Java Class org/rocksdb/Logger + * + * @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/Logger"); + return RocksDBNativeClass::getJClass(env, "org/rocksdb/Logger"); } - // Get the java method `name` of org.rocksdb.Logger. + /** + * Get the Java Method: Logger#log + * + * @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 getLogMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getJClass(env), "log", - "(Lorg/rocksdb/InfoLogLevel;Ljava/lang/String;)V"); + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + static jmethodID mid = + env->GetMethodID(jclazz, "log", + "(Lorg/rocksdb/InfoLogLevel;Ljava/lang/String;)V"); assert(mid != nullptr); return mid; } }; +// The portal class for org.rocksdb.TransactionLogIterator.BatchResult +class BatchResultJni : public JavaClass { + public: + /** + * Get the Java Class org.rocksdb.TransactionLogIterator.BatchResult + * + * @param env A pointer to the Java environment + * + * @return The Java Class or nullptr if one of the + * ClassFormatError, ClassCircularityError, NoClassDefFoundError, + * OutOfMemoryError or ExceptionInInitializerError exceptions is thrown + */ + static jclass getJClass(JNIEnv* env) { + return JavaClass::getJClass(env, + "org/rocksdb/TransactionLogIterator$BatchResult"); + } + + /** + * Create a new Java org.rocksdb.TransactionLogIterator.BatchResult object + * with the same properties as the provided C++ rocksdb::BatchResult object + * + * @param env A pointer to the Java environment + * @param batch_result The rocksdb::BatchResult object + * + * @return A reference to a Java + * org.rocksdb.TransactionLogIterator.BatchResult object, + * or nullptr if an an exception occurs + */ + static jobject construct(JNIEnv* env, + rocksdb::BatchResult& batch_result) { + jclass jclazz = getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + + jmethodID mid = env->GetMethodID( + jclazz, "", "(JJ)V"); + if(mid == nullptr) { + // exception thrown: NoSuchMethodException or OutOfMemoryError + return nullptr; + } + + jobject jbatch_result = env->NewObject(jclazz, mid, + batch_result.sequence, batch_result.writeBatchPtr.get()); + if(jbatch_result == nullptr) { + // exception thrown: InstantiationException or OutOfMemoryError + return nullptr; + } + + batch_result.writeBatchPtr.release(); + return jbatch_result; + } +}; + +// various utility functions for working with RocksDB and JNI class JniUtil { public: - /* + /** + * Obtains a reference to the JNIEnv from + * the JVM + * + * If the current thread is not attached to the JavaVM + * then it will be attached so as to retrieve the JNIEnv + * + * If a thread is attached, it must later be manually + * released by calling JavaVM::DetachCurrentThread. + * This can be handled by always matching calls to this + * function with calls to {@link JniUtil::releaseJniEnv(JavaVM*, jboolean)} + * + * @param jvm (IN) A pointer to the JavaVM instance + * @param attached (OUT) A pointer to a boolean which + * will be set to JNI_TRUE if we had to attach the thread + * + * @return A pointer to the JNIEnv or nullptr if a fatal error + * occurs and the JNIEnv cannot be retrieved + */ + static JNIEnv* getJniEnv(JavaVM* jvm, jboolean* attached) { + assert(jvm != nullptr); + + JNIEnv *env; + const jint env_rs = jvm->GetEnv(reinterpret_cast(&env), + JNI_VERSION_1_2); + + if(env_rs == JNI_OK) { + // current thread is already attached, return the JNIEnv + *attached = JNI_FALSE; + return env; + } else if(env_rs == JNI_EDETACHED) { + // current thread is not attached, attempt to attach + const jint rs_attach = jvm->AttachCurrentThread(reinterpret_cast(&env), NULL); + if(rs_attach == JNI_OK) { + *attached = JNI_TRUE; + return env; + } else { + // error, could not attach the thread + std::cerr << "JniUtil::getJinEnv - Fatal: could not attach current thread to JVM!" << std::endl; + return nullptr; + } + } else if(env_rs == JNI_EVERSION) { + // error, JDK does not support JNI_VERSION_1_2+ + std::cerr << "JniUtil::getJinEnv - Fatal: JDK does not support JNI_VERSION_1_2" << std::endl; + return nullptr; + } else { + std::cerr << "JniUtil::getJinEnv - Fatal: Unknown error: env_rs=" << env_rs << std::endl; + return nullptr; + } + } + + /** + * Counterpart to {@link JniUtil::getJniEnv(JavaVM*, jboolean*)} + * + * Detachess the current thread from the JVM if it was previously + * attached + * + * @param jvm (IN) A pointer to the JavaVM instance + * @param attached (IN) JNI_TRUE if we previously had to attach the thread + * to the JavaVM to get the JNIEnv + */ + static void releaseJniEnv(JavaVM* jvm, jboolean& attached) { + assert(jvm != nullptr); + if(attached == JNI_TRUE) { + const jint rs_detach = jvm->DetachCurrentThread(); + assert(rs_detach == JNI_OK); + if(rs_detach != JNI_OK) { + std::cerr << "JniUtil::getJinEnv - Warn: Unable to detach current thread from JVM!" << std::endl; + } + } + } + + /** + * Copies a Java String[] to a C++ std::vector + * + * @param env (IN) A pointer to the java environment + * @param jss (IN) The Java String array to copy + * @param has_exception (OUT) will be set to JNI_TRUE + * if an OutOfMemoryError or ArrayIndexOutOfBoundsException + * exception occurs + * + * @return A std::vector containing copies of the Java strings + */ + static std::vector copyStrings(JNIEnv* env, + jobjectArray jss, jboolean* has_exception) { + return rocksdb::JniUtil::copyStrings(env, jss, + env->GetArrayLength(jss), has_exception); + } + + /** + * Copies a Java String[] to a C++ std::vector + * + * @param env (IN) A pointer to the java environment + * @param jss (IN) The Java String array to copy + * @param jss_len (IN) The length of the Java String array to copy + * @param has_exception (OUT) will be set to JNI_TRUE + * if an OutOfMemoryError or ArrayIndexOutOfBoundsException + * exception occurs + * + * @return A std::vector containing copies of the Java strings + */ + static std::vector copyStrings(JNIEnv* env, + jobjectArray jss, const jsize jss_len, jboolean* has_exception) { + std::vector strs; + for (jsize i = 0; i < jss_len; i++) { + jobject js = env->GetObjectArrayElement(jss, i); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + *has_exception = JNI_TRUE; + return strs; + } + + jstring jstr = static_cast(js); + const char* str = env->GetStringUTFChars(jstr, nullptr); + if(str == nullptr) { + // exception thrown: OutOfMemoryError + env->DeleteLocalRef(js); + *has_exception = JNI_TRUE; + return strs; + } + + strs.push_back(std::string(str)); + + env->ReleaseStringUTFChars(jstr, str); + env->DeleteLocalRef(js); + } + + *has_exception = JNI_FALSE; + return strs; + } + + /** * Copies a jstring to a std::string * and releases the original jstring + * + * If an exception occurs, then JNIEnv::ExceptionCheck() + * will have been called + * + * @param env (IN) A pointer to the java environment + * @param js (IN) The java string to copy + * @param has_exception (OUT) will be set to JNI_TRUE + * if an OutOfMemoryError exception occurs + * + * @return A std:string copy of the jstring, or an + * empty std::string if has_exception == JNI_TRUE */ - static std::string copyString(JNIEnv* env, jstring js) { - const char *utf = env->GetStringUTFChars(js, NULL); + static std::string 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 std::string(); + } else if(env->ExceptionCheck()) { + // exception thrown + env->ReleaseStringUTFChars(js, utf); + *has_exception = JNI_TRUE; + return std::string(); + } + std::string name(utf); env->ReleaseStringUTFChars(js, utf); + *has_exception = JNI_FALSE; return name; } + /** + * Copies bytes from a std::string to a jByteArray + * + * @param env A pointer to the java environment + * @param bytes The bytes to copy + * + * @return the Java byte[] or nullptr if an exception occurs + */ + static jbyteArray copyBytes(JNIEnv* env, std::string bytes) { + const jsize jlen = static_cast(bytes.size()); + + jbyteArray jbytes = env->NewByteArray(jlen); + if(jbytes == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + env->SetByteArrayRegion(jbytes, 0, jlen, + reinterpret_cast(bytes.c_str())); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jbytes); + return nullptr; + } + + return jbytes; + } + + /** + * Given a Java byte[][] which is an array of java.lang.Strings + * where each String is a byte[], the passed function `string_fn` + * will be called on each String, the result is the collected by + * calling the passed function `collector_fn` + * + * @param env (IN) A pointer to the java environment + * @param jbyte_strings (IN) A Java array of Strings expressed as bytes + * @param string_fn (IN) A transform function to call for each String + * @param collector_fn (IN) A collector which is called for the result + * of each `string_fn` + * @param has_exception (OUT) will be set to JNI_TRUE + * if an ArrayIndexOutOfBoundsException or OutOfMemoryError + * exception occurs + */ + template static void byteStrings(JNIEnv* env, + jobjectArray jbyte_strings, + std::function string_fn, + std::function collector_fn, + jboolean *has_exception) { + const jsize jlen = env->GetArrayLength(jbyte_strings); + + for(jsize i = 0; i < jlen; i++) { + jobject jbyte_string_obj = env->GetObjectArrayElement(jbyte_strings, i); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + *has_exception = JNI_TRUE; // signal error + return; + } + + jbyteArray jbyte_string_ary = + reinterpret_cast(jbyte_string_obj); + T result = byteString(env, jbyte_string_ary, string_fn, has_exception); + + env->DeleteLocalRef(jbyte_string_obj); + + if(*has_exception == JNI_TRUE) { + // exception thrown: OutOfMemoryError + return; + } + + collector_fn(i, result); + } + + *has_exception = JNI_FALSE; + } + + /** + * Given a Java String which is expressed as a Java Byte Array byte[], + * the passed function `string_fn` will be called on the String + * and the result returned + * + * @param env (IN) A pointer to the java environment + * @param jbyte_string_ary (IN) A Java String expressed in bytes + * @param string_fn (IN) A transform function to call on the String + * @param has_exception (OUT) will be set to JNI_TRUE + * if an OutOfMemoryError exception occurs + */ + template static T byteString(JNIEnv* env, + jbyteArray jbyte_string_ary, + std::function string_fn, + jboolean* has_exception) { + const jsize jbyte_string_len = env->GetArrayLength(jbyte_string_ary); + jbyte* jbyte_string = + env->GetByteArrayElements(jbyte_string_ary, nullptr); + if(jbyte_string == nullptr) { + // exception thrown: OutOfMemoryError + *has_exception = JNI_TRUE; + return nullptr; // signal error + } + + T result = + string_fn(reinterpret_cast(jbyte_string), jbyte_string_len); + + env->ReleaseByteArrayElements(jbyte_string_ary, jbyte_string, JNI_ABORT); + + *has_exception = JNI_FALSE; + return result; + } + + /** + * Converts a std::vector to a Java byte[][] where each Java String + * is expressed as a Java Byte Array byte[]. + * + * @param env A pointer to the java environment + * @param strings A vector of Strings + * + * @return A Java array of Strings expressed as bytes + */ + static jobjectArray stringsBytes(JNIEnv* env, std::vector strings) { + jclass jcls_ba = ByteJni::getArrayJClass(env); + if(jcls_ba == nullptr) { + // exception occurred + return nullptr; + } + + const jsize len = static_cast(strings.size()); + + jobjectArray jbyte_strings = env->NewObjectArray(len, jcls_ba, nullptr); + if(jbyte_strings == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + for (jsize i = 0; i < len; i++) { + std::string *str = &strings[i]; + const jsize str_len = static_cast(str->size()); + + jbyteArray jbyte_string_ary = env->NewByteArray(str_len); + if(jbyte_string_ary == nullptr) { + // exception thrown: OutOfMemoryError + env->DeleteLocalRef(jbyte_strings); + return nullptr; + } + + env->SetByteArrayRegion( + jbyte_string_ary, 0, str_len, + reinterpret_cast(str->c_str())); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jbyte_string_ary); + env->DeleteLocalRef(jbyte_strings); + return nullptr; + } + + env->SetObjectArrayElement(jbyte_strings, i, jbyte_string_ary); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + // or ArrayStoreException + env->DeleteLocalRef(jbyte_string_ary); + env->DeleteLocalRef(jbyte_strings); + return nullptr; + } + + env->DeleteLocalRef(jbyte_string_ary); + } + + return jbyte_strings; + } + /* * Helper for operations on a key and value * for example WriteBatch->Put @@ -872,15 +2400,32 @@ class JniUtil { jbyteArray jkey, jint jkey_len, jbyteArray jentry_value, jint jentry_value_len) { jbyte* key = env->GetByteArrayElements(jkey, nullptr); + if(env->ExceptionCheck()) { + // exception thrown: OutOfMemoryError + return; + } + jbyte* value = env->GetByteArrayElements(jentry_value, nullptr); + if(env->ExceptionCheck()) { + // exception thrown: OutOfMemoryError + if(key != nullptr) { + env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); + } + return; + } + rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); rocksdb::Slice value_slice(reinterpret_cast(value), jentry_value_len); op(key_slice, value_slice); - env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); - env->ReleaseByteArrayElements(jentry_value, value, JNI_ABORT); + if(value != nullptr) { + env->ReleaseByteArrayElements(jentry_value, value, JNI_ABORT); + } + if(key != nullptr) { + env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); + } } /* @@ -895,11 +2440,18 @@ class JniUtil { JNIEnv* env, jobject jobj, jbyteArray jkey, jint jkey_len) { jbyte* key = env->GetByteArrayElements(jkey, nullptr); + if(env->ExceptionCheck()) { + // exception thrown: OutOfMemoryError + return; + } + rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); op(key_slice); - env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); + if(key != nullptr) { + env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); + } } /* @@ -909,14 +2461,20 @@ class JniUtil { static jbyteArray v_op( std::function op, JNIEnv* env, jbyteArray jkey, jint jkey_len) { - jboolean isCopy; - jbyte* key = env->GetByteArrayElements(jkey, &isCopy); + jbyte* key = env->GetByteArrayElements(jkey, nullptr); + if(env->ExceptionCheck()) { + // exception thrown: OutOfMemoryError + return nullptr; + } + rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); std::string value; rocksdb::Status s = op(key_slice, &value); - env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); + if(key != nullptr) { + env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); + } if (s.IsNotFound()) { return nullptr; @@ -925,12 +2483,25 @@ class JniUtil { if (s.ok()) { jbyteArray jret_value = env->NewByteArray(static_cast(value.size())); + if(jret_value == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + env->SetByteArrayRegion(jret_value, 0, static_cast(value.size()), reinterpret_cast(value.c_str())); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + if(jret_value != nullptr) { + env->DeleteLocalRef(jret_value); + } + return nullptr; + } + return jret_value; } - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); return nullptr; } }; diff --git a/java/rocksjni/ratelimiterjni.cc b/java/rocksjni/ratelimiterjni.cc index 0b76a8b29..99d4f30fd 100644 --- a/java/rocksjni/ratelimiterjni.cc +++ b/java/rocksjni/ratelimiterjni.cc @@ -6,24 +6,9 @@ // This file implements the "bridge" between Java and C++ for RateLimiter. #include "rocksjni/portal.h" -#include "include/org_rocksdb_GenericRateLimiterConfig.h" #include "include/org_rocksdb_RateLimiter.h" #include "rocksdb/rate_limiter.h" -/* - * Class: org_rocksdb_GenericRateLimiterConfig - * Method: newRateLimiterHandle - * Signature: (JJI)J - */ -jlong Java_org_rocksdb_GenericRateLimiterConfig_newRateLimiterHandle( - JNIEnv* env, jobject jobj, jlong jrate_bytes_per_second, - jlong jrefill_period_micros, jint jfairness) { - return reinterpret_cast(rocksdb::NewGenericRateLimiter( - static_cast(jrate_bytes_per_second), - static_cast(jrefill_period_micros), - static_cast(jfairness))); -} - /* * Class: org_rocksdb_RateLimiter * Method: newRateLimiterHandle @@ -32,16 +17,13 @@ jlong Java_org_rocksdb_GenericRateLimiterConfig_newRateLimiterHandle( jlong Java_org_rocksdb_RateLimiter_newRateLimiterHandle( JNIEnv* env, jclass jclazz, jlong jrate_bytes_per_second, jlong jrefill_period_micros, jint jfairness) { - auto* rate_limiter = rocksdb::NewGenericRateLimiter( - static_cast(jrate_bytes_per_second), - static_cast(jrefill_period_micros), - static_cast(jfairness)); + auto * sptr_rate_limiter = + new std::shared_ptr(rocksdb::NewGenericRateLimiter( + static_cast(jrate_bytes_per_second), + static_cast(jrefill_period_micros), + static_cast(jfairness))); - std::shared_ptr *ptr_sptr_rate_limiter = - new std::shared_ptr; - *ptr_sptr_rate_limiter = std::shared_ptr(rate_limiter); - - return reinterpret_cast(ptr_sptr_rate_limiter); + return reinterpret_cast(sptr_rate_limiter); } /* @@ -51,10 +33,9 @@ jlong Java_org_rocksdb_RateLimiter_newRateLimiterHandle( */ void Java_org_rocksdb_RateLimiter_disposeInternal( JNIEnv* env, jobject jobj, jlong jhandle) { - std::shared_ptr *handle = + auto* handle = reinterpret_cast *>(jhandle); - handle->reset(); - delete handle; + delete handle; // delete std::shared_ptr } /* @@ -65,8 +46,8 @@ void Java_org_rocksdb_RateLimiter_disposeInternal( void Java_org_rocksdb_RateLimiter_setBytesPerSecond( JNIEnv* env, jobject jobj, jlong handle, jlong jbytes_per_second) { - reinterpret_cast( - handle)->SetBytesPerSecond(jbytes_per_second); + reinterpret_cast *>(handle)->get()-> + SetBytesPerSecond(jbytes_per_second); } /* @@ -77,9 +58,8 @@ void Java_org_rocksdb_RateLimiter_setBytesPerSecond( void Java_org_rocksdb_RateLimiter_request( JNIEnv* env, jobject jobj, jlong handle, jlong jbytes) { - reinterpret_cast( - handle)->Request(jbytes, - rocksdb::Env::IO_TOTAL); + reinterpret_cast *>(handle)->get()-> + Request(jbytes, rocksdb::Env::IO_TOTAL); } /* @@ -88,10 +68,9 @@ void Java_org_rocksdb_RateLimiter_request( * Signature: (J)J */ jlong Java_org_rocksdb_RateLimiter_getSingleBurstBytes( - JNIEnv* env, jobject jobj, jlong handle, - jlong jbytes) { - return reinterpret_cast( - handle)->GetSingleBurstBytes(); + JNIEnv* env, jobject jobj, jlong handle) { + return reinterpret_cast *>(handle)-> + get()->GetSingleBurstBytes(); } /* @@ -100,10 +79,9 @@ jlong Java_org_rocksdb_RateLimiter_getSingleBurstBytes( * Signature: (J)J */ jlong Java_org_rocksdb_RateLimiter_getTotalBytesThrough( - JNIEnv* env, jobject jobj, jlong handle, - jlong jbytes) { - return reinterpret_cast( - handle)->GetTotalBytesThrough(); + JNIEnv* env, jobject jobj, jlong handle) { + return reinterpret_cast *>(handle)-> + get()->GetTotalBytesThrough(); } /* @@ -112,8 +90,7 @@ jlong Java_org_rocksdb_RateLimiter_getTotalBytesThrough( * Signature: (J)J */ jlong Java_org_rocksdb_RateLimiter_getTotalRequests( - JNIEnv* env, jobject jobj, jlong handle, - jlong jbytes) { - return reinterpret_cast( - handle)->GetTotalRequests(); + JNIEnv* env, jobject jobj, jlong handle) { + return reinterpret_cast *>(handle)-> + get()->GetTotalRequests(); } diff --git a/java/rocksjni/restorejni.cc b/java/rocksjni/restorejni.cc index 154a9b5f1..d8ec147ea 100644 --- a/java/rocksjni/restorejni.cc +++ b/java/rocksjni/restorejni.cc @@ -22,7 +22,7 @@ */ jlong Java_org_rocksdb_RestoreOptions_newRestoreOptions(JNIEnv* env, jclass jcls, jboolean keep_log_files) { - auto ropt = new rocksdb::RestoreOptions(keep_log_files); + auto* ropt = new rocksdb::RestoreOptions(keep_log_files); return reinterpret_cast(ropt); } @@ -33,7 +33,7 @@ jlong Java_org_rocksdb_RestoreOptions_newRestoreOptions(JNIEnv* env, */ void Java_org_rocksdb_RestoreOptions_disposeInternal(JNIEnv* env, jobject jobj, jlong jhandle) { - auto ropt = reinterpret_cast(jhandle); + auto* ropt = reinterpret_cast(jhandle); assert(ropt); delete ropt; } diff --git a/java/rocksjni/rocksjni.cc b/java/rocksjni/rocksjni.cc index 4c5d2b8ae..9ecdbb581 100644 --- a/java/rocksjni/rocksjni.cc +++ b/java/rocksjni/rocksjni.cc @@ -33,10 +33,16 @@ jlong rocksdb_open_helper(JNIEnv* env, jlong jopt_handle, jstring jdb_path, std::function open_fn ) { + const char* db_path = env->GetStringUTFChars(jdb_path, nullptr); + if(db_path == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + auto* opt = reinterpret_cast(jopt_handle); rocksdb::DB* db = nullptr; - const char* db_path = env->GetStringUTFChars(jdb_path, NULL); rocksdb::Status s = open_fn(*opt, db_path, &db); + env->ReleaseStringUTFChars(jdb_path, db_path); if (s.ok()) { @@ -83,40 +89,56 @@ jlongArray rocksdb_open_helper(JNIEnv* env, jlong jopt_handle, std::vector*, rocksdb::DB**)> open_fn ) { - auto* opt = reinterpret_cast(jopt_handle); - const char* db_path = env->GetStringUTFChars(jdb_path, NULL); - - std::vector column_families; + const char* db_path = env->GetStringUTFChars(jdb_path, nullptr); + if(db_path == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } - jsize len_cols = env->GetArrayLength(jcolumn_names); - jlong* jco = env->GetLongArrayElements(jcolumn_options, NULL); - for(int i = 0; i < len_cols; i++) { - jobject jcn = env->GetObjectArrayElement(jcolumn_names, i); - jbyteArray jcn_ba = reinterpret_cast(jcn); - jbyte* jcf_name = env->GetByteArrayElements(jcn_ba, NULL); - const int jcf_name_len = env->GetArrayLength(jcn_ba); + const jsize len_cols = env->GetArrayLength(jcolumn_names); + jlong* jco = env->GetLongArrayElements(jcolumn_options, nullptr); + if(jco == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jdb_path, db_path); + return nullptr; + } - //TODO(AR) do I need to make a copy of jco[i] ? + std::vector column_families; + jboolean has_exception = JNI_FALSE; + rocksdb::JniUtil::byteStrings( + env, + jcolumn_names, + [](const char* str_data, const size_t str_len) { + return std::string(str_data, str_len); + }, + [&jco, &column_families](size_t idx, std::string cf_name) { + rocksdb::ColumnFamilyOptions* cf_options = + reinterpret_cast(jco[idx]); + column_families.push_back( + rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options)); + }, + &has_exception); - std::string cf_name (reinterpret_cast(jcf_name), jcf_name_len); - rocksdb::ColumnFamilyOptions* cf_options = - reinterpret_cast(jco[i]); - column_families.push_back( - rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options)); + env->ReleaseLongArrayElements(jcolumn_options, jco, JNI_ABORT); - env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT); - env->DeleteLocalRef(jcn); + if(has_exception == JNI_TRUE) { + // exception occured + env->ReleaseStringUTFChars(jdb_path, db_path); + return nullptr; } - env->ReleaseLongArrayElements(jcolumn_options, jco, JNI_ABORT); + auto* opt = reinterpret_cast(jopt_handle); std::vector handles; rocksdb::DB* db = nullptr; rocksdb::Status s = open_fn(*opt, db_path, column_families, &handles, &db); + // we have now finished with db_path + env->ReleaseStringUTFChars(jdb_path, db_path); + // check if open operation was successful if (s.ok()) { - jsize resultsLen = 1 + len_cols; //db handle + column family handles + const jsize resultsLen = 1 + len_cols; //db handle + column family handles std::unique_ptr results = std::unique_ptr(new jlong[resultsLen]); results[0] = reinterpret_cast(db); @@ -125,11 +147,22 @@ jlongArray rocksdb_open_helper(JNIEnv* env, jlong jopt_handle, } jlongArray jresults = env->NewLongArray(resultsLen); + if(jresults == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + env->SetLongArrayRegion(jresults, 0, resultsLen, results.get()); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jresults); + return nullptr; + } + return jresults; } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, s); - return NULL; + return nullptr; } } @@ -179,43 +212,51 @@ jlongArray Java_org_rocksdb_RocksDB_open__JLjava_lang_String_2_3_3B_3J( jobjectArray Java_org_rocksdb_RocksDB_listColumnFamilies( JNIEnv* env, jclass jclazz, jlong jopt_handle, jstring jdb_path) { std::vector column_family_names; + const char* db_path = env->GetStringUTFChars(jdb_path, nullptr); + if(db_path == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + auto* opt = reinterpret_cast(jopt_handle); - const char* db_path = env->GetStringUTFChars(jdb_path, 0); rocksdb::Status s = rocksdb::DB::ListColumnFamilies(*opt, db_path, &column_family_names); + env->ReleaseStringUTFChars(jdb_path, db_path); - jclass jcls_ba = env->FindClass("[B"); - jobjectArray jresults = env->NewObjectArray( - static_cast(column_family_names.size()), jcls_ba, NULL); - if (s.ok()) { - for (std::vector::size_type i = 0; - i < column_family_names.size(); i++) { - jbyteArray jcf_value = - env->NewByteArray(static_cast(column_family_names[i].size())); - env->SetByteArrayRegion( - jcf_value, 0, static_cast(column_family_names[i].size()), - reinterpret_cast(column_family_names[i].data())); - env->SetObjectArrayElement(jresults, static_cast(i), jcf_value); - env->DeleteLocalRef(jcf_value); - } - } - return jresults; + jobjectArray jcolumn_family_names = + rocksdb::JniUtil::stringsBytes(env, column_family_names); + + return jcolumn_family_names; } ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::Put -void rocksdb_put_helper(JNIEnv* env, rocksdb::DB* db, +/** + * @return true if the put succeeded, false if a Java Exception was thrown + */ +bool rocksdb_put_helper(JNIEnv* env, rocksdb::DB* db, const rocksdb::WriteOptions& write_options, rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { jbyte* key = new jbyte[jkey_len]; env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] key; + return false; + } jbyte* value = new jbyte[jval_len]; env->GetByteArrayRegion(jval, jval_off, jval_len, value); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] value; + delete [] key; + return false; + } rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); rocksdb::Slice value_slice(reinterpret_cast(value), jval_len); @@ -233,9 +274,11 @@ void rocksdb_put_helper(JNIEnv* env, rocksdb::DB* db, delete [] key; if (s.ok()) { - return; + return true; + } else { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + return false; } - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } /* @@ -248,7 +291,7 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BII(JNIEnv* env, jobject jdb, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); @@ -267,10 +310,10 @@ void Java_org_rocksdb_RocksDB_put__J_3BII_3BIIJ(JNIEnv* env, jobject jdb, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_put_helper(env, db, default_write_options, cf_handle, jkey, jkey_off, jkey_len, jval, jval_off, jval_len); @@ -291,8 +334,8 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BII(JNIEnv* env, jobject jdb, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast( + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = reinterpret_cast( jwrite_options_handle); rocksdb_put_helper(env, db, *write_options, nullptr, jkey, jkey_off, jkey_len, @@ -308,10 +351,10 @@ void Java_org_rocksdb_RocksDB_put__JJ_3BII_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast( + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = reinterpret_cast( jwrite_options_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_put_helper(env, db, *write_options, cf_handle, jkey, jkey_off, jkey_len, jval, jval_off, jval_len); @@ -369,12 +412,21 @@ void Java_org_rocksdb_RocksDB_write1( jboolean key_may_exist_helper(JNIEnv* env, rocksdb::DB* db, const rocksdb::ReadOptions& read_opt, rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_off, - jint jkey_len, jobject jstring_buffer) { - std::string value; - bool value_found = false; + jint jkey_len, jobject jstring_builder, bool* has_exception) { + jbyte* key = new jbyte[jkey_len]; env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] key; + *has_exception = true; + return false; + } + rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); + + std::string value; + bool value_found = false; bool keyMayExist; if (cf_handle != nullptr) { keyMayExist = db->KeyMayExist(read_opt, cf_handle, key_slice, @@ -389,42 +441,48 @@ jboolean key_may_exist_helper(JNIEnv* env, rocksdb::DB* db, // extract the value if (value_found && !value.empty()) { - jclass clazz = env->GetObjectClass(jstring_buffer); - jmethodID mid = env->GetMethodID(clazz, "append", - "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); - jstring new_value_str = env->NewStringUTF(value.c_str()); - env->CallObjectMethod(jstring_buffer, mid, new_value_str); + jobject jresult_string_builder = + rocksdb::StringBuilderJni::append(env, jstring_builder, + value.c_str()); + if(jresult_string_builder == nullptr) { + *has_exception = true; + return false; + } } + + *has_exception = false; return static_cast(keyMayExist); } /* * Class: org_rocksdb_RocksDB * Method: keyMayExist - * Signature: (J[BIILjava/lang/StringBuffer;)Z + * Signature: (J[BIILjava/lang/StringBuilder;)Z */ -jboolean Java_org_rocksdb_RocksDB_keyMayExist__J_3BIILjava_lang_StringBuffer_2( +jboolean Java_org_rocksdb_RocksDB_keyMayExist__J_3BIILjava_lang_StringBuilder_2( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_off, - jint jkey_len, jobject jstring_buffer) { + jint jkey_len, jobject jstring_builder) { auto* db = reinterpret_cast(jdb_handle); + bool has_exception = false; return key_may_exist_helper(env, db, rocksdb::ReadOptions(), - nullptr, jkey, jkey_off, jkey_len, jstring_buffer); + nullptr, jkey, jkey_off, jkey_len, jstring_builder, &has_exception); } /* * Class: org_rocksdb_RocksDB * Method: keyMayExist - * Signature: (J[BIIJLjava/lang/StringBuffer;)Z + * Signature: (J[BIIJLjava/lang/StringBuilder;)Z */ -jboolean Java_org_rocksdb_RocksDB_keyMayExist__J_3BIIJLjava_lang_StringBuffer_2( +jboolean Java_org_rocksdb_RocksDB_keyMayExist__J_3BIIJLjava_lang_StringBuilder_2( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_off, - jint jkey_len, jlong jcf_handle, jobject jstring_buffer) { + jint jkey_len, jlong jcf_handle, jobject jstring_builder) { auto* db = reinterpret_cast(jdb_handle); auto* cf_handle = reinterpret_cast( jcf_handle); if (cf_handle != nullptr) { + bool has_exception = false; return key_may_exist_helper(env, db, rocksdb::ReadOptions(), - cf_handle, jkey, jkey_off, jkey_len, jstring_buffer); + cf_handle, jkey, jkey_off, jkey_len, jstring_builder, &has_exception); } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); @@ -435,35 +493,37 @@ jboolean Java_org_rocksdb_RocksDB_keyMayExist__J_3BIIJLjava_lang_StringBuffer_2( /* * Class: org_rocksdb_RocksDB * Method: keyMayExist - * Signature: (JJ[BIILjava/lang/StringBuffer;)Z + * Signature: (JJ[BIILjava/lang/StringBuilder;)Z */ -jboolean Java_org_rocksdb_RocksDB_keyMayExist__JJ_3BIILjava_lang_StringBuffer_2( +jboolean Java_org_rocksdb_RocksDB_keyMayExist__JJ_3BIILjava_lang_StringBuilder_2( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jread_options_handle, - jbyteArray jkey, jint jkey_off, jint jkey_len, jobject jstring_buffer) { + jbyteArray jkey, jint jkey_off, jint jkey_len, jobject jstring_builder) { auto* db = reinterpret_cast(jdb_handle); auto& read_options = *reinterpret_cast( jread_options_handle); + bool has_exception = false; return key_may_exist_helper(env, db, read_options, - nullptr, jkey, jkey_off, jkey_len, jstring_buffer); + nullptr, jkey, jkey_off, jkey_len, jstring_builder, &has_exception); } /* * Class: org_rocksdb_RocksDB * Method: keyMayExist - * Signature: (JJ[BIIJLjava/lang/StringBuffer;)Z + * Signature: (JJ[BIIJLjava/lang/StringBuilder;)Z */ -jboolean Java_org_rocksdb_RocksDB_keyMayExist__JJ_3BIIJLjava_lang_StringBuffer_2( +jboolean Java_org_rocksdb_RocksDB_keyMayExist__JJ_3BIIJLjava_lang_StringBuilder_2( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jread_options_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jlong jcf_handle, - jobject jstring_buffer) { + jobject jstring_builder) { auto* db = reinterpret_cast(jdb_handle); auto& read_options = *reinterpret_cast( jread_options_handle); auto* cf_handle = reinterpret_cast( jcf_handle); if (cf_handle != nullptr) { + bool has_exception = false; return key_may_exist_helper(env, db, read_options, cf_handle, - jkey, jkey_off, jkey_len, jstring_buffer); + jkey, jkey_off, jkey_len, jstring_builder, &has_exception); } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); @@ -481,6 +541,12 @@ jbyteArray rocksdb_get_helper( jbyte* key = new jbyte[jkey_len]; env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] key; + return nullptr; + } + rocksdb::Slice key_slice( reinterpret_cast(key), jkey_len); @@ -501,13 +567,15 @@ jbyteArray rocksdb_get_helper( } if (s.ok()) { - jbyteArray jret_value = env->NewByteArray(static_cast(value.size())); - env->SetByteArrayRegion(jret_value, 0, static_cast(value.size()), - reinterpret_cast(value.c_str())); + jbyteArray jret_value = rocksdb::JniUtil::copyBytes(env, value); + if(jret_value == nullptr) { + // exception occurred + return nullptr; + } return jret_value; } - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); return nullptr; } @@ -541,8 +609,7 @@ jbyteArray Java_org_rocksdb_RocksDB_get__J_3BIIJ( } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); - // will never be evaluated - return env->NewByteArray(0); + return nullptr; } } @@ -568,17 +635,16 @@ jbyteArray Java_org_rocksdb_RocksDB_get__JJ_3BII( jbyteArray Java_org_rocksdb_RocksDB_get__JJ_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jropt_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jlong jcf_handle) { - auto db_handle = reinterpret_cast(jdb_handle); + auto* db_handle = reinterpret_cast(jdb_handle); auto& ro_opt = *reinterpret_cast(jropt_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { return rocksdb_get_helper(env, db_handle, ro_opt, cf_handle, jkey, jkey_off, jkey_len); } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); - // will never be evaluated - return env->NewByteArray(0); + return nullptr; } } @@ -586,14 +652,20 @@ jint rocksdb_get_helper(JNIEnv* env, rocksdb::DB* db, const rocksdb::ReadOptions& read_options, rocksdb::ColumnFamilyHandle* column_family_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, - jbyteArray jval, jint jval_off, jint jval_len) { + jbyteArray jval, jint jval_off, jint jval_len, + bool* has_exception) { static const int kNotFound = -1; static const int kStatusError = -2; jbyte* key = new jbyte[jkey_len]; env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key); - rocksdb::Slice key_slice( - reinterpret_cast(key), jkey_len); + if(env->ExceptionCheck()) { + // exception thrown: OutOfMemoryError + delete [] key; + *has_exception = true; + return kStatusError; + } + rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); // TODO(yhchiang): we might save one memory allocation here by adding // a DB::Get() function which takes preallocated jbyte* as input. @@ -610,8 +682,10 @@ jint rocksdb_get_helper(JNIEnv* env, rocksdb::DB* db, delete [] key; if (s.IsNotFound()) { + *has_exception = false; return kNotFound; } else if (!s.ok()) { + *has_exception = true; // Here since we are throwing a Java exception from c++ side. // As a result, c++ does not know calling this function will in fact // throwing an exception. As a result, the execution flow will @@ -624,24 +698,51 @@ jint rocksdb_get_helper(JNIEnv* env, rocksdb::DB* db, return kStatusError; } - jint cvalue_len = static_cast(cvalue.size()); - jint length = std::min(jval_len, cvalue_len); + const jint cvalue_len = static_cast(cvalue.size()); + const jint length = std::min(jval_len, cvalue_len); env->SetByteArrayRegion(jval, jval_off, length, reinterpret_cast(cvalue.c_str())); + if(env->ExceptionCheck()) { + // exception thrown: OutOfMemoryError + *has_exception = true; + return kStatusError; + } + + *has_exception = false; return cvalue_len; } -// cf multi get +inline void multi_get_helper_release_keys(JNIEnv* env, + std::vector> &keys_to_free) { + auto end = keys_to_free.end(); + for (auto it = keys_to_free.begin(); it != end; ++it) { + delete [] it->first; + env->DeleteLocalRef(it->second); + } + keys_to_free.clear(); +} + +/** + * cf multi get + * + * @return byte[][] of values or nullptr if an exception occurs + */ jobjectArray multi_get_helper(JNIEnv* env, jobject jdb, rocksdb::DB* db, const rocksdb::ReadOptions& rOpt, jobjectArray jkeys, jintArray jkey_offs, jintArray jkey_lens, jlongArray jcolumn_family_handles) { std::vector cf_handles; if (jcolumn_family_handles != nullptr) { - jsize len_cols = env->GetArrayLength(jcolumn_family_handles); - jlong* jcfh = env->GetLongArrayElements(jcolumn_family_handles, NULL); - for (int i = 0; i < len_cols; i++) { + const jsize len_cols = env->GetArrayLength(jcolumn_family_handles); + + jlong* jcfh = env->GetLongArrayElements(jcolumn_family_handles, nullptr); + if(jcfh == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + for (jsize i = 0; i < len_cols; i++) { auto* cf_handle = reinterpret_cast(jcfh[i]); cf_handles.push_back(cf_handle); @@ -649,25 +750,51 @@ jobjectArray multi_get_helper(JNIEnv* env, jobject jdb, rocksdb::DB* db, env->ReleaseLongArrayElements(jcolumn_family_handles, jcfh, JNI_ABORT); } - std::vector keys; - std::vector> keys_to_free; - jsize len_keys = env->GetArrayLength(jkeys); + const jsize len_keys = env->GetArrayLength(jkeys); if (env->EnsureLocalCapacity(len_keys) != 0) { - // out of memory - return NULL; + // exception thrown: OutOfMemoryError + return nullptr; } - jint* jkey_off = env->GetIntArrayElements(jkey_offs, NULL); - jint* jkey_len = env->GetIntArrayElements(jkey_lens, NULL); + jint* jkey_off = env->GetIntArrayElements(jkey_offs, nullptr); + if(jkey_off == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + jint* jkey_len = env->GetIntArrayElements(jkey_lens, nullptr); + if(jkey_len == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseIntArrayElements(jkey_offs, jkey_off, JNI_ABORT); + return nullptr; + } - for (int i = 0; i < len_keys; i++) { + std::vector keys; + std::vector> keys_to_free; + for (jsize i = 0; i < len_keys; i++) { jobject jkey = env->GetObjectArrayElement(jkeys, i); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->ReleaseIntArrayElements(jkey_lens, jkey_len, JNI_ABORT); + env->ReleaseIntArrayElements(jkey_offs, jkey_off, JNI_ABORT); + multi_get_helper_release_keys(env, keys_to_free); + return nullptr; + } jbyteArray jkey_ba = reinterpret_cast(jkey); - jint len_key = jkey_len[i]; + const jint len_key = jkey_len[i]; jbyte* key = new jbyte[len_key]; env->GetByteArrayRegion(jkey_ba, jkey_off[i], len_key, key); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] key; + env->DeleteLocalRef(jkey); + env->ReleaseIntArrayElements(jkey_lens, jkey_len, JNI_ABORT); + env->ReleaseIntArrayElements(jkey_offs, jkey_off, JNI_ABORT); + multi_get_helper_release_keys(env, keys_to_free); + return nullptr; + } rocksdb::Slice key_slice(reinterpret_cast(key), len_key); keys.push_back(key_slice); @@ -688,26 +815,48 @@ jobjectArray multi_get_helper(JNIEnv* env, jobject jdb, rocksdb::DB* db, } // free up allocated byte arrays - for (auto it = keys_to_free.begin(); it != keys_to_free.end(); ++it) { - delete [] it->first; - env->DeleteLocalRef(it->second); - } - keys_to_free.clear(); + multi_get_helper_release_keys(env, keys_to_free); // prepare the results - jclass jcls_ba = env->FindClass("[B"); jobjectArray jresults = - env->NewObjectArray(static_cast(s.size()), jcls_ba, NULL); + rocksdb::ByteJni::new2dByteArray(env, static_cast(s.size())); + if(jresults == nullptr) { + // exception occurred + return nullptr; + } + // TODO(AR) it is not clear to me why EnsureLocalCapacity is needed for the + // loop as we cleanup references with env->DeleteLocalRef(jentry_value); + if (env->EnsureLocalCapacity(static_cast(s.size())) != 0) { + // exception thrown: OutOfMemoryError + return nullptr; + } // add to the jresults for (std::vector::size_type i = 0; i != s.size(); i++) { if (s[i].ok()) { - jbyteArray jentry_value = - env->NewByteArray(static_cast(values[i].size())); - env->SetByteArrayRegion( - jentry_value, 0, static_cast(values[i].size()), - reinterpret_cast(values[i].c_str())); + std::string* value = &values[i]; + const jsize jvalue_len = static_cast(value->size()); + jbyteArray jentry_value = env->NewByteArray(jvalue_len); + if(jentry_value == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + env->SetByteArrayRegion(jentry_value, 0, static_cast(jvalue_len), + reinterpret_cast(value->c_str())); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jentry_value); + return nullptr; + } + env->SetObjectArrayElement(jresults, static_cast(i), jentry_value); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jentry_value); + return nullptr; + } + env->DeleteLocalRef(jentry_value); } } @@ -778,9 +927,11 @@ jint Java_org_rocksdb_RocksDB_get__J_3BII_3BII(JNIEnv* env, jobject jdb, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { + bool has_exception = false; return rocksdb_get_helper(env, reinterpret_cast(jdb_handle), rocksdb::ReadOptions(), nullptr, jkey, jkey_off, - jkey_len, jval, jval_off, jval_len); + jkey_len, jval, jval_off, jval_len, + &has_exception); } /* @@ -794,12 +945,13 @@ jint Java_org_rocksdb_RocksDB_get__J_3BII_3BIIJ(JNIEnv* env, jobject jdb, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len, jlong jcf_handle) { - auto db_handle = reinterpret_cast(jdb_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db_handle = reinterpret_cast(jdb_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { + bool has_exception = false; return rocksdb_get_helper(env, db_handle, rocksdb::ReadOptions(), cf_handle, jkey, jkey_off, jkey_len, jval, jval_off, - jval_len); + jval_len, &has_exception); } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); @@ -819,10 +971,11 @@ jint Java_org_rocksdb_RocksDB_get__JJ_3BII_3BII(JNIEnv* env, jobject jdb, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { + bool has_exception = false; return rocksdb_get_helper( env, reinterpret_cast(jdb_handle), *reinterpret_cast(jropt_handle), nullptr, jkey, - jkey_off, jkey_len, jval, jval_off, jval_len); + jkey_off, jkey_len, jval, jval_off, jval_len, &has_exception); } /* @@ -834,12 +987,14 @@ jint Java_org_rocksdb_RocksDB_get__JJ_3BII_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jropt_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len, jlong jcf_handle) { - auto db_handle = reinterpret_cast(jdb_handle); + auto* db_handle = reinterpret_cast(jdb_handle); auto& ro_opt = *reinterpret_cast(jropt_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { + bool has_exception = false; return rocksdb_get_helper(env, db_handle, ro_opt, cf_handle, jkey, jkey_off, - jkey_len, jval, jval_off, jval_len); + jkey_len, jval, jval_off, jval_len, + &has_exception); } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::InvalidArgument("Invalid ColumnFamilyHandle.")); @@ -847,14 +1002,24 @@ jint Java_org_rocksdb_RocksDB_get__JJ_3BII_3BIIJ( return 0; } } + ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::Delete() -void rocksdb_delete_helper( + +/** + * @return true if the delete succeeded, false if a Java Exception was thrown + */ +bool rocksdb_delete_helper( JNIEnv* env, rocksdb::DB* db, const rocksdb::WriteOptions& write_options, rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_off, jint jkey_len) { jbyte* key = new jbyte[jkey_len]; env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] key; + return false; + } rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); rocksdb::Status s; @@ -868,10 +1033,12 @@ void rocksdb_delete_helper( // cleanup delete [] key; - if (!s.ok()) { - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + if (s.ok()) { + return true; } - return; + + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + return false; } /* @@ -882,7 +1049,7 @@ void rocksdb_delete_helper( void Java_org_rocksdb_RocksDB_delete__J_3BII( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_off, jint jkey_len) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); rocksdb_delete_helper(env, db, default_write_options, nullptr, @@ -897,10 +1064,10 @@ void Java_org_rocksdb_RocksDB_delete__J_3BII( void Java_org_rocksdb_RocksDB_delete__J_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_delete_helper(env, db, default_write_options, cf_handle, jkey, jkey_off, jkey_len); @@ -918,8 +1085,9 @@ void Java_org_rocksdb_RocksDB_delete__J_3BIIJ( void Java_org_rocksdb_RocksDB_delete__JJ_3BII( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options, jbyteArray jkey, jint jkey_off, jint jkey_len) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast(jwrite_options); + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options); rocksdb_delete_helper(env, db, *write_options, nullptr, jkey, jkey_off, jkey_len); } @@ -933,9 +1101,10 @@ void Java_org_rocksdb_RocksDB_delete__JJ_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options, jbyteArray jkey, jint jkey_off, jint jkey_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast(jwrite_options); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_delete_helper(env, db, *write_options, cf_handle, jkey, jkey_off, jkey_len); @@ -947,10 +1116,18 @@ void Java_org_rocksdb_RocksDB_delete__JJ_3BIIJ( ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::SingleDelete() -void rocksdb_single_delete_helper( +/** + * @return true if the single delete succeeded, false if a Java Exception + * was thrown + */ +bool rocksdb_single_delete_helper( JNIEnv* env, rocksdb::DB* db, const rocksdb::WriteOptions& write_options, rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_len) { - jbyte* key = env->GetByteArrayElements(jkey, 0); + jbyte* key = env->GetByteArrayElements(jkey, nullptr); + if(key == nullptr) { + // exception thrown: OutOfMemoryError + return false; + } rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); rocksdb::Status s; @@ -960,14 +1137,18 @@ void rocksdb_single_delete_helper( // backwards compatibility s = db->SingleDelete(write_options, key_slice); } + // trigger java unref on key and value. // by passing JNI_ABORT, it will simply release the reference without // copying the result back to the java byte array. env->ReleaseByteArrayElements(jkey, key, JNI_ABORT); - if (!s.ok()) { - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + if (s.ok()) { + return true; } + + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + return false; } /* @@ -978,7 +1159,7 @@ void rocksdb_single_delete_helper( void Java_org_rocksdb_RocksDB_singleDelete__J_3BI( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_len) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); rocksdb_single_delete_helper(env, db, default_write_options, nullptr, @@ -993,10 +1174,10 @@ void Java_org_rocksdb_RocksDB_singleDelete__J_3BI( void Java_org_rocksdb_RocksDB_singleDelete__J_3BIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_single_delete_helper(env, db, default_write_options, cf_handle, jkey, jkey_len); @@ -1014,8 +1195,9 @@ void Java_org_rocksdb_RocksDB_singleDelete__J_3BIJ( void Java_org_rocksdb_RocksDB_singleDelete__JJ_3BI( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options, jbyteArray jkey, jint jkey_len) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast(jwrite_options); + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options); rocksdb_single_delete_helper(env, db, *write_options, nullptr, jkey, jkey_len); } @@ -1029,9 +1211,10 @@ void Java_org_rocksdb_RocksDB_singleDelete__JJ_3BIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options, jbyteArray jkey, jint jkey_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast(jwrite_options); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_single_delete_helper(env, db, *write_options, cf_handle, jkey, jkey_len); @@ -1044,16 +1227,31 @@ void Java_org_rocksdb_RocksDB_singleDelete__JJ_3BIJ( ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::Merge -void rocksdb_merge_helper(JNIEnv* env, rocksdb::DB* db, +/** + * @return true if the merge succeeded, false if a Java Exception was thrown + */ +bool rocksdb_merge_helper(JNIEnv* env, rocksdb::DB* db, const rocksdb::WriteOptions& write_options, rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { jbyte* key = new jbyte[jkey_len]; env->GetByteArrayRegion(jkey, jkey_off, jkey_len, key); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] key; + return false; + } + rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); + jbyte* value = new jbyte[jkey_len]; env->GetByteArrayRegion(jval, jval_off, jval_len, value); - rocksdb::Slice key_slice(reinterpret_cast(key), jkey_len); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + delete [] value; + delete [] key; + return false; + } rocksdb::Slice value_slice(reinterpret_cast(value), jval_len); rocksdb::Status s; @@ -1068,9 +1266,11 @@ void rocksdb_merge_helper(JNIEnv* env, rocksdb::DB* db, delete [] key; if (s.ok()) { - return; + return true; } + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + return false; } /* @@ -1083,7 +1283,7 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BII(JNIEnv* env, jobject jdb, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); @@ -1100,10 +1300,10 @@ void Java_org_rocksdb_RocksDB_merge__J_3BII_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); static const rocksdb::WriteOptions default_write_options = rocksdb::WriteOptions(); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_merge_helper(env, db, default_write_options, cf_handle, jkey, jkey_off, jkey_len, jval, jval_off, jval_len); @@ -1122,9 +1322,9 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BII( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast( - jwrite_options_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options_handle); rocksdb_merge_helper(env, db, *write_options, nullptr, jkey, jkey_off, jkey_len, jval, jval_off, jval_len); @@ -1139,10 +1339,10 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jwrite_options_handle, jbyteArray jkey, jint jkey_off, jint jkey_len, jbyteArray jval, jint jval_off, jint jval_len, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto write_options = reinterpret_cast( - jwrite_options_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* write_options = + reinterpret_cast(jwrite_options_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); if (cf_handle != nullptr) { rocksdb_merge_helper(env, db, *write_options, cf_handle, jkey, jkey_off, jkey_len, jval, jval_off, jval_len); @@ -1162,7 +1362,9 @@ void Java_org_rocksdb_RocksDB_merge__JJ_3BII_3BIIJ( */ void Java_org_rocksdb_RocksDB_disposeInternal( JNIEnv* env, jobject java_db, jlong jhandle) { - delete reinterpret_cast(jhandle); + auto* db = reinterpret_cast(jhandle); + assert(db != nullptr); + delete db; } jlong rocksdb_iterator_helper( @@ -1184,7 +1386,7 @@ jlong rocksdb_iterator_helper( */ jlong Java_org_rocksdb_RocksDB_iterator__J( JNIEnv* env, jobject jdb, jlong db_handle) { - auto db = reinterpret_cast(db_handle); + auto* db = reinterpret_cast(db_handle); return rocksdb_iterator_helper(db, rocksdb::ReadOptions(), nullptr); } @@ -1197,7 +1399,7 @@ jlong Java_org_rocksdb_RocksDB_iterator__J( jlong Java_org_rocksdb_RocksDB_iterator__JJ( JNIEnv* env, jobject jdb, jlong db_handle, jlong jread_options_handle) { - auto db = reinterpret_cast(db_handle); + auto* db = reinterpret_cast(db_handle); auto& read_options = *reinterpret_cast( jread_options_handle); return rocksdb_iterator_helper(db, read_options, @@ -1211,8 +1413,8 @@ jlong Java_org_rocksdb_RocksDB_iterator__JJ( */ jlong Java_org_rocksdb_RocksDB_iteratorCF__JJ( JNIEnv* env, jobject jdb, jlong db_handle, jlong jcf_handle) { - auto db = reinterpret_cast(db_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(db_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); return rocksdb_iterator_helper(db, rocksdb::ReadOptions(), cf_handle); } @@ -1225,8 +1427,8 @@ jlong Java_org_rocksdb_RocksDB_iteratorCF__JJ( jlong Java_org_rocksdb_RocksDB_iteratorCF__JJJ( JNIEnv* env, jobject jdb, jlong db_handle, jlong jcf_handle, jlong jread_options_handle) { - auto db = reinterpret_cast(db_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(db_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); auto& read_options = *reinterpret_cast( jread_options_handle); return rocksdb_iterator_helper(db, read_options, @@ -1244,15 +1446,22 @@ jlongArray Java_org_rocksdb_RocksDB_iterators( auto* db = reinterpret_cast(db_handle); auto& read_options = *reinterpret_cast( jread_options_handle); + std::vector cf_handles; if (jcolumn_family_handles != nullptr) { - jsize len_cols = env->GetArrayLength(jcolumn_family_handles); - jlong* jcfh = env->GetLongArrayElements(jcolumn_family_handles, NULL); - for (int i = 0; i < len_cols; i++) { + const jsize len_cols = env->GetArrayLength(jcolumn_family_handles); + jlong* jcfh = env->GetLongArrayElements(jcolumn_family_handles, nullptr); + if(jcfh == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + + for (jsize i = 0; i < len_cols; i++) { auto* cf_handle = reinterpret_cast(jcfh[i]); cf_handles.push_back(cf_handle); } + env->ReleaseLongArrayElements(jcolumn_family_handles, jcfh, JNI_ABORT); } @@ -1262,15 +1471,26 @@ jlongArray Java_org_rocksdb_RocksDB_iterators( if (s.ok()) { jlongArray jLongArray = env->NewLongArray(static_cast(iterators.size())); + if(jLongArray == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + for (std::vector::size_type i = 0; i < iterators.size(); i++) { env->SetLongArrayRegion(jLongArray, static_cast(i), 1, reinterpret_cast(&iterators[i])); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jLongArray); + return nullptr; + } } + return jLongArray; } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, s); - return NULL; + return nullptr; } } @@ -1295,21 +1515,27 @@ jlong Java_org_rocksdb_RocksDB_createColumnFamily( JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jcolumn_name, jlong jcolumn_options) { rocksdb::ColumnFamilyHandle* handle; - auto db_handle = reinterpret_cast(jdb_handle); - - jbyte* cfname = env->GetByteArrayElements(jcolumn_name, 0); - const int len = env->GetArrayLength(jcolumn_name); + jboolean has_exception = JNI_FALSE; + std::string column_name = rocksdb::JniUtil::byteString(env, + jcolumn_name, + [](const char* str, const size_t len) { return std::string(str, len); }, + &has_exception); + if(has_exception == JNI_TRUE) { + // exception occurred + return 0; + } + auto* db_handle = reinterpret_cast(jdb_handle); auto* cfOptions = reinterpret_cast(jcolumn_options); rocksdb::Status s = db_handle->CreateColumnFamily( - *cfOptions, std::string(reinterpret_cast(cfname), len), &handle); - env->ReleaseByteArrayElements(jcolumn_name, cfname, 0); + *cfOptions, column_name, &handle); if (s.ok()) { return reinterpret_cast(handle); } + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); return 0; } @@ -1321,8 +1547,8 @@ jlong Java_org_rocksdb_RocksDB_createColumnFamily( */ void Java_org_rocksdb_RocksDB_dropColumnFamily( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jcf_handle) { - auto cf_handle = reinterpret_cast(jcf_handle); - auto db_handle = reinterpret_cast(jdb_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); + auto* db_handle = reinterpret_cast(jdb_handle); rocksdb::Status s = db_handle->DropColumnFamily(cf_handle); if (!s.ok()) { rocksdb::RocksDBExceptionJni::ThrowNew(env, s); @@ -1335,7 +1561,7 @@ void Java_org_rocksdb_RocksDB_dropColumnFamily( */ jlong Java_org_rocksdb_RocksDB_getSnapshot( JNIEnv* env, jobject jdb, jlong db_handle) { - auto db = reinterpret_cast(db_handle); + auto* db = reinterpret_cast(db_handle); const rocksdb::Snapshot* snapshot = db->GetSnapshot(); return reinterpret_cast(snapshot); } @@ -1346,8 +1572,8 @@ jlong Java_org_rocksdb_RocksDB_getSnapshot( */ void Java_org_rocksdb_RocksDB_releaseSnapshot( JNIEnv* env, jobject jdb, jlong db_handle, jlong snapshot_handle) { - auto db = reinterpret_cast(db_handle); - auto snapshot = reinterpret_cast(snapshot_handle); + auto* db = reinterpret_cast(db_handle); + auto* snapshot = reinterpret_cast(snapshot_handle); db->ReleaseSnapshot(snapshot); } @@ -1359,20 +1585,24 @@ void Java_org_rocksdb_RocksDB_releaseSnapshot( jstring Java_org_rocksdb_RocksDB_getProperty0__JLjava_lang_String_2I( JNIEnv* env, jobject jdb, jlong db_handle, jstring jproperty, jint jproperty_len) { - auto db = reinterpret_cast(db_handle); - - const char* property = env->GetStringUTFChars(jproperty, 0); + const char* property = env->GetStringUTFChars(jproperty, nullptr); + if(property == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } rocksdb::Slice property_slice(property, jproperty_len); + auto *db = reinterpret_cast(db_handle); std::string property_value; bool retCode = db->GetProperty(property_slice, &property_value); env->ReleaseStringUTFChars(jproperty, property); - if (!retCode) { - rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + if (retCode) { + return env->NewStringUTF(property_value.c_str()); } - return env->NewStringUTF(property_value.data()); + rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + return nullptr; } /* @@ -1383,21 +1613,25 @@ jstring Java_org_rocksdb_RocksDB_getProperty0__JLjava_lang_String_2I( jstring Java_org_rocksdb_RocksDB_getProperty0__JJLjava_lang_String_2I( JNIEnv* env, jobject jdb, jlong db_handle, jlong jcf_handle, jstring jproperty, jint jproperty_len) { - auto db = reinterpret_cast(db_handle); - auto cf_handle = reinterpret_cast(jcf_handle); - - const char* property = env->GetStringUTFChars(jproperty, 0); + const char* property = env->GetStringUTFChars(jproperty, nullptr); + if(property == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } rocksdb::Slice property_slice(property, jproperty_len); + auto* db = reinterpret_cast(db_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); std::string property_value; bool retCode = db->GetProperty(cf_handle, property_slice, &property_value); env->ReleaseStringUTFChars(jproperty, property); - if (!retCode) { - rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + if (retCode) { + return env->NewStringUTF(property_value.c_str()); } - return env->NewStringUTF(property_value.data()); + rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + return nullptr; } /* @@ -1408,19 +1642,24 @@ jstring Java_org_rocksdb_RocksDB_getProperty0__JJLjava_lang_String_2I( jlong Java_org_rocksdb_RocksDB_getLongProperty__JLjava_lang_String_2I( JNIEnv* env, jobject jdb, jlong db_handle, jstring jproperty, jint jproperty_len) { - auto db = reinterpret_cast(db_handle); - - const char* property = env->GetStringUTFChars(jproperty, 0); + const char* property = env->GetStringUTFChars(jproperty, nullptr); + if(property == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } rocksdb::Slice property_slice(property, jproperty_len); + auto* db = reinterpret_cast(db_handle); uint64_t property_value = 0; bool retCode = db->GetIntProperty(property_slice, &property_value); env->ReleaseStringUTFChars(jproperty, property); - if (!retCode) { - rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + if (retCode) { + return property_value; } - return property_value; + + rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + return 0; } /* @@ -1431,20 +1670,25 @@ jlong Java_org_rocksdb_RocksDB_getLongProperty__JLjava_lang_String_2I( jlong Java_org_rocksdb_RocksDB_getLongProperty__JJLjava_lang_String_2I( JNIEnv* env, jobject jdb, jlong db_handle, jlong jcf_handle, jstring jproperty, jint jproperty_len) { - auto db = reinterpret_cast(db_handle); - auto cf_handle = reinterpret_cast(jcf_handle); - - const char* property = env->GetStringUTFChars(jproperty, 0); + const char* property = env->GetStringUTFChars(jproperty, nullptr); + if(property == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } rocksdb::Slice property_slice(property, jproperty_len); + auto* db = reinterpret_cast(db_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); uint64_t property_value; bool retCode = db->GetIntProperty(cf_handle, property_slice, &property_value); env->ReleaseStringUTFChars(jproperty, property); - if (!retCode) { - rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + if (retCode) { + return property_value; } - return property_value; + + rocksdb::RocksDBExceptionJni::ThrowNew(env, rocksdb::Status::NotFound()); + return 0; } ////////////////////////////////////////////////////////////////////////////// @@ -1472,8 +1716,9 @@ void rocksdb_flush_helper( void Java_org_rocksdb_RocksDB_flush__JJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jflush_options) { - auto db = reinterpret_cast(jdb_handle); - auto flush_options = reinterpret_cast(jflush_options); + auto* db = reinterpret_cast(jdb_handle); + auto* flush_options = + reinterpret_cast(jflush_options); rocksdb_flush_helper(env, db, *flush_options, nullptr); } @@ -1485,9 +1730,10 @@ void Java_org_rocksdb_RocksDB_flush__JJ( void Java_org_rocksdb_RocksDB_flush__JJJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jflush_options, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto flush_options = reinterpret_cast(jflush_options); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* flush_options = + reinterpret_cast(jflush_options); + auto* cf_handle = reinterpret_cast(jcf_handle); rocksdb_flush_helper(env, db, *flush_options, cf_handle); } @@ -1524,7 +1770,7 @@ void rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db, void Java_org_rocksdb_RocksDB_compactRange0__JZII(JNIEnv* env, jobject jdb, jlong jdb_handle, jboolean jreduce_level, jint jtarget_level, jint jtarget_path_id) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); rocksdb_compactrange_helper(env, db, nullptr, jreduce_level, jtarget_level, jtarget_path_id); } @@ -1538,8 +1784,8 @@ void Java_org_rocksdb_RocksDB_compactRange__JZIIJ( JNIEnv* env, jobject jdb, jlong jdb_handle, jboolean jreduce_level, jint jtarget_level, jint jtarget_path_id, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); rocksdb_compactrange_helper(env, db, cf_handle, jreduce_level, jtarget_level, jtarget_path_id); } @@ -1547,13 +1793,28 @@ void Java_org_rocksdb_RocksDB_compactRange__JZIIJ( ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::CompactRange - Range -void rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db, +/** + * @return true if the compact range succeeded, false if a Java Exception + * was thrown + */ +bool rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db, rocksdb::ColumnFamilyHandle* cf_handle, jbyteArray jbegin, jint jbegin_len, jbyteArray jend, jint jend_len, jboolean jreduce_level, jint jtarget_level, jint jtarget_path_id) { - jbyte* begin = env->GetByteArrayElements(jbegin, 0); - jbyte* end = env->GetByteArrayElements(jend, 0); + jbyte* begin = env->GetByteArrayElements(jbegin, nullptr); + if(begin == nullptr) { + // exception thrown: OutOfMemoryError + return false; + } + + jbyte* end = env->GetByteArrayElements(jend, nullptr); + if(end == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseByteArrayElements(jbegin, begin, JNI_ABORT); + return false; + } + const rocksdb::Slice begin_slice(reinterpret_cast(begin), jbegin_len); const rocksdb::Slice end_slice(reinterpret_cast(end), jend_len); @@ -1569,13 +1830,15 @@ void rocksdb_compactrange_helper(JNIEnv* env, rocksdb::DB* db, s = db->CompactRange(compact_options, &begin_slice, &end_slice); } - env->ReleaseByteArrayElements(jbegin, begin, JNI_ABORT); - env->ReleaseByteArrayElements(jend, end, JNI_ABORT); + env->ReleaseByteArrayElements(jend, begin, JNI_ABORT); + env->ReleaseByteArrayElements(jbegin, end, JNI_ABORT); if (s.ok()) { - return; + return true; } + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); + return false; } /* @@ -1587,7 +1850,7 @@ void Java_org_rocksdb_RocksDB_compactRange0__J_3BI_3BIZII(JNIEnv* env, jobject jdb, jlong jdb_handle, jbyteArray jbegin, jint jbegin_len, jbyteArray jend, jint jend_len, jboolean jreduce_level, jint jtarget_level, jint jtarget_path_id) { - auto db = reinterpret_cast(jdb_handle); + auto* db = reinterpret_cast(jdb_handle); rocksdb_compactrange_helper(env, db, nullptr, jbegin, jbegin_len, jend, jend_len, jreduce_level, jtarget_level, jtarget_path_id); } @@ -1602,8 +1865,8 @@ void Java_org_rocksdb_RocksDB_compactRange__J_3BI_3BIZIIJ( jint jbegin_len, jbyteArray jend, jint jend_len, jboolean jreduce_level, jint jtarget_level, jint jtarget_path_id, jlong jcf_handle) { - auto db = reinterpret_cast(jdb_handle); - auto cf_handle = reinterpret_cast(jcf_handle); + auto* db = reinterpret_cast(jdb_handle); + auto* cf_handle = reinterpret_cast(jcf_handle); rocksdb_compactrange_helper(env, db, cf_handle, jbegin, jbegin_len, jend, jend_len, jreduce_level, jtarget_level, jtarget_path_id); } @@ -1620,10 +1883,9 @@ void Java_org_rocksdb_RocksDB_pauseBackgroundWork( JNIEnv* env, jobject jobj, jlong jdb_handle) { auto* db = reinterpret_cast(jdb_handle); auto s = db->PauseBackgroundWork(); - if (s.ok()) { - return; + if (!s.ok()) { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } ////////////////////////////////////////////////////////////////////////////// @@ -1638,10 +1900,9 @@ void Java_org_rocksdb_RocksDB_continueBackgroundWork( JNIEnv* env, jobject jobj, jlong jdb_handle) { auto* db = reinterpret_cast(jdb_handle); auto s = db->ContinueBackgroundWork(); - if (s.ok()) { - return; + if (!s.ok()) { + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } ////////////////////////////////////////////////////////////////////////////// @@ -1707,6 +1968,7 @@ jlong Java_org_rocksdb_RocksDB_getUpdatesSince(JNIEnv* env, if (s.ok()) { return reinterpret_cast(iter.release()); } + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); return 0; } @@ -1719,23 +1981,52 @@ jlong Java_org_rocksdb_RocksDB_getUpdatesSince(JNIEnv* env, void Java_org_rocksdb_RocksDB_setOptions(JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jcf_handle, jobjectArray jkeys, jobjectArray jvalues) { - std::unordered_map options_map; const jsize len = env->GetArrayLength(jkeys); assert(len == env->GetArrayLength(jvalues)); - for (int i = 0; i < len; i++) { + + std::unordered_map options_map; + for (jsize i = 0; i < len; i++) { jobject jobj_key = env->GetObjectArrayElement(jkeys, i); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + return; + } + jobject jobj_value = env->GetObjectArrayElement(jvalues, i); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jobj_key); + return; + } + jstring jkey = reinterpret_cast(jobj_key); jstring jval = reinterpret_cast(jobj_value); - const char* key = env->GetStringUTFChars(jkey, NULL); - const char* value = env->GetStringUTFChars(jval, NULL); + + const char* key = env->GetStringUTFChars(jkey, nullptr); + if(key == nullptr) { + // exception thrown: OutOfMemoryError + env->DeleteLocalRef(jobj_value); + env->DeleteLocalRef(jobj_key); + return; + } + + const char* value = env->GetStringUTFChars(jval, nullptr); + if(value == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jkey, key); + env->DeleteLocalRef(jobj_value); + env->DeleteLocalRef(jobj_key); + return; + } + std::string s_key(key); std::string s_value(value); + options_map[s_key] = s_value; + env->ReleaseStringUTFChars(jkey, key); env->ReleaseStringUTFChars(jval, value); env->DeleteLocalRef(jobj_key); env->DeleteLocalRef(jobj_value); - options_map[s_key] = s_value; } auto* db = reinterpret_cast(jdb_handle); @@ -1746,19 +2037,6 @@ void Java_org_rocksdb_RocksDB_setOptions(JNIEnv* env, jobject jdb, ////////////////////////////////////////////////////////////////////////////// // rocksdb::DB::AddFile -void add_file_helper(JNIEnv* env, const jobjectArray& jfile_path_list, - int file_path_list_len, - std::vector* file_path_list) { - for (int i = 0; i < file_path_list_len; i++) { - jstring jfile_path = - static_cast(env->GetObjectArrayElement(jfile_path_list, i)); - const char* file_path = env->GetStringUTFChars(jfile_path, NULL); - file_path_list->push_back(std::string(file_path)); - env->ReleaseStringUTFChars(jfile_path, file_path); - env->DeleteLocalRef(jfile_path); - } -} - /* * Class: org_rocksdb_RocksDB * Method: addFile @@ -1768,10 +2046,17 @@ void Java_org_rocksdb_RocksDB_addFile__JJ_3Ljava_lang_String_2IZ( JNIEnv* env, jobject jdb, jlong jdb_handle, jlong jcf_handle, jobjectArray jfile_path_list, jint jfile_path_list_len, jboolean jmove_file) { + + jboolean has_exception = JNI_FALSE; + std::vector file_path_list = + rocksdb::JniUtil::copyStrings(env, jfile_path_list, jfile_path_list_len, + &has_exception); + if(has_exception == JNI_TRUE) { + // exception occured + return; + } + auto* db = reinterpret_cast(jdb_handle); - std::vector file_path_list; - add_file_helper(env, jfile_path_list, static_cast(jfile_path_list_len), - &file_path_list); auto* column_family = reinterpret_cast(jcf_handle); rocksdb::IngestExternalFileOptions ifo; diff --git a/java/rocksjni/slice.cc b/java/rocksjni/slice.cc index e5eb383bd..d485b2b7f 100644 --- a/java/rocksjni/slice.cc +++ b/java/rocksjni/slice.cc @@ -26,8 +26,17 @@ */ jlong Java_org_rocksdb_AbstractSlice_createNewSliceFromString( JNIEnv * env, jclass jcls, jstring jstr) { - const auto* str = env->GetStringUTFChars(jstr, NULL); + const auto* str = env->GetStringUTFChars(jstr, nullptr); + if(str == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + const size_t len = strlen(str); + + // NOTE: buf will be deleted in the + // Java_org_rocksdb_Slice_disposeInternalBuf or + // or Java_org_rocksdb_DirectSlice_disposeInternalBuf methods char* buf = new char[len + 1]; memcpy(buf, str, len); buf[len] = 0; @@ -118,13 +127,18 @@ void Java_org_rocksdb_AbstractSlice_disposeInternal( */ jlong Java_org_rocksdb_Slice_createNewSlice0( JNIEnv * env, jclass jcls, jbyteArray data, jint offset) { - const jsize dataSize = env->GetArrayLength(data); const int len = dataSize - offset; - jbyte* ptrData = new jbyte[len]; - env->GetByteArrayRegion(data, offset, len, ptrData); - const auto* slice = new rocksdb::Slice((const char*)ptrData, len); + // NOTE: buf will be deleted in the Java_org_rocksdb_Slice_disposeInternalBuf method + jbyte* buf = new jbyte[len]; + env->GetByteArrayRegion(data, offset, len, buf); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + return 0; + } + + const auto* slice = new rocksdb::Slice((const char*)buf, len); return reinterpret_cast(slice); } @@ -135,16 +149,17 @@ jlong Java_org_rocksdb_Slice_createNewSlice0( */ jlong Java_org_rocksdb_Slice_createNewSlice1( JNIEnv * env, jclass jcls, jbyteArray data) { - + jbyte* ptrData = env->GetByteArrayElements(data, nullptr); + if(ptrData == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } const int len = env->GetArrayLength(data) + 1; - jboolean isCopy; - jbyte* ptrData = env->GetByteArrayElements(data, &isCopy); - - // NOTE: buf will be deleted in the org.rocksdb.Slice#dispose method + // NOTE: buf will be deleted in the Java_org_rocksdb_Slice_disposeInternalBuf method char* buf = new char[len]; memcpy(buf, ptrData, len - 1); - buf[len-1]='\0'; + buf[len-1] = '\0'; const auto* slice = new rocksdb::Slice(buf, len - 1); @@ -162,22 +177,61 @@ jlong Java_org_rocksdb_Slice_createNewSlice1( jbyteArray Java_org_rocksdb_Slice_data0( JNIEnv* env, jobject jobj, jlong handle) { const auto* slice = reinterpret_cast(handle); - const int len = static_cast(slice->size()); + const jsize len = static_cast(slice->size()); const jbyteArray data = env->NewByteArray(len); + if(data == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + env->SetByteArrayRegion(data, 0, len, reinterpret_cast(slice->data())); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(data); + return nullptr; + } + return data; } +/* + * Class: org_rocksdb_Slice + * Method: clear0 + * Signature: (JZJ)V + */ +void Java_org_rocksdb_Slice_clear0( + JNIEnv * env, jobject jobj, jlong handle, jboolean shouldRelease, + jlong internalBufferOffset) { + auto* slice = reinterpret_cast(handle); + if(shouldRelease == JNI_TRUE) { + const char* buf = slice->data_ - internalBufferOffset; + delete [] buf; + } + slice->clear(); +} + +/* + * Class: org_rocksdb_Slice + * Method: removePrefix0 + * Signature: (JI)V + */ +void Java_org_rocksdb_Slice_removePrefix0( + JNIEnv * env, jobject jobj, jlong handle, jint length) { + auto* slice = reinterpret_cast(handle); + slice->remove_prefix(length); +} + /* * Class: org_rocksdb_Slice * Method: disposeInternalBuf - * Signature: (J)V + * Signature: (JJ)V */ void Java_org_rocksdb_Slice_disposeInternalBuf( - JNIEnv * env, jobject jobj, jlong handle) { + JNIEnv * env, jobject jobj, jlong handle, jlong internalBufferOffset) { const auto* slice = reinterpret_cast(handle); - delete [] slice->data_; + const char* buf = slice->data_ - internalBufferOffset; + delete [] buf; } // @@ -191,8 +245,19 @@ void Java_org_rocksdb_Slice_disposeInternalBuf( */ jlong Java_org_rocksdb_DirectSlice_createNewDirectSlice0( JNIEnv* env, jclass jcls, jobject data, jint length) { + assert(data != nullptr); + void* data_addr = env->GetDirectBufferAddress(data); + if(data_addr == nullptr) { + // error: memory region is undefined, given object is not a direct + // java.nio.Buffer, or JNI access to direct buffers is not supported by JVM + rocksdb::IllegalArgumentExceptionJni::ThrowNew(env, + rocksdb::Status::InvalidArgument( + "Could not access DirectBuffer")); + return 0; + } + const auto* ptrData = - reinterpret_cast(env->GetDirectBufferAddress(data)); + reinterpret_cast(data_addr); const auto* slice = new rocksdb::Slice(ptrData, length); return reinterpret_cast(slice); } @@ -204,8 +269,17 @@ jlong Java_org_rocksdb_DirectSlice_createNewDirectSlice0( */ jlong Java_org_rocksdb_DirectSlice_createNewDirectSlice1( JNIEnv* env, jclass jcls, jobject data) { - const auto* ptrData = - reinterpret_cast(env->GetDirectBufferAddress(data)); + void* data_addr = env->GetDirectBufferAddress(data); + if(data_addr == nullptr) { + // error: memory region is undefined, given object is not a direct + // java.nio.Buffer, or JNI access to direct buffers is not supported by JVM + rocksdb::IllegalArgumentExceptionJni::ThrowNew(env, + rocksdb::Status::InvalidArgument( + "Could not access DirectBuffer")); + return 0; + } + + const auto* ptrData = reinterpret_cast(data_addr); const auto* slice = new rocksdb::Slice(ptrData); return reinterpret_cast(slice); } @@ -236,12 +310,16 @@ jbyte Java_org_rocksdb_DirectSlice_get0( /* * Class: org_rocksdb_DirectSlice * Method: clear0 - * Signature: (J)V + * Signature: (JZJ)V */ void Java_org_rocksdb_DirectSlice_clear0( - JNIEnv* env, jobject jobj, jlong handle) { + JNIEnv* env, jobject jobj, jlong handle, + jboolean shouldRelease, jlong internalBufferOffset) { auto* slice = reinterpret_cast(handle); - delete [] slice->data_; + if(shouldRelease == JNI_TRUE) { + const char* buf = slice->data_ - internalBufferOffset; + delete [] buf; + } slice->clear(); } @@ -256,4 +334,16 @@ void Java_org_rocksdb_DirectSlice_removePrefix0( slice->remove_prefix(length); } +/* + * Class: org_rocksdb_DirectSlice + * Method: disposeInternalBuf + * Signature: (JJ)V + */ +void Java_org_rocksdb_DirectSlice_disposeInternalBuf( + JNIEnv* env, jobject jobj, jlong handle, jlong internalBufferOffset) { + const auto* slice = reinterpret_cast(handle); + const char* buf = slice->data_ - internalBufferOffset; + delete [] buf; +} + // diff --git a/java/rocksjni/sst_file_writerjni.cc b/java/rocksjni/sst_file_writerjni.cc index f3fb3c88a..fe3ec3940 100644 --- a/java/rocksjni/sst_file_writerjni.cc +++ b/java/rocksjni/sst_file_writerjni.cc @@ -42,7 +42,11 @@ jlong Java_org_rocksdb_SstFileWriter_newSstFileWriter(JNIEnv *env, jclass jcls, */ void Java_org_rocksdb_SstFileWriter_open(JNIEnv *env, jobject jobj, jlong jhandle, jstring jfile_path) { - const char *file_path = env->GetStringUTFChars(jfile_path, NULL); + const char *file_path = env->GetStringUTFChars(jfile_path, nullptr); + if(file_path == nullptr) { + // exception thrown: OutOfMemoryError + return; + } rocksdb::Status s = reinterpret_cast(jhandle)->Open(file_path); env->ReleaseStringUTFChars(jfile_path, file_path); @@ -62,8 +66,9 @@ void Java_org_rocksdb_SstFileWriter_add(JNIEnv *env, jobject jobj, jlong jvalue_handle) { auto *key_slice = reinterpret_cast(jkey_handle); auto *value_slice = reinterpret_cast(jvalue_handle); - rocksdb::Status s = reinterpret_cast(jhandle)->Add( - *key_slice, *value_slice); + rocksdb::Status s = + reinterpret_cast(jhandle)->Add(*key_slice, + *value_slice); if (!s.ok()) { rocksdb::RocksDBExceptionJni::ThrowNew(env, s); } diff --git a/java/rocksjni/statistics.cc b/java/rocksjni/statistics.cc index c41ec72c0..6a30fa043 100644 --- a/java/rocksjni/statistics.cc +++ b/java/rocksjni/statistics.cc @@ -21,9 +21,8 @@ */ jlong Java_org_rocksdb_Statistics_getTickerCount0( JNIEnv* env, jobject jobj, jint tickerType, jlong handle) { - auto st = reinterpret_cast(handle); + auto* st = reinterpret_cast(handle); assert(st != nullptr); - return st->getTickerCount(static_cast(tickerType)); } @@ -34,17 +33,28 @@ jlong Java_org_rocksdb_Statistics_getTickerCount0( */ jobject Java_org_rocksdb_Statistics_getHistogramData0( JNIEnv* env, jobject jobj, jint histogramType, jlong handle) { - auto st = reinterpret_cast(handle); + auto* st = reinterpret_cast(handle); assert(st != nullptr); rocksdb::HistogramData data; st->histogramData(static_cast(histogramType), &data); - // Don't reuse class pointer - jclass jclazz = env->FindClass("org/rocksdb/HistogramData"); + jclass jclazz = rocksdb::HistogramDataJni::getJClass(env); + if(jclazz == nullptr) { + // exception occurred accessing class + return nullptr; + } + jmethodID mid = rocksdb::HistogramDataJni::getConstructorMethodId( - env, jclazz); - return env->NewObject(jclazz, mid, data.median, data.percentile95, - data.percentile99, data.average, data.standard_deviation); + env); + if(mid == nullptr) { + // exception occurred accessing method + return nullptr; + } + + return env->NewObject( + jclazz, + mid, data.median, data.percentile95,data.percentile99, data.average, + data.standard_deviation); } diff --git a/java/rocksjni/transaction_log.cc b/java/rocksjni/transaction_log.cc index eed8d84b5..ed44976fd 100644 --- a/java/rocksjni/transaction_log.cc +++ b/java/rocksjni/transaction_log.cc @@ -67,12 +67,5 @@ jobject Java_org_rocksdb_TransactionLogIterator_getBatch( JNIEnv* env, jobject jobj, jlong handle) { rocksdb::BatchResult batch_result = reinterpret_cast(handle)->GetBatch(); - jclass jclazz = env->FindClass( - "org/rocksdb/TransactionLogIterator$BatchResult"); - assert(jclazz != nullptr); - jmethodID mid = env->GetMethodID( - jclazz, "", "(Lorg/rocksdb/TransactionLogIterator;JJ)V"); - assert(mid != nullptr); - return env->NewObject(jclazz, mid, jobj, - batch_result.sequence, batch_result.writeBatchPtr.release()); + return rocksdb::BatchResultJni::construct(env, batch_result); } diff --git a/java/rocksjni/ttl.cc b/java/rocksjni/ttl.cc index 6b39252f1..84f640e9e 100644 --- a/java/rocksjni/ttl.cc +++ b/java/rocksjni/ttl.cc @@ -26,9 +26,14 @@ jlong Java_org_rocksdb_TtlDB_open(JNIEnv* env, jclass jcls, jlong joptions_handle, jstring jdb_path, jint jttl, jboolean jread_only) { + const char* db_path = env->GetStringUTFChars(jdb_path, nullptr); + if(db_path == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + auto* opt = reinterpret_cast(joptions_handle); rocksdb::DBWithTTL* db = nullptr; - const char* db_path = env->GetStringUTFChars(jdb_path, 0); rocksdb::Status s = rocksdb::DBWithTTL::Open(*opt, db_path, &db, jttl, jread_only); env->ReleaseStringUTFChars(jdb_path, db_path); @@ -53,49 +58,69 @@ jlongArray JNIEnv* env, jclass jcls, jlong jopt_handle, jstring jdb_path, jobjectArray jcolumn_names, jlongArray jcolumn_options, jintArray jttls, jboolean jread_only) { - auto* opt = reinterpret_cast(jopt_handle); - const char* db_path = env->GetStringUTFChars(jdb_path, NULL); - - std::vector column_families; - - jsize len_cols = env->GetArrayLength(jcolumn_names); - jlong* jco = env->GetLongArrayElements(jcolumn_options, NULL); - for(int i = 0; i < len_cols; i++) { - jobject jcn = env->GetObjectArrayElement(jcolumn_names, i); - jbyteArray jcn_ba = reinterpret_cast(jcn); - jbyte* jcf_name = env->GetByteArrayElements(jcn_ba, NULL); - const int jcf_name_len = env->GetArrayLength(jcn_ba); + const char* db_path = env->GetStringUTFChars(jdb_path, nullptr); + if(db_path == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } - //TODO(AR) do I need to make a copy of jco[i] ? + const jsize len_cols = env->GetArrayLength(jcolumn_names); + jlong* jco = env->GetLongArrayElements(jcolumn_options, nullptr); + if(jco == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jdb_path, db_path); + return nullptr; + } - std::string cf_name (reinterpret_cast(jcf_name), jcf_name_len); - rocksdb::ColumnFamilyOptions* cf_options = - reinterpret_cast(jco[i]); - column_families.push_back( - rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options)); + std::vector column_families; + jboolean has_exception = JNI_FALSE; + rocksdb::JniUtil::byteStrings( + env, + jcolumn_names, + [](const char* str_data, const size_t str_len) { + return std::string(str_data, str_len); + }, + [&jco, &column_families](size_t idx, std::string cf_name) { + rocksdb::ColumnFamilyOptions* cf_options = + reinterpret_cast(jco[idx]); + column_families.push_back( + rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options)); + }, + &has_exception); - env->ReleaseByteArrayElements(jcn_ba, jcf_name, JNI_ABORT); - env->DeleteLocalRef(jcn); - } env->ReleaseLongArrayElements(jcolumn_options, jco, JNI_ABORT); - std::vector handles; - rocksdb::DBWithTTL* db = nullptr; + if(has_exception == JNI_TRUE) { + // exception occured + env->ReleaseStringUTFChars(jdb_path, db_path); + return nullptr; + } std::vector ttl_values; - jint* jttlv = env->GetIntArrayElements(jttls, NULL); - jsize len_ttls = env->GetArrayLength(jttls); - for(int i = 0; i < len_ttls; i++) { + jint* jttlv = env->GetIntArrayElements(jttls, nullptr); + if(jttlv == nullptr) { + // exception thrown: OutOfMemoryError + env->ReleaseStringUTFChars(jdb_path, db_path); + return nullptr; + } + const jsize len_ttls = env->GetArrayLength(jttls); + for(jsize i = 0; i < len_ttls; i++) { ttl_values.push_back(jttlv[i]); } env->ReleaseIntArrayElements(jttls, jttlv, JNI_ABORT); + auto* opt = reinterpret_cast(jopt_handle); + std::vector handles; + rocksdb::DBWithTTL* db = nullptr; rocksdb::Status s = rocksdb::DBWithTTL::Open(*opt, db_path, column_families, &handles, &db, ttl_values, jread_only); + // we have now finished with db_path + env->ReleaseStringUTFChars(jdb_path, db_path); + // check if open operation was successful if (s.ok()) { - jsize resultsLen = 1 + len_cols; //db handle + column family handles + const jsize resultsLen = 1 + len_cols; //db handle + column family handles std::unique_ptr results = std::unique_ptr(new jlong[resultsLen]); results[0] = reinterpret_cast(db); @@ -104,7 +129,18 @@ jlongArray } jlongArray jresults = env->NewLongArray(resultsLen); + if(jresults == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + env->SetLongArrayRegion(jresults, 0, resultsLen, results.get()); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jresults); + return nullptr; + } + return jresults; } else { rocksdb::RocksDBExceptionJni::ThrowNew(env, s); @@ -120,18 +156,23 @@ jlongArray jlong Java_org_rocksdb_TtlDB_createColumnFamilyWithTtl( JNIEnv* env, jobject jobj, jlong jdb_handle, jbyteArray jcolumn_name, jlong jcolumn_options, jint jttl) { - rocksdb::ColumnFamilyHandle* handle; - auto* db_handle = reinterpret_cast(jdb_handle); - jbyte* cfname = env->GetByteArrayElements(jcolumn_name, 0); - const int len = env->GetArrayLength(jcolumn_name); + jbyte* cfname = env->GetByteArrayElements(jcolumn_name, nullptr); + if(cfname == nullptr) { + // exception thrown: OutOfMemoryError + return 0; + } + const jsize len = env->GetArrayLength(jcolumn_name); auto* cfOptions = reinterpret_cast(jcolumn_options); + auto* db_handle = reinterpret_cast(jdb_handle); + rocksdb::ColumnFamilyHandle* handle; rocksdb::Status s = db_handle->CreateColumnFamilyWithTtl( *cfOptions, std::string(reinterpret_cast(cfname), len), &handle, jttl); + env->ReleaseByteArrayElements(jcolumn_name, cfname, 0); if (s.ok()) { diff --git a/java/rocksjni/write_batch.cc b/java/rocksjni/write_batch.cc index 67d57963a..8e6ba6674 100644 --- a/java/rocksjni/write_batch.cc +++ b/java/rocksjni/write_batch.cc @@ -30,8 +30,7 @@ */ jlong Java_org_rocksdb_WriteBatch_newWriteBatch( JNIEnv* env, jclass jcls, jint jreserved_bytes) { - rocksdb::WriteBatch* wb = new rocksdb::WriteBatch( - static_cast(jreserved_bytes)); + auto* wb = new rocksdb::WriteBatch(static_cast(jreserved_bytes)); return reinterpret_cast(wb); } @@ -244,7 +243,9 @@ void Java_org_rocksdb_WriteBatch_iterate( */ void Java_org_rocksdb_WriteBatch_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* wb = reinterpret_cast(handle); + assert(wb != nullptr); + delete wb; } /* @@ -254,9 +255,8 @@ void Java_org_rocksdb_WriteBatch_disposeInternal( */ jlong Java_org_rocksdb_WriteBatch_00024Handler_createNewHandler0( JNIEnv* env, jobject jobj) { - const rocksdb::WriteBatchHandlerJniCallback* h = - new rocksdb::WriteBatchHandlerJniCallback(env, jobj); - return reinterpret_cast(h); + auto* wbjnic = new rocksdb::WriteBatchHandlerJniCallback(env, jobj); + return reinterpret_cast(wbjnic); } /* @@ -266,5 +266,8 @@ jlong Java_org_rocksdb_WriteBatch_00024Handler_createNewHandler0( */ void Java_org_rocksdb_WriteBatch_00024Handler_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { - delete reinterpret_cast(handle); + auto* wbjnic = + reinterpret_cast(handle); + assert(wbjnic != nullptr); + delete wbjnic; } diff --git a/java/rocksjni/write_batch_test.cc b/java/rocksjni/write_batch_test.cc index 9ba7a430c..2d83910a3 100644 --- a/java/rocksjni/write_batch_test.cc +++ b/java/rocksjni/write_batch_test.cc @@ -101,8 +101,18 @@ jbyteArray Java_org_rocksdb_WriteBatchTest_getContents( delete mem->Unref(); jbyteArray jstate = env->NewByteArray(static_cast(state.size())); + if(jstate == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + env->SetByteArrayRegion(jstate, 0, static_cast(state.size()), reinterpret_cast(state.c_str())); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jstate); + return nullptr; + } return jstate; } diff --git a/java/rocksjni/write_batch_with_index.cc b/java/rocksjni/write_batch_with_index.cc index 9e2f4a00a..53cf60584 100644 --- a/java/rocksjni/write_batch_with_index.cc +++ b/java/rocksjni/write_batch_with_index.cc @@ -19,7 +19,7 @@ */ jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__( JNIEnv* env, jclass jcls) { - rocksdb::WriteBatchWithIndex* wbwi = new rocksdb::WriteBatchWithIndex(); + auto* wbwi = new rocksdb::WriteBatchWithIndex(); return reinterpret_cast(wbwi); } @@ -30,9 +30,9 @@ jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__( */ jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__Z( JNIEnv* env, jclass jcls, jboolean joverwrite_key) { - rocksdb::WriteBatchWithIndex* wbwi = + auto* wbwi = new rocksdb::WriteBatchWithIndex(rocksdb::BytewiseComparator(), 0, - static_cast(joverwrite_key)); + static_cast(joverwrite_key)); return reinterpret_cast(wbwi); } @@ -44,10 +44,10 @@ jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__Z( jlong Java_org_rocksdb_WriteBatchWithIndex_newWriteBatchWithIndex__JIZ( JNIEnv* env, jclass jcls, jlong jfallback_index_comparator_handle, jint jreserved_bytes, jboolean joverwrite_key) { - rocksdb::WriteBatchWithIndex* wbwi = + auto* wbwi = new rocksdb::WriteBatchWithIndex( - reinterpret_cast(jfallback_index_comparator_handle), - static_cast(jreserved_bytes), static_cast(joverwrite_key)); + reinterpret_cast(jfallback_index_comparator_handle), + static_cast(jreserved_bytes), static_cast(joverwrite_key)); return reinterpret_cast(wbwi); } @@ -241,7 +241,7 @@ void Java_org_rocksdb_WriteBatchWithIndex_rollbackToSavePoint0( jlong Java_org_rocksdb_WriteBatchWithIndex_iterator0( JNIEnv* env, jobject jobj, jlong jwbwi_handle) { auto* wbwi = reinterpret_cast(jwbwi_handle); - rocksdb::WBWIIterator* wbwi_iterator = wbwi->NewIterator(); + auto* wbwi_iterator = wbwi->NewIterator(); return reinterpret_cast(wbwi_iterator); } @@ -254,7 +254,7 @@ jlong Java_org_rocksdb_WriteBatchWithIndex_iterator1( JNIEnv* env, jobject jobj, jlong jwbwi_handle, jlong jcf_handle) { auto* wbwi = reinterpret_cast(jwbwi_handle); auto* cf_handle = reinterpret_cast(jcf_handle); - rocksdb::WBWIIterator* wbwi_iterator = wbwi->NewIterator(cf_handle); + auto* wbwi_iterator = wbwi->NewIterator(cf_handle); return reinterpret_cast(wbwi_iterator); } @@ -362,6 +362,7 @@ jbyteArray Java_org_rocksdb_WriteBatchWithIndex_getFromBatchAndDB__JJJ_3BIJ( void Java_org_rocksdb_WriteBatchWithIndex_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { auto* wbwi = reinterpret_cast(handle); + assert(wbwi != nullptr); delete wbwi; } @@ -375,6 +376,7 @@ void Java_org_rocksdb_WriteBatchWithIndex_disposeInternal( void Java_org_rocksdb_WBWIRocksIterator_disposeInternal( JNIEnv* env, jobject jobj, jlong handle) { auto* it = reinterpret_cast(handle); + assert(it != nullptr); delete it; } @@ -437,7 +439,12 @@ void Java_org_rocksdb_WBWIRocksIterator_seek0( JNIEnv* env, jobject jobj, jlong handle, jbyteArray jtarget, jint jtarget_len) { auto* it = reinterpret_cast(handle); - jbyte* target = env->GetByteArrayElements(jtarget, 0); + jbyte* target = env->GetByteArrayElements(jtarget, nullptr); + if(target == nullptr) { + // exception thrown: OutOfMemoryError + return; + } + rocksdb::Slice target_slice( reinterpret_cast(target), jtarget_len); @@ -497,26 +504,41 @@ jlongArray Java_org_rocksdb_WBWIRocksIterator_entry1( results[0] = 0x0; } - //TODO(AR) do we leak buf and value_buf? + // key_slice and value_slice will be freed by org.rocksdb.DirectSlice#close - //set the pointer to the key slice - char* buf = new char[we.key.size()]; - memcpy(buf, we.key.data(), we.key.size()); - auto* key_slice = new rocksdb::Slice(buf, we.key.size()); + auto* key_slice = new rocksdb::Slice(we.key.data(), we.key.size()); results[1] = reinterpret_cast(key_slice); - - //set the pointer to the value slice - if (we.type == rocksdb::kDeleteRecord || we.type == rocksdb::kLogDataRecord) { + if (we.type == rocksdb::kDeleteRecord + || we.type == rocksdb::kLogDataRecord) { // set native handle of value slice to null if no value available results[2] = 0; } else { - char* value_buf = new char[we.value.size()]; - memcpy(value_buf, we.value.data(), we.value.size()); - auto* value_slice = new rocksdb::Slice(value_buf, we.value.size()); + auto* value_slice = new rocksdb::Slice(we.value.data(), we.value.size()); results[2] = reinterpret_cast(value_slice); } jlongArray jresults = env->NewLongArray(3); + if(jresults == nullptr) { + // exception thrown: OutOfMemoryError + if(results[2] != 0) { + auto* value_slice = reinterpret_cast(results[2]); + delete value_slice; + } + delete key_slice; + return nullptr; + } + env->SetLongArrayRegion(jresults, 0, 3, results); + if(env->ExceptionCheck()) { + // exception thrown: ArrayIndexOutOfBoundsException + env->DeleteLocalRef(jresults); + if(results[2] != 0) { + auto* value_slice = reinterpret_cast(results[2]); + delete value_slice; + } + delete key_slice; + return nullptr; + } + return jresults; } diff --git a/java/rocksjni/writebatchhandlerjnicallback.cc b/java/rocksjni/writebatchhandlerjnicallback.cc index b25236518..d24419176 100644 --- a/java/rocksjni/writebatchhandlerjnicallback.cc +++ b/java/rocksjni/writebatchhandlerjnicallback.cc @@ -16,69 +16,202 @@ WriteBatchHandlerJniCallback::WriteBatchHandlerJniCallback( // 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; + } m_jPutMethodId = WriteBatchHandlerJni::getPutMethodId(env); + if(m_jPutMethodId == nullptr) { + // exception thrown + return; + } + m_jMergeMethodId = WriteBatchHandlerJni::getMergeMethodId(env); + if(m_jMergeMethodId == nullptr) { + // exception thrown + return; + } + m_jDeleteMethodId = WriteBatchHandlerJni::getDeleteMethodId(env); + if(m_jDeleteMethodId == nullptr) { + // exception thrown + return; + } + m_jLogDataMethodId = WriteBatchHandlerJni::getLogDataMethodId(env); + if(m_jLogDataMethodId == nullptr) { + // exception thrown + return; + } + m_jContinueMethodId = WriteBatchHandlerJni::getContinueMethodId(env); + if(m_jContinueMethodId == nullptr) { + // exception thrown + return; + } } void WriteBatchHandlerJniCallback::Put(const Slice& key, const Slice& value) { const jbyteArray j_key = sliceToJArray(key); + if(j_key == nullptr) { + // exception thrown + if(m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + return; + } + const jbyteArray j_value = sliceToJArray(value); + if(j_value == nullptr) { + // exception thrown + if(m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } + return; + } m_env->CallVoidMethod( m_jWriteBatchHandler, m_jPutMethodId, j_key, j_value); + if(m_env->ExceptionCheck()) { + // exception thrown + m_env->ExceptionDescribe(); + if(j_value != nullptr) { + m_env->DeleteLocalRef(j_value); + } + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } + return; + } - m_env->DeleteLocalRef(j_value); - m_env->DeleteLocalRef(j_key); + if(j_value != nullptr) { + m_env->DeleteLocalRef(j_value); + } + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } } void WriteBatchHandlerJniCallback::Merge(const Slice& key, const Slice& value) { const jbyteArray j_key = sliceToJArray(key); + if(j_key == nullptr) { + // exception thrown + if(m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + return; + } + const jbyteArray j_value = sliceToJArray(value); + if(j_value == nullptr) { + // exception thrown + if(m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } + return; + } m_env->CallVoidMethod( m_jWriteBatchHandler, m_jMergeMethodId, j_key, j_value); + if(m_env->ExceptionCheck()) { + // exception thrown + m_env->ExceptionDescribe(); + if(j_value != nullptr) { + m_env->DeleteLocalRef(j_value); + } + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } + return; + } - m_env->DeleteLocalRef(j_value); - m_env->DeleteLocalRef(j_key); + if(j_value != nullptr) { + m_env->DeleteLocalRef(j_value); + } + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } } void WriteBatchHandlerJniCallback::Delete(const Slice& key) { const jbyteArray j_key = sliceToJArray(key); + if(j_key == nullptr) { + // exception thrown + if(m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + return; + } m_env->CallVoidMethod( m_jWriteBatchHandler, m_jDeleteMethodId, j_key); + if(m_env->ExceptionCheck()) { + // exception thrown + m_env->ExceptionDescribe(); + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } + return; + } - m_env->DeleteLocalRef(j_key); + if(j_key != nullptr) { + m_env->DeleteLocalRef(j_key); + } } void WriteBatchHandlerJniCallback::LogData(const Slice& blob) { const jbyteArray j_blob = sliceToJArray(blob); + if(j_blob == nullptr) { + // exception thrown + if(m_env->ExceptionCheck()) { + m_env->ExceptionDescribe(); + } + return; + } m_env->CallVoidMethod( m_jWriteBatchHandler, m_jLogDataMethodId, j_blob); + if(m_env->ExceptionCheck()) { + // exception thrown + m_env->ExceptionDescribe(); + if(j_blob != nullptr) { + m_env->DeleteLocalRef(j_blob); + } + return; + } - m_env->DeleteLocalRef(j_blob); + if(j_blob != nullptr) { + m_env->DeleteLocalRef(j_blob); + } } bool WriteBatchHandlerJniCallback::Continue() { jboolean jContinue = m_env->CallBooleanMethod( m_jWriteBatchHandler, m_jContinueMethodId); + if(m_env->ExceptionCheck()) { + // exception thrown + m_env->ExceptionDescribe(); + } return static_cast(jContinue == JNI_TRUE); } @@ -89,16 +222,36 @@ bool WriteBatchHandlerJniCallback::Continue() { * When calling this function * you must remember to call env->DeleteLocalRef * on the result after you have finished with it + * + * @param s A Slice to convery to a Java byte array + * + * @return A reference to a Java byte array, or a nullptr if an + * exception occurs */ jbyteArray WriteBatchHandlerJniCallback::sliceToJArray(const Slice& s) { jbyteArray ja = m_env->NewByteArray(static_cast(s.size())); + if(ja == nullptr) { + // exception thrown: OutOfMemoryError + return nullptr; + } + m_env->SetByteArrayRegion( ja, 0, static_cast(s.size()), reinterpret_cast(s.data())); + if(m_env->ExceptionCheck()) { + if(ja != nullptr) { + m_env->DeleteLocalRef(ja); + } + // exception thrown: ArrayIndexOutOfBoundsException + return nullptr; + } + return ja; } WriteBatchHandlerJniCallback::~WriteBatchHandlerJniCallback() { - m_env->DeleteGlobalRef(m_jWriteBatchHandler); + if(m_jWriteBatchHandler != nullptr) { + m_env->DeleteGlobalRef(m_jWriteBatchHandler); + } } } // namespace rocksdb diff --git a/java/samples/src/main/java/RocksDBColumnFamilySample.java b/java/samples/src/main/java/RocksDBColumnFamilySample.java index 7dd0de9cc..73170d91f 100644 --- a/java/samples/src/main/java/RocksDBColumnFamilySample.java +++ b/java/samples/src/main/java/RocksDBColumnFamilySample.java @@ -13,13 +13,14 @@ public class RocksDBColumnFamilySample { RocksDB.loadLibrary(); } - public static void main(String[] args) throws RocksDBException { + public static void main(final String[] args) throws RocksDBException { if (args.length < 1) { System.out.println( "usage: RocksDBColumnFamilySample db_path"); - return; + System.exit(-1); } - String db_path = args[0]; + + final String db_path = args[0]; System.out.println("RocksDBColumnFamilySample"); try(final Options options = new Options().setCreateIfMissing(true); @@ -54,8 +55,6 @@ public class RocksDBColumnFamilySample { // put and get from non-default column family db.put(columnFamilyHandles.get(0), new WriteOptions(), "key".getBytes(), "value".getBytes()); - String value = new String(db.get(columnFamilyHandles.get(0), - "key".getBytes())); // atomic write try (final WriteBatch wb = new WriteBatch()) { diff --git a/java/samples/src/main/java/RocksDBSample.java b/java/samples/src/main/java/RocksDBSample.java index a1536cb9d..97007aa7f 100644 --- a/java/samples/src/main/java/RocksDBSample.java +++ b/java/samples/src/main/java/RocksDBSample.java @@ -12,31 +12,31 @@ import java.util.ArrayList; import org.rocksdb.*; import org.rocksdb.util.SizeUnit; -import java.io.IOException; - public class RocksDBSample { static { RocksDB.loadLibrary(); } - public static void main(String[] args) { + public static void main(final String[] args) { if (args.length < 1) { System.out.println("usage: RocksDBSample db_path"); - return; + System.exit(-1); } - String db_path = args[0]; - String db_path_not_found = db_path + "_not_found"; + + final String db_path = args[0]; + final String db_path_not_found = db_path + "_not_found"; System.out.println("RocksDBSample"); try (final Options options = new Options(); final Filter bloomFilter = new BloomFilter(10); final ReadOptions readOptions = new ReadOptions() - .setFillCache(false)) { + .setFillCache(false); + final RateLimiter rateLimiter = new RateLimiter(10000000,10000, 10)) { try (final RocksDB db = RocksDB.open(options, db_path_not_found)) { assert (false); - } catch (RocksDBException e) { - System.out.format("caught the expected exception -- %s\n", e); + } catch (final RocksDBException e) { + System.out.format("Caught the expected exception -- %s\n", e); } try { @@ -47,11 +47,11 @@ public class RocksDBSample { .setMaxBackgroundCompactions(10) .setCompressionType(CompressionType.SNAPPY_COMPRESSION) .setCompactionStyle(CompactionStyle.UNIVERSAL); - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { assert (false); } - Statistics stats = options.statisticsPtr(); + final Statistics stats = options.statisticsPtr(); assert (options.createIfMissing() == true); assert (options.writeBufferSize() == 8 * SizeUnit.KB); @@ -85,9 +85,7 @@ public class RocksDBSample { options.setAllowMmapReads(true); assert (options.tableFactoryName().equals("PlainTable")); - options.setRateLimiterConfig(new GenericRateLimiterConfig(10000000, - 10000, 10)); - options.setRateLimiterConfig(new GenericRateLimiterConfig(10000000)); + options.setRateLimiter(rateLimiter); final BlockBasedTableConfig table_options = new BlockBasedTableConfig(); table_options.setBlockCacheSize(64 * SizeUnit.KB) @@ -114,12 +112,14 @@ public class RocksDBSample { try (final RocksDB db = RocksDB.open(options, db_path)) { db.put("hello".getBytes(), "world".getBytes()); - byte[] value = db.get("hello".getBytes()); + + final byte[] value = db.get("hello".getBytes()); assert ("world".equals(new String(value))); - String str = db.getProperty("rocksdb.stats"); + + final String str = db.getProperty("rocksdb.stats"); assert (str != null && !str.equals("")); - } catch (RocksDBException e) { - System.out.format("[ERROR] caught the unexpceted exception -- %s\n", e); + } catch (final RocksDBException e) { + System.out.format("[ERROR] caught the unexpected exception -- %s\n", e); assert (false); } @@ -174,8 +174,8 @@ public class RocksDBSample { value = db.get(readOptions, "world".getBytes()); assert (value == null); - byte[] testKey = "asdf".getBytes(); - byte[] testValue = + final byte[] testKey = "asdf".getBytes(); + final byte[] testValue = "asdfghjkl;'?> insufficientArray.length); @@ -220,21 +220,21 @@ public class RocksDBSample { } try { - for (TickerType statsType : TickerType.values()) { + for (final TickerType statsType : TickerType.values()) { stats.getTickerCount(statsType); } System.out.println("getTickerCount() passed."); - } catch (Exception e) { + } catch (final Exception e) { System.out.println("Failed in call to getTickerCount()"); assert (false); //Should never reach here. } try { - for (HistogramType histogramType : HistogramType.values()) { + for (final HistogramType histogramType : HistogramType.values()) { HistogramData data = stats.getHistogramData(histogramType); } System.out.println("getHistogramData() passed."); - } catch (Exception e) { + } catch (final Exception e) { System.out.println("Failed in call to getHistogramData()"); assert (false); //Should never reach here. } @@ -283,16 +283,16 @@ public class RocksDBSample { Map values = db.multiGet(keys); assert (values.size() == keys.size()); - for (byte[] value1 : values.values()) { + for (final byte[] value1 : values.values()) { assert (value1 != null); } values = db.multiGet(new ReadOptions(), keys); assert (values.size() == keys.size()); - for (byte[] value1 : values.values()) { + for (final byte[] value1 : values.values()) { assert (value1 != null); } - } catch (RocksDBException e) { + } catch (final RocksDBException e) { System.err.println(e); } } diff --git a/java/src/main/java/org/rocksdb/AbstractSlice.java b/java/src/main/java/org/rocksdb/AbstractSlice.java index b6335a5f4..75e1f393f 100644 --- a/java/src/main/java/org/rocksdb/AbstractSlice.java +++ b/java/src/main/java/org/rocksdb/AbstractSlice.java @@ -57,6 +57,20 @@ public abstract class AbstractSlice extends RocksMutableObject { */ protected abstract T data0(long handle); + /** + * Drops the specified {@code n} + * number of bytes from the start + * of the backing slice + * + * @param n The number of bytes to drop + */ + public abstract void removePrefix(final int n); + + /** + * Clears the backing slice + */ + public abstract void clear(); + /** * Return the length (in bytes) of the data. * diff --git a/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java b/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java index 5d4c63485..9d2ed82cf 100644 --- a/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java +++ b/java/src/main/java/org/rocksdb/ColumnFamilyOptions.java @@ -143,7 +143,7 @@ public class ColumnFamilyOptions extends RocksObject @Override public ColumnFamilyOptions setMergeOperator( final MergeOperator mergeOperator) { - setMergeOperator(nativeHandle_, mergeOperator.newMergeOperatorHandle()); + setMergeOperator(nativeHandle_, mergeOperator.nativeHandle_); return this; } diff --git a/java/src/main/java/org/rocksdb/DBOptions.java b/java/src/main/java/org/rocksdb/DBOptions.java index 22bde7147..635867f7a 100644 --- a/java/src/main/java/org/rocksdb/DBOptions.java +++ b/java/src/main/java/org/rocksdb/DBOptions.java @@ -134,15 +134,6 @@ public class DBOptions extends RocksObject implements DBOptionsInterface { return paranoidChecks(nativeHandle_); } - @Override - public DBOptions setRateLimiterConfig( - final RateLimiterConfig config) { - assert(isOwningHandle()); - rateLimiterConfig_ = config; - setOldRateLimiter(nativeHandle_, config.newRateLimiterHandle()); - return this; - } - @Override public DBOptions setRateLimiter(final RateLimiter rateLimiter) { assert(isOwningHandle()); @@ -650,9 +641,6 @@ public long delayedWriteRate(){ private native void setParanoidChecks( long handle, boolean paranoidChecks); private native boolean paranoidChecks(long handle); - @Deprecated - private native void setOldRateLimiter(long handle, - long rateLimiterHandle); private native void setRateLimiter(long handle, long rateLimiterHandle); private native void setLogger(long handle, @@ -750,6 +738,5 @@ public long delayedWriteRate(){ private native long delayedWriteRate(long handle); int numShardBits_; - RateLimiterConfig rateLimiterConfig_; RateLimiter rateLimiter_; } diff --git a/java/src/main/java/org/rocksdb/DBOptionsInterface.java b/java/src/main/java/org/rocksdb/DBOptionsInterface.java index e762119d5..1e7b4fda4 100644 --- a/java/src/main/java/org/rocksdb/DBOptionsInterface.java +++ b/java/src/main/java/org/rocksdb/DBOptionsInterface.java @@ -118,18 +118,6 @@ public interface DBOptionsInterface { */ boolean paranoidChecks(); - /** - * Use to control write rate of flush and compaction. Flush has higher - * priority than compaction. Rate limiting is disabled if nullptr. - * Default: nullptr - * - * @param config rate limiter config. - * @return the instance of the current Object. - * @deprecated See: {@link #setRateLimiter(RateLimiter)}. - */ - @Deprecated - Object setRateLimiterConfig(RateLimiterConfig config); - /** * Use to control write rate of flush and compaction. Flush has higher * priority than compaction. Rate limiting is disabled if nullptr. diff --git a/java/src/main/java/org/rocksdb/DirectSlice.java b/java/src/main/java/org/rocksdb/DirectSlice.java index 8f96eb49f..6a7654ffe 100644 --- a/java/src/main/java/org/rocksdb/DirectSlice.java +++ b/java/src/main/java/org/rocksdb/DirectSlice.java @@ -18,6 +18,13 @@ import java.nio.ByteBuffer; public class DirectSlice extends AbstractSlice { public final static DirectSlice NONE = new DirectSlice(); + /** + * Indicates whether we have to free the memory pointed to by the Slice + */ + private final boolean internalBuffer; + private volatile boolean cleared = false; + private volatile long internalBufferOffset = 0; + /** * Called from JNI to construct a new Java DirectSlice * without an underlying C++ object set @@ -32,6 +39,7 @@ public class DirectSlice extends AbstractSlice { */ DirectSlice() { super(); + this.internalBuffer = false; } /** @@ -43,6 +51,7 @@ public class DirectSlice extends AbstractSlice { */ public DirectSlice(final String str) { super(createNewSliceFromString(str)); + this.internalBuffer = true; } /** @@ -55,6 +64,7 @@ public class DirectSlice extends AbstractSlice { */ public DirectSlice(final ByteBuffer data, final int length) { super(createNewDirectSlice0(ensureDirect(data), length)); + this.internalBuffer = false; } /** @@ -66,12 +76,13 @@ public class DirectSlice extends AbstractSlice { */ public DirectSlice(final ByteBuffer data) { super(createNewDirectSlice1(ensureDirect(data))); + this.internalBuffer = false; } private static ByteBuffer ensureDirect(final ByteBuffer data) { - // TODO(AR) consider throwing a checked exception, as if it's not direct - // this can SIGSEGV - assert(data.isDirect()); + if(!data.isDirect()) { + throw new IllegalArgumentException("The ByteBuffer must be direct"); + } return data; } @@ -83,26 +94,29 @@ public class DirectSlice extends AbstractSlice { * * @return the requested byte */ - public byte get(int offset) { + public byte get(final int offset) { return get0(getNativeHandle(), offset); } - /** - * Clears the backing slice - */ + @Override public void clear() { - clear0(getNativeHandle()); + clear0(getNativeHandle(), !cleared && internalBuffer, internalBufferOffset); + cleared = true; } - /** - * Drops the specified {@code n} - * number of bytes from the start - * of the backing slice - * - * @param n The number of bytes to drop - */ + @Override public void removePrefix(final int n) { removePrefix0(getNativeHandle(), n); + this.internalBufferOffset += n; + } + + @Override + protected void disposeInternal() { + final long nativeHandle = getNativeHandle(); + if(!cleared && internalBuffer) { + disposeInternalBuf(nativeHandle, internalBufferOffset); + } + disposeInternal(nativeHandle); } private native static long createNewDirectSlice0(final ByteBuffer data, @@ -110,6 +124,9 @@ public class DirectSlice extends AbstractSlice { private native static long createNewDirectSlice1(final ByteBuffer data); @Override protected final native ByteBuffer data0(long handle); private native byte get0(long handle, int offset); - private native void clear0(long handle); + private native void clear0(long handle, boolean internalBuffer, + long internalBufferOffset); private native void removePrefix0(long handle, int length); + private native void disposeInternalBuf(final long handle, + long internalBufferOffset); } diff --git a/java/src/main/java/org/rocksdb/EnvOptions.java b/java/src/main/java/org/rocksdb/EnvOptions.java index 2fabf4b7c..0ef553271 100644 --- a/java/src/main/java/org/rocksdb/EnvOptions.java +++ b/java/src/main/java/org/rocksdb/EnvOptions.java @@ -134,15 +134,15 @@ public class EnvOptions extends RocksObject { return writableFileMaxBufferSize(nativeHandle_); } - public EnvOptions setRateLimiterConfig(final RateLimiterConfig rateLimiterConfig) { - this.rateLimiterConfig = rateLimiterConfig; - setRateLimiter(nativeHandle_, rateLimiterConfig.newRateLimiterHandle()); + public EnvOptions setRateLimiter(final RateLimiter rateLimiter) { + this.rateLimiter = rateLimiter; + setRateLimiter(nativeHandle_, rateLimiter.nativeHandle_); return this; } - public RateLimiterConfig rateLimiterConfig() { + public RateLimiter rateLimiter() { assert(isOwningHandle()); - return rateLimiterConfig; + return rateLimiter; } private native static long newEnvOptions(); @@ -203,5 +203,5 @@ public class EnvOptions extends RocksObject { private native void setRateLimiter(final long handle, final long rateLimiterHandle); - private RateLimiterConfig rateLimiterConfig; + private RateLimiter rateLimiter; } diff --git a/java/src/main/java/org/rocksdb/GenericRateLimiterConfig.java b/java/src/main/java/org/rocksdb/GenericRateLimiterConfig.java deleted file mode 100644 index a15aea8fe..000000000 --- a/java/src/main/java/org/rocksdb/GenericRateLimiterConfig.java +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -package org.rocksdb; - -/** - * Config for rate limiter, which is used to control write rate of flush and - * compaction. - * - * @see RateLimiterConfig - * @deprecated obsolete. See: {@link org.rocksdb.RateLimiter}. - */ -@Deprecated -public class GenericRateLimiterConfig extends RateLimiterConfig { - private static final long DEFAULT_REFILL_PERIOD_MICROS = (100 * 1000); - private static final int DEFAULT_FAIRNESS = 10; - - /** - * GenericRateLimiterConfig constructor - * - * @param rateBytesPerSecond this is the only parameter you want to set - * most of the time. It controls the total write rate of compaction - * and flush in bytes per second. Currently, RocksDB does not enforce - * rate limit for anything other than flush and compaction, e.g. write to WAL. - * @param refillPeriodMicros this controls how often tokens are refilled. For example, - * when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to - * 100ms, then 1MB is refilled every 100ms internally. Larger value can lead to - * burstier writes while smaller value introduces more CPU overhead. - * The default should work for most cases. - * @param fairness RateLimiter accepts high-pri requests and low-pri requests. - * A low-pri request is usually blocked in favor of hi-pri request. Currently, - * RocksDB assigns low-pri to request from compaction and high-pri to request - * from flush. Low-pri requests can get blocked if flush requests come in - * continuously. This fairness parameter grants low-pri requests permission by - * fairness chance even though high-pri requests exist to avoid starvation. - * You should be good by leaving it at default 10. - */ - public GenericRateLimiterConfig(final long rateBytesPerSecond, - final long refillPeriodMicros, final int fairness) { - rateBytesPerSecond_ = rateBytesPerSecond; - refillPeriodMicros_ = refillPeriodMicros; - fairness_ = fairness; - } - - /** - * GenericRateLimiterConfig constructor - * - * @param rateBytesPerSecond this is the only parameter you want to set - * most of the time. It controls the total write rate of compaction - * and flush in bytes per second. Currently, RocksDB does not enforce - * rate limit for anything other than flush and compaction, e.g. write to WAL. - */ - public GenericRateLimiterConfig(final long rateBytesPerSecond) { - this(rateBytesPerSecond, DEFAULT_REFILL_PERIOD_MICROS, DEFAULT_FAIRNESS); - } - - @Override protected long newRateLimiterHandle() { - return newRateLimiterHandle(rateBytesPerSecond_, refillPeriodMicros_, - fairness_); - } - - private native long newRateLimiterHandle(long rateBytesPerSecond, - long refillPeriodMicros, int fairness); - private final long rateBytesPerSecond_; - private final long refillPeriodMicros_; - private final int fairness_; -} diff --git a/java/src/main/java/org/rocksdb/MergeOperator.java b/java/src/main/java/org/rocksdb/MergeOperator.java index 3abea024d..2cc1a1f3a 100644 --- a/java/src/main/java/org/rocksdb/MergeOperator.java +++ b/java/src/main/java/org/rocksdb/MergeOperator.java @@ -10,6 +10,8 @@ package org.rocksdb; * two merge operands held under the same key in order to obtain a single * value. */ -public interface MergeOperator { - long newMergeOperatorHandle(); +public abstract class MergeOperator extends RocksObject { + protected MergeOperator(final long nativeHandle) { + super(nativeHandle); + } } diff --git a/java/src/main/java/org/rocksdb/MutableColumnFamilyOptions.java b/java/src/main/java/org/rocksdb/MutableColumnFamilyOptions.java index 7dc477908..534f57697 100644 --- a/java/src/main/java/org/rocksdb/MutableColumnFamilyOptions.java +++ b/java/src/main/java/org/rocksdb/MutableColumnFamilyOptions.java @@ -49,6 +49,10 @@ public class MutableColumnFamilyOptions { * For int[] values, each int should be separated by a comma, e.g. * * key1=value1;intArrayKey1=1,2,3 + * + * @param str The string representation of the mutable column family options + * + * @return A builder for the mutable column family options */ public static MutableColumnFamilyOptionsBuilder parse(final String str) { Objects.requireNonNull(str); diff --git a/java/src/main/java/org/rocksdb/Options.java b/java/src/main/java/org/rocksdb/Options.java index 924bad482..05699837d 100644 --- a/java/src/main/java/org/rocksdb/Options.java +++ b/java/src/main/java/org/rocksdb/Options.java @@ -188,7 +188,7 @@ public class Options extends RocksObject @Override public Options setMergeOperator(final MergeOperator mergeOperator) { - setMergeOperator(nativeHandle_, mergeOperator.newMergeOperatorHandle()); + setMergeOperator(nativeHandle_, mergeOperator.nativeHandle_); return this; } @@ -683,13 +683,6 @@ public class Options extends RocksObject return this; } - @Override - public Options setRateLimiterConfig(final RateLimiterConfig config) { - rateLimiterConfig_ = config; - setOldRateLimiter(nativeHandle_, config.newRateLimiterHandle()); - return this; - } - @Override public Options setRateLimiter(final RateLimiter rateLimiter) { assert(isOwningHandle()); @@ -1202,9 +1195,6 @@ public class Options extends RocksObject private native void setParanoidChecks( long handle, boolean paranoidChecks); private native boolean paranoidChecks(long handle); - @Deprecated - private native void setOldRateLimiter(long handle, - long rateLimiterHandle); private native void setRateLimiter(long handle, long rateLimiterHandle); private native void setLogger(long handle, @@ -1436,7 +1426,6 @@ public class Options extends RocksObject Env env_; MemTableConfig memTableConfig_; TableFormatConfig tableFormatConfig_; - RateLimiterConfig rateLimiterConfig_; RateLimiter rateLimiter_; AbstractComparator> comparator_; } diff --git a/java/src/main/java/org/rocksdb/RateLimiterConfig.java b/java/src/main/java/org/rocksdb/RateLimiterConfig.java deleted file mode 100644 index 3ec5cd914..000000000 --- a/java/src/main/java/org/rocksdb/RateLimiterConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. -package org.rocksdb; - -/** - * Config for rate limiter, which is used to control write rate of flush and - * compaction. - * - * @deprecated obsolete. See: {@link org.rocksdb.RateLimiter}. - */ -@Deprecated -public abstract class RateLimiterConfig { - /** - * This function should only be called by - * {@link org.rocksdb.DBOptions#setRateLimiter(long, long)}, which will - * create a c++ shared-pointer to the c++ {@code RateLimiter} that is associated - * with a Java RateLimiterConfig. - * - * @see org.rocksdb.DBOptions#setRateLimiter(long, long) - * - * @return native handle address to rate limiter instance. - */ - abstract protected long newRateLimiterHandle(); -} diff --git a/java/src/main/java/org/rocksdb/RocksDB.java b/java/src/main/java/org/rocksdb/RocksDB.java index 64c05afc1..2a4beefd1 100644 --- a/java/src/main/java/org/rocksdb/RocksDB.java +++ b/java/src/main/java/org/rocksdb/RocksDB.java @@ -520,11 +520,11 @@ public class RocksDB extends RocksObject { * to make this lighter weight is to avoid doing any IOs. * * @param key byte array of a key to search for - * @param value StringBuffer instance which is a out parameter if a value is + * @param value StringBuilder instance which is a out parameter if a value is * found in block-cache. * @return boolean value indicating if key does not exist or might exist. */ - public boolean keyMayExist(final byte[] key, final StringBuffer value) { + public boolean keyMayExist(final byte[] key, final StringBuilder value) { return keyMayExist(nativeHandle_, key, 0, key.length, value); } @@ -537,12 +537,12 @@ public class RocksDB extends RocksObject { * * @param columnFamilyHandle {@link ColumnFamilyHandle} instance * @param key byte array of a key to search for - * @param value StringBuffer instance which is a out parameter if a value is + * @param value StringBuilder instance which is a out parameter if a value is * found in block-cache. * @return boolean value indicating if key does not exist or might exist. */ public boolean keyMayExist(final ColumnFamilyHandle columnFamilyHandle, - final byte[] key, final StringBuffer value) { + final byte[] key, final StringBuilder value) { return keyMayExist(nativeHandle_, key, 0, key.length, columnFamilyHandle.nativeHandle_, value); } @@ -556,12 +556,12 @@ public class RocksDB extends RocksObject { * * @param readOptions {@link ReadOptions} instance * @param key byte array of a key to search for - * @param value StringBuffer instance which is a out parameter if a value is + * @param value StringBuilder instance which is a out parameter if a value is * found in block-cache. * @return boolean value indicating if key does not exist or might exist. */ public boolean keyMayExist(final ReadOptions readOptions, - final byte[] key, final StringBuffer value) { + final byte[] key, final StringBuilder value) { return keyMayExist(nativeHandle_, readOptions.nativeHandle_, key, 0, key.length, value); } @@ -576,13 +576,13 @@ public class RocksDB extends RocksObject { * @param readOptions {@link ReadOptions} instance * @param columnFamilyHandle {@link ColumnFamilyHandle} instance * @param key byte array of a key to search for - * @param value StringBuffer instance which is a out parameter if a value is + * @param value StringBuilder instance which is a out parameter if a value is * found in block-cache. * @return boolean value indicating if key does not exist or might exist. */ public boolean keyMayExist(final ReadOptions readOptions, final ColumnFamilyHandle columnFamilyHandle, final byte[] key, - final StringBuffer value) { + final StringBuilder value) { return keyMayExist(nativeHandle_, readOptions.nativeHandle_, key, 0, key.length, columnFamilyHandle.nativeHandle_, value); @@ -685,6 +685,9 @@ public class RocksDB extends RocksObject { columnFamilyHandle.nativeHandle_); } + // TODO(AR) we should improve the #get() API, returning -1 (RocksDB.NOT_FOUND) is not very nice + // when we could communicate better status into, also the C++ code show that -2 could be returned + /** * Get the value associated with the specified key within column family* * @param key the key to retrieve the value. @@ -1917,6 +1920,8 @@ public class RocksDB extends RocksObject { * This function will wait until all currently running background processes * finish. After it returns, no background process will be run until * {@link #continueBackgroundWork()} is called + * + * @throws RocksDBException If an error occurs when pausing background work */ public void pauseBackgroundWork() throws RocksDBException { pauseBackgroundWork(nativeHandle_); @@ -1925,6 +1930,8 @@ public class RocksDB extends RocksObject { /** * Resumes backround work which was suspended by * previously calling {@link #pauseBackgroundWork()} + * + * @throws RocksDBException If an error occurs when resuming background work */ public void continueBackgroundWork() throws RocksDBException { continueBackgroundWork(nativeHandle_); @@ -2182,17 +2189,17 @@ public class RocksDB extends RocksObject { long wbwiHandle) throws RocksDBException; protected native boolean keyMayExist(final long handle, final byte[] key, final int keyOffset, final int keyLength, - final StringBuffer stringBuffer); + final StringBuilder stringBuilder); protected native boolean keyMayExist(final long handle, final byte[] key, final int keyOffset, final int keyLength, final long cfHandle, - final StringBuffer stringBuffer); + final StringBuilder stringBuilder); protected native boolean keyMayExist(final long handle, final long optionsHandle, final byte[] key, final int keyOffset, - final int keyLength, final StringBuffer stringBuffer); + final int keyLength, final StringBuilder stringBuilder); protected native boolean keyMayExist(final long handle, final long optionsHandle, final byte[] key, final int keyOffset, final int keyLength, final long cfHandle, - final StringBuffer stringBuffer); + final StringBuilder stringBuilder); protected native void merge(long handle, byte[] key, int keyOffset, int keyLength, byte[] value, int valueOffset, int valueLength) throws RocksDBException; diff --git a/java/src/main/java/org/rocksdb/RocksMutableObject.java b/java/src/main/java/org/rocksdb/RocksMutableObject.java index 9b9e576dc..e167b27b1 100644 --- a/java/src/main/java/org/rocksdb/RocksMutableObject.java +++ b/java/src/main/java/org/rocksdb/RocksMutableObject.java @@ -30,6 +30,24 @@ public abstract class RocksMutableObject extends AbstractNativeReference { this.owningHandle_ = true; } + /** + * Closes the existing handle, and changes the handle to the new handle + * + * @param newNativeHandle The C++ pointer to the new native object + * @param owningNativeHandle true if we own the new native object + */ + public synchronized void resetNativeHandle(final long newNativeHandle, + final boolean owningNativeHandle) { + close(); + setNativeHandle(newNativeHandle, owningNativeHandle); + } + + /** + * Sets the handle (C++ pointer) of the underlying C++ native object + * + * @param nativeHandle The C++ pointer to the native object + * @param owningNativeHandle true if we own the native object + */ public synchronized void setNativeHandle(final long nativeHandle, final boolean owningNativeHandle) { this.nativeHandle_ = nativeHandle; diff --git a/java/src/main/java/org/rocksdb/Slice.java b/java/src/main/java/org/rocksdb/Slice.java index cbb9742a3..a43af75ae 100644 --- a/java/src/main/java/org/rocksdb/Slice.java +++ b/java/src/main/java/org/rocksdb/Slice.java @@ -14,6 +14,13 @@ package org.rocksdb; * values consider using {@link org.rocksdb.DirectSlice}

*/ public class Slice extends AbstractSlice { + + /** + * Indicates whether we have to free the memory pointed to by the Slice + */ + private volatile boolean cleared; + private volatile long internalBufferOffset = 0; + /** *

Called from JNI to construct a new Java Slice * without an underlying C++ object set @@ -27,6 +34,7 @@ public class Slice extends AbstractSlice { * Slice objects through this, they are not creating underlying C++ Slice * objects, and so there is nothing to free (dispose) from Java.

*/ + @SuppressWarnings("unused") private Slice() { super(); } @@ -62,6 +70,18 @@ public class Slice extends AbstractSlice { super(createNewSlice1(data)); } + @Override + public void clear() { + clear0(getNativeHandle(), !cleared, internalBufferOffset); + cleared = true; + } + + @Override + public void removePrefix(final int n) { + removePrefix0(getNativeHandle(), n); + this.internalBufferOffset += n; + } + /** *

Deletes underlying C++ slice pointer * and any buffered data.

@@ -74,7 +94,9 @@ public class Slice extends AbstractSlice { @Override protected void disposeInternal() { final long nativeHandle = getNativeHandle(); - disposeInternalBuf(nativeHandle); + if(!cleared) { + disposeInternalBuf(nativeHandle, internalBufferOffset); + } super.disposeInternal(nativeHandle); } @@ -82,5 +104,9 @@ public class Slice extends AbstractSlice { private native static long createNewSlice0(final byte[] data, final int length); private native static long createNewSlice1(final byte[] data); - private native void disposeInternalBuf(final long handle); + private native void clear0(long handle, boolean internalBuffer, + long internalBufferOffset); + private native void removePrefix0(long handle, int length); + private native void disposeInternalBuf(final long handle, + long internalBufferOffset); } diff --git a/java/src/main/java/org/rocksdb/StringAppendOperator.java b/java/src/main/java/org/rocksdb/StringAppendOperator.java index 52cd43e79..b392ef677 100644 --- a/java/src/main/java/org/rocksdb/StringAppendOperator.java +++ b/java/src/main/java/org/rocksdb/StringAppendOperator.java @@ -9,9 +9,11 @@ package org.rocksdb; * StringAppendOperator is a merge operator that concatenates * two strings. */ -public class StringAppendOperator implements MergeOperator { - @Override public long newMergeOperatorHandle() { - return newMergeOperatorHandleImpl(); +public class StringAppendOperator extends MergeOperator { + public StringAppendOperator() { + super(newSharedStringAppendOperator()); } - private native long newMergeOperatorHandleImpl(); + + private native static long newSharedStringAppendOperator(); + @Override protected final native void disposeInternal(final long handle); } diff --git a/java/src/main/java/org/rocksdb/TransactionLogIterator.java b/java/src/main/java/org/rocksdb/TransactionLogIterator.java index 868548085..b6bfc495b 100644 --- a/java/src/main/java/org/rocksdb/TransactionLogIterator.java +++ b/java/src/main/java/org/rocksdb/TransactionLogIterator.java @@ -65,7 +65,7 @@ public class TransactionLogIterator extends RocksObject { * by a TransactionLogIterator containing a sequence * number and a {@link WriteBatch} instance.

*/ - public final class BatchResult { + public static final class BatchResult { /** *

Constructor of BatchResult class.

* diff --git a/java/src/main/java/org/rocksdb/WBWIRocksIterator.java b/java/src/main/java/org/rocksdb/WBWIRocksIterator.java index 7eb019d0b..4222e1a26 100644 --- a/java/src/main/java/org/rocksdb/WBWIRocksIterator.java +++ b/java/src/main/java/org/rocksdb/WBWIRocksIterator.java @@ -29,12 +29,11 @@ public class WBWIRocksIterator */ public WriteEntry entry() { assert(isOwningHandle()); - assert(entry != null); final long ptrs[] = entry1(nativeHandle_); entry.type = WriteType.fromId((byte)ptrs[0]); - entry.key.setNativeHandle(ptrs[1], true); - entry.value.setNativeHandle(ptrs[2], ptrs[2] != 0); + entry.key.resetNativeHandle(ptrs[1], ptrs[1] != 0); + entry.value.resetNativeHandle(ptrs[2], ptrs[2] != 0); return entry; } @@ -75,6 +74,12 @@ public class WBWIRocksIterator } } + @Override + public void close() { + entry.close(); + super.close(); + } + /** * Represents an entry returned by * {@link org.rocksdb.WBWIRocksIterator#entry()} @@ -84,7 +89,7 @@ public class WBWIRocksIterator * or {@link org.rocksdb.WBWIRocksIterator.WriteType#LOG} * will not have a value. */ - public static class WriteEntry { + public static class WriteEntry implements AutoCloseable { WriteType type = null; final DirectSlice key; final DirectSlice value; @@ -101,7 +106,8 @@ public class WBWIRocksIterator value = new DirectSlice(); } - public WriteEntry(WriteType type, DirectSlice key, DirectSlice value) { + public WriteEntry(final WriteType type, final DirectSlice key, + final DirectSlice value) { this.type = type; this.key = key; this.value = value; @@ -154,7 +160,7 @@ public class WBWIRocksIterator } @Override - public boolean equals(Object other) { + public boolean equals(final Object other) { if(other == null) { return false; } else if (this == other) { @@ -168,5 +174,11 @@ public class WBWIRocksIterator return false; } } + + @Override + public void close() { + value.close(); + key.close(); + } } } diff --git a/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java b/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java index 5d9b2ad19..b84ea8583 100644 --- a/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java +++ b/java/src/main/java/org/rocksdb/WriteBatchWithIndex.java @@ -144,6 +144,9 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { * @param options The database options to use * @param key The key to read the value for * + * @return a byte array storing the value associated with the input key if + * any. null if it does not find the specified key. + * * @throws RocksDBException if the batch does not have enough data to resolve * Merge operations, MergeInProgress status may be returned. */ @@ -160,6 +163,9 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { * @param options The database options to use * @param key The key to read the value for * + * @return a byte array storing the value associated with the input key if + * any. null if it does not find the specified key. + * * @throws RocksDBException if the batch does not have enough data to resolve * Merge operations, MergeInProgress status may be returned. */ @@ -181,10 +187,14 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { * (the keys in this batch do not yet belong to any snapshot and will be * fetched regardless). * + * @param db The Rocks database * @param columnFamilyHandle The column family to retrieve the value from * @param options The read options to use * @param key The key to read the value for * + * @return a byte array storing the value associated with the input key if + * any. null if it does not find the specified key. + * * @throws RocksDBException if the value for the key cannot be read */ public byte[] getFromBatchAndDB(final RocksDB db, final ColumnFamilyHandle columnFamilyHandle, @@ -207,9 +217,13 @@ public class WriteBatchWithIndex extends AbstractWriteBatch { * (the keys in this batch do not yet belong to any snapshot and will be * fetched regardless). * + * @param db The Rocks database * @param options The read options to use * @param key The key to read the value for * + * @return a byte array storing the value associated with the input key if + * any. null if it does not find the specified key. + * * @throws RocksDBException if the value for the key cannot be read */ public byte[] getFromBatchAndDB(final RocksDB db, final ReadOptions options, diff --git a/java/src/test/java/org/rocksdb/ColumnFamilyTest.java b/java/src/test/java/org/rocksdb/ColumnFamilyTest.java index c5b4fe96a..3bf8cd22b 100644 --- a/java/src/test/java/org/rocksdb/ColumnFamilyTest.java +++ b/java/src/test/java/org/rocksdb/ColumnFamilyTest.java @@ -203,8 +203,9 @@ public class ColumnFamilyTest { @Test public void writeBatch() throws RocksDBException { - try (final ColumnFamilyOptions defaultCfOptions = new ColumnFamilyOptions() - .setMergeOperator(new StringAppendOperator())) { + try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); + final ColumnFamilyOptions defaultCfOptions = new ColumnFamilyOptions() + .setMergeOperator(stringAppendOperator)) { final List cfDescriptors = Arrays.asList( new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, defaultCfOptions), diff --git a/java/src/test/java/org/rocksdb/DBOptionsTest.java b/java/src/test/java/org/rocksdb/DBOptionsTest.java index ac00d1d16..157f267ff 100644 --- a/java/src/test/java/org/rocksdb/DBOptionsTest.java +++ b/java/src/test/java/org/rocksdb/DBOptionsTest.java @@ -388,25 +388,11 @@ public class DBOptionsTest { } } - @Test - public void rateLimiterConfig() { - try(final DBOptions options = new DBOptions(); - final DBOptions anotherOptions = new DBOptions()) { - final RateLimiterConfig rateLimiterConfig = - new GenericRateLimiterConfig(1000, 100 * 1000, 1); - options.setRateLimiterConfig(rateLimiterConfig); - // Test with parameter initialization - - anotherOptions.setRateLimiterConfig( - new GenericRateLimiterConfig(1000)); - } - } - @Test public void rateLimiter() { try(final DBOptions options = new DBOptions(); - final DBOptions anotherOptions = new DBOptions()) { - final RateLimiter rateLimiter = new RateLimiter(1000, 100 * 1000, 1); + final DBOptions anotherOptions = new DBOptions(); + final RateLimiter rateLimiter = new RateLimiter(1000, 100 * 1000, 1)) { options.setRateLimiter(rateLimiter); // Test with parameter initialization anotherOptions.setRateLimiter( diff --git a/java/src/test/java/org/rocksdb/DirectSliceTest.java b/java/src/test/java/org/rocksdb/DirectSliceTest.java index 2d3abea45..5c3a97858 100644 --- a/java/src/test/java/org/rocksdb/DirectSliceTest.java +++ b/java/src/test/java/org/rocksdb/DirectSliceTest.java @@ -54,7 +54,7 @@ public class DirectSliceTest { } } - @Test(expected = AssertionError.class) + @Test(expected = IllegalArgumentException.class) public void directSliceInitWithoutDirectAllocation() { final byte[] data = "Some text".getBytes(); final ByteBuffer buffer = ByteBuffer.wrap(data); @@ -63,7 +63,7 @@ public class DirectSliceTest { } } - @Test(expected = AssertionError.class) + @Test(expected = IllegalArgumentException.class) public void directSlicePrefixInitWithoutDirectAllocation() { final byte[] data = "Some text".getBytes(); final ByteBuffer buffer = ByteBuffer.wrap(data); @@ -71,4 +71,23 @@ public class DirectSliceTest { //no-op } } + + @Test + public void directSliceClear() { + try(final DirectSlice directSlice = new DirectSlice("abc")) { + assertThat(directSlice.toString()).isEqualTo("abc"); + directSlice.clear(); + assertThat(directSlice.toString()).isEmpty(); + directSlice.clear(); // make sure we don't double-free + } + } + + @Test + public void directSliceRemovePrefix() { + try(final DirectSlice directSlice = new DirectSlice("abc")) { + assertThat(directSlice.toString()).isEqualTo("abc"); + directSlice.removePrefix(1); + assertThat(directSlice.toString()).isEqualTo("bc"); + } + } } diff --git a/java/src/test/java/org/rocksdb/EnvOptionsTest.java b/java/src/test/java/org/rocksdb/EnvOptionsTest.java index 35dbc902e..648f4eb32 100644 --- a/java/src/test/java/org/rocksdb/EnvOptionsTest.java +++ b/java/src/test/java/org/rocksdb/EnvOptionsTest.java @@ -118,16 +118,16 @@ public class EnvOptionsTest { } @Test - public void rateLimiterConfig() { - try (final EnvOptions envOptions = new EnvOptions()) { - final RateLimiterConfig rateLimiterConfig1 = - new GenericRateLimiterConfig(1000, 100 * 1000, 1); - envOptions.setRateLimiterConfig(rateLimiterConfig1); - assertThat(envOptions.rateLimiterConfig()).isEqualTo(rateLimiterConfig1); - - final RateLimiterConfig rateLimiterConfig2 = new GenericRateLimiterConfig(1000); - envOptions.setRateLimiterConfig(rateLimiterConfig2); - assertThat(envOptions.rateLimiterConfig()).isEqualTo(rateLimiterConfig2); + public void rateLimiter() { + try (final EnvOptions envOptions = new EnvOptions(); + final RateLimiter rateLimiter1 = new RateLimiter(1000, 100 * 1000, 1)) { + envOptions.setRateLimiter(rateLimiter1); + assertThat(envOptions.rateLimiter()).isEqualTo(rateLimiter1); + + try(final RateLimiter rateLimiter2 = new RateLimiter(1000)) { + envOptions.setRateLimiter(rateLimiter2); + assertThat(envOptions.rateLimiter()).isEqualTo(rateLimiter2); + } } } } diff --git a/java/src/test/java/org/rocksdb/KeyMayExistTest.java b/java/src/test/java/org/rocksdb/KeyMayExistTest.java index bc341c9d2..b2f69c41d 100644 --- a/java/src/test/java/org/rocksdb/KeyMayExistTest.java +++ b/java/src/test/java/org/rocksdb/KeyMayExistTest.java @@ -43,21 +43,21 @@ public class KeyMayExistTest { isEqualTo(2); db.put("key".getBytes(), "value".getBytes()); // Test without column family - StringBuffer retValue = new StringBuffer(); + StringBuilder retValue = new StringBuilder(); boolean exists = db.keyMayExist("key".getBytes(), retValue); assertThat(exists).isTrue(); assertThat(retValue.toString()).isEqualTo("value"); // Test without column family but with readOptions try (final ReadOptions readOptions = new ReadOptions()) { - retValue = new StringBuffer(); + retValue = new StringBuilder(); exists = db.keyMayExist(readOptions, "key".getBytes(), retValue); assertThat(exists).isTrue(); assertThat(retValue.toString()).isEqualTo("value"); } // Test with column family - retValue = new StringBuffer(); + retValue = new StringBuilder(); exists = db.keyMayExist(columnFamilyHandleList.get(0), "key".getBytes(), retValue); assertThat(exists).isTrue(); @@ -65,7 +65,7 @@ public class KeyMayExistTest { // Test with column family and readOptions try (final ReadOptions readOptions = new ReadOptions()) { - retValue = new StringBuffer(); + retValue = new StringBuilder(); exists = db.keyMayExist(readOptions, columnFamilyHandleList.get(0), "key".getBytes(), retValue); diff --git a/java/src/test/java/org/rocksdb/MergeTest.java b/java/src/test/java/org/rocksdb/MergeTest.java index d38df3195..dec01a162 100644 --- a/java/src/test/java/org/rocksdb/MergeTest.java +++ b/java/src/test/java/org/rocksdb/MergeTest.java @@ -89,11 +89,10 @@ public class MergeTest { @Test public void operatorOption() throws InterruptedException, RocksDBException { - final StringAppendOperator stringAppendOperator = - new StringAppendOperator(); - try (final Options opt = new Options() - .setCreateIfMissing(true) - .setMergeOperator(stringAppendOperator); + try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); + final Options opt = new Options() + .setCreateIfMissing(true) + .setMergeOperator(stringAppendOperator); final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) { // Writing aa under key @@ -112,10 +111,9 @@ public class MergeTest { @Test public void cFOperatorOption() throws InterruptedException, RocksDBException { - final StringAppendOperator stringAppendOperator = - new StringAppendOperator(); - try (final ColumnFamilyOptions cfOpt1 = new ColumnFamilyOptions() - .setMergeOperator(stringAppendOperator); + try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); + final ColumnFamilyOptions cfOpt1 = new ColumnFamilyOptions() + .setMergeOperator(stringAppendOperator); final ColumnFamilyOptions cfOpt2 = new ColumnFamilyOptions() .setMergeOperator(stringAppendOperator) ) { @@ -175,42 +173,43 @@ public class MergeTest { @Test public void operatorGcBehaviour() throws RocksDBException { - final StringAppendOperator stringAppendOperator - = new StringAppendOperator(); - try (final Options opt = new Options() - .setCreateIfMissing(true) - .setMergeOperator(stringAppendOperator); - final RocksDB db = RocksDB.open(opt, - dbFolder.getRoot().getAbsolutePath())) { - //no-op - } + try (final StringAppendOperator stringAppendOperator = new StringAppendOperator()) { + try (final Options opt = new Options() + .setCreateIfMissing(true) + .setMergeOperator(stringAppendOperator); + final RocksDB db = RocksDB.open(opt, + dbFolder.getRoot().getAbsolutePath())) { + //no-op + } - // test reuse - try (final Options opt = new Options() - .setMergeOperator(stringAppendOperator); - final RocksDB db = RocksDB.open(opt, - dbFolder.getRoot().getAbsolutePath())) { - //no-op - } - // test param init - try (final Options opt = new Options() - .setMergeOperator(new StringAppendOperator()); - final RocksDB db = RocksDB.open(opt, - dbFolder.getRoot().getAbsolutePath())) { - //no-op - } + // test reuse + try (final Options opt = new Options() + .setMergeOperator(stringAppendOperator); + final RocksDB db = RocksDB.open(opt, + dbFolder.getRoot().getAbsolutePath())) { + //no-op + } - // test replace one with another merge operator instance - try (final Options opt = new Options() - .setMergeOperator(stringAppendOperator)) { - final StringAppendOperator newStringAppendOperator - = new StringAppendOperator(); - opt.setMergeOperator(newStringAppendOperator); - try (final RocksDB db = RocksDB.open(opt, - dbFolder.getRoot().getAbsolutePath())) { + // test param init + try (final StringAppendOperator stringAppendOperator2 = new StringAppendOperator(); + final Options opt = new Options() + .setMergeOperator(stringAppendOperator2); + final RocksDB db = RocksDB.open(opt, + dbFolder.getRoot().getAbsolutePath())) { //no-op } + + // test replace one with another merge operator instance + try (final Options opt = new Options() + .setMergeOperator(stringAppendOperator); + final StringAppendOperator newStringAppendOperator = new StringAppendOperator()) { + opt.setMergeOperator(newStringAppendOperator); + try (final RocksDB db = RocksDB.open(opt, + dbFolder.getRoot().getAbsolutePath())) { + //no-op + } + } } } diff --git a/java/src/test/java/org/rocksdb/OptionsTest.java b/java/src/test/java/org/rocksdb/OptionsTest.java index 084a77af0..f13356826 100644 --- a/java/src/test/java/org/rocksdb/OptionsTest.java +++ b/java/src/test/java/org/rocksdb/OptionsTest.java @@ -697,16 +697,15 @@ public class OptionsTest { @Test public void compressionPerLevel() { - try (final ColumnFamilyOptions columnFamilyOptions = - new ColumnFamilyOptions()) { - assertThat(columnFamilyOptions.compressionPerLevel()).isEmpty(); + try (final Options options = new Options()) { + assertThat(options.compressionPerLevel()).isEmpty(); List compressionTypeList = new ArrayList<>(); - for (int i = 0; i < columnFamilyOptions.numLevels(); i++) { + for (int i = 0; i < options.numLevels(); i++) { compressionTypeList.add(CompressionType.NO_COMPRESSION); } - columnFamilyOptions.setCompressionPerLevel(compressionTypeList); - compressionTypeList = columnFamilyOptions.compressionPerLevel(); + options.setCompressionPerLevel(compressionTypeList); + compressionTypeList = options.compressionPerLevel(); for (final CompressionType compressionType : compressionTypeList) { assertThat(compressionType).isEqualTo( CompressionType.NO_COMPRESSION); @@ -716,19 +715,18 @@ public class OptionsTest { @Test public void differentCompressionsPerLevel() { - try (final ColumnFamilyOptions columnFamilyOptions = - new ColumnFamilyOptions()) { - columnFamilyOptions.setNumLevels(3); + try (final Options options = new Options()) { + options.setNumLevels(3); - assertThat(columnFamilyOptions.compressionPerLevel()).isEmpty(); + assertThat(options.compressionPerLevel()).isEmpty(); List compressionTypeList = new ArrayList<>(); compressionTypeList.add(CompressionType.BZLIB2_COMPRESSION); compressionTypeList.add(CompressionType.SNAPPY_COMPRESSION); compressionTypeList.add(CompressionType.LZ4_COMPRESSION); - columnFamilyOptions.setCompressionPerLevel(compressionTypeList); - compressionTypeList = columnFamilyOptions.compressionPerLevel(); + options.setCompressionPerLevel(compressionTypeList); + compressionTypeList = options.compressionPerLevel(); assertThat(compressionTypeList.size()).isEqualTo(3); assertThat(compressionTypeList). @@ -767,26 +765,12 @@ public class OptionsTest { } } - @Test - public void rateLimiterConfig() { - try (final Options options = new Options(); - final Options anotherOptions = new Options()) { - final RateLimiterConfig rateLimiterConfig = - new GenericRateLimiterConfig(1000, 100 * 1000, 1); - options.setRateLimiterConfig(rateLimiterConfig); - // Test with parameter initialization - - anotherOptions.setRateLimiterConfig( - new GenericRateLimiterConfig(1000)); - } - } - @Test public void rateLimiter() { try (final Options options = new Options(); - final Options anotherOptions = new Options()) { - final RateLimiter rateLimiter = - new RateLimiter(1000, 100 * 1000, 1); + final Options anotherOptions = new Options(); + final RateLimiter rateLimiter = + new RateLimiter(1000, 100 * 1000, 1)) { options.setRateLimiter(rateLimiter); // Test with parameter initialization anotherOptions.setRateLimiter( @@ -810,7 +794,6 @@ public class OptionsTest { } } - @Test public void shouldTestMemTableFactoryName() throws RocksDBException { diff --git a/java/src/test/java/org/rocksdb/RateLimiterTest.java b/java/src/test/java/org/rocksdb/RateLimiterTest.java new file mode 100644 index 000000000..96733d74b --- /dev/null +++ b/java/src/test/java/org/rocksdb/RateLimiterTest.java @@ -0,0 +1,49 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +package org.rocksdb; + +import org.junit.ClassRule; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RateLimiterTest { + + @ClassRule + public static final RocksMemoryResource rocksMemoryResource = + new RocksMemoryResource(); + + @Test + public void setBytesPerSecond() { + try(final RateLimiter rateLimiter = + new RateLimiter(1000, 100 * 1000, 1)) { + rateLimiter.setBytesPerSecond(2000); + } + } + + @Test + public void getSingleBurstBytes() { + try(final RateLimiter rateLimiter = + new RateLimiter(1000, 100 * 1000, 1)) { + assertThat(rateLimiter.getSingleBurstBytes()).isEqualTo(100); + } + } + + @Test + public void getTotalBytesThrough() { + try(final RateLimiter rateLimiter = + new RateLimiter(1000, 100 * 1000, 1)) { + assertThat(rateLimiter.getTotalBytesThrough()).isEqualTo(0); + } + } + + @Test + public void getTotalRequests() { + try(final RateLimiter rateLimiter = + new RateLimiter(1000, 100 * 1000, 1)) { + assertThat(rateLimiter.getTotalRequests()).isEqualTo(0); + } + } +} diff --git a/java/src/test/java/org/rocksdb/RocksDBTest.java b/java/src/test/java/org/rocksdb/RocksDBTest.java index 7ae0da6e2..ab1feb0e5 100644 --- a/java/src/test/java/org/rocksdb/RocksDBTest.java +++ b/java/src/test/java/org/rocksdb/RocksDBTest.java @@ -73,8 +73,10 @@ public class RocksDBTest { @Test public void write() throws RocksDBException { - try (final Options options = new Options().setMergeOperator( - new StringAppendOperator()).setCreateIfMissing(true); + try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); + final Options options = new Options() + .setMergeOperator(stringAppendOperator) + .setCreateIfMissing(true); final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath()); final WriteOptions opts = new WriteOptions()) { @@ -182,9 +184,10 @@ public class RocksDBTest { @Test public void merge() throws RocksDBException { - try (final Options opt = new Options() - .setCreateIfMissing(true) - .setMergeOperator(new StringAppendOperator()); + try (final StringAppendOperator stringAppendOperator = new StringAppendOperator(); + final Options opt = new Options() + .setCreateIfMissing(true) + .setMergeOperator(stringAppendOperator); final WriteOptions wOpt = new WriteOptions(); final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath()) diff --git a/java/src/test/java/org/rocksdb/SliceTest.java b/java/src/test/java/org/rocksdb/SliceTest.java index 952c9ab86..84894ee38 100644 --- a/java/src/test/java/org/rocksdb/SliceTest.java +++ b/java/src/test/java/org/rocksdb/SliceTest.java @@ -32,6 +32,25 @@ public class SliceTest { } } + @Test + public void sliceClear() { + try (final Slice slice = new Slice("abc")) { + assertThat(slice.toString()).isEqualTo("abc"); + slice.clear(); + assertThat(slice.toString()).isEmpty(); + slice.clear(); // make sure we don't double-free + } + } + + @Test + public void sliceRemovePrefix() { + try (final Slice slice = new Slice("abc")) { + assertThat(slice.toString()).isEqualTo("abc"); + slice.removePrefix(1); + assertThat(slice.toString()).isEqualTo("bc"); + } + } + @Test public void sliceEquals() { try (final Slice slice = new Slice("abc"); diff --git a/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java b/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java index 5e074eab1..b2283480a 100644 --- a/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java +++ b/java/src/test/java/org/rocksdb/WriteBatchWithIndexTest.java @@ -192,16 +192,16 @@ public class WriteBatchWithIndexTest { final ByteBuffer buffer = ByteBuffer.allocateDirect(zeroByteValue.length); buffer.put(zeroByteValue); - WBWIRocksIterator.WriteEntry[] expected = { + final WBWIRocksIterator.WriteEntry expected = new WBWIRocksIterator.WriteEntry(WBWIRocksIterator.WriteType.PUT, new DirectSlice(buffer, zeroByteValue.length), - new DirectSlice(buffer, zeroByteValue.length)) - }; + new DirectSlice(buffer, zeroByteValue.length)); try (final WBWIRocksIterator it = wbwi.newIterator()) { it.seekToFirst(); - assertThat(it.entry().equals(expected[0])).isTrue(); - assertThat(it.entry().hashCode() == expected[0].hashCode()).isTrue(); + final WBWIRocksIterator.WriteEntry actual = it.entry(); + assertThat(actual.equals(expected)).isTrue(); + assertThat(it.entry().hashCode() == expected.hashCode()).isTrue(); } } } diff --git a/java/src/test/java/org/rocksdb/test/RocksJunitRunner.java b/java/src/test/java/org/rocksdb/test/RocksJunitRunner.java index 044f96b94..68f100274 100644 --- a/java/src/test/java/org/rocksdb/test/RocksJunitRunner.java +++ b/java/src/test/java/org/rocksdb/test/RocksJunitRunner.java @@ -31,12 +31,12 @@ public class RocksJunitRunner { * * @param system JUnitSystem */ - public RocksJunitListener(JUnitSystem system) { + public RocksJunitListener(final JUnitSystem system) { super(system); } @Override - public void testStarted(Description description) { + public void testStarted(final Description description) { System.out.format("Run: %s testing now -> %s \n", description.getClassName(), description.getMethodName()); @@ -48,21 +48,23 @@ public class RocksJunitRunner { * * @param args Test classes as String names */ - public static void main(String[] args){ - JUnitCore runner = new JUnitCore(); + public static void main(final String[] args){ + final JUnitCore runner = new JUnitCore(); final JUnitSystem system = new RealSystem(); runner.addListener(new RocksJunitListener(system)); try { - List> classes = new ArrayList<>(); - for (String arg : args) { + final List> classes = new ArrayList<>(); + for (final String arg : args) { classes.add(Class.forName(arg)); } - final Result result = runner.run(classes.toArray(new Class[1])); + final Class[] clazzes = classes.toArray(new Class[classes.size()]); + final Result result = runner.run(clazzes); if(!result.wasSuccessful()) { System.exit(-1); } - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { e.printStackTrace(); + System.exit(-2); } } }