diff --git a/java/Makefile b/java/Makefile index c9e65808a..81b5afc79 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1,4 +1,4 @@ -NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.WriteBatch org.rocksdb.WriteBatchInternal org.rocksdb.WriteBatchTest org.rocksdb.WriteOptions org.rocksdb.BackupableDB org.rocksdb.BackupableDBOptions org.rocksdb.Statistics +NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options org.rocksdb.WriteBatch org.rocksdb.WriteBatchInternal org.rocksdb.WriteBatchTest org.rocksdb.WriteOptions org.rocksdb.BackupableDB org.rocksdb.BackupableDBOptions org.rocksdb.Statistics org.rocksdb.Iterator NATIVE_INCLUDE = ./include ROCKSDB_JAR = rocksdbjni.jar diff --git a/java/RocksDBSample.java b/java/RocksDBSample.java index 406102426..d33428a50 100644 --- a/java/RocksDBSample.java +++ b/java/RocksDBSample.java @@ -3,8 +3,7 @@ // 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. -import java.util.*; -import java.lang.*; +import java.util.Arrays; import org.rocksdb.*; import org.rocksdb.util.SizeUnit; import java.io.IOException; @@ -142,6 +141,41 @@ public class RocksDBSample { System.out.println("Failed in call to geHistogramData()"); assert(false); //Should never reach here. } + + Iterator iterator = db.newIterator(); + + boolean seekToFirstPassed = false; + for (iterator.seekToFirst(); iterator.isValid(); iterator.next()) { + iterator.status(); + assert(iterator.key() != null); + assert(iterator.value() != null); + seekToFirstPassed = true; + } + if(seekToFirstPassed) { + System.out.println("iterator seekToFirst tests passed."); + } + + boolean seekToLastPassed = false; + for (iterator.seekToLast(); iterator.isValid(); iterator.prev()) { + iterator.status(); + assert(iterator.key() != null); + assert(iterator.value() != null); + seekToLastPassed = true; + } + + if(seekToLastPassed) { + System.out.println("iterator seekToLastPassed tests passed."); + } + + iterator.seekToFirst(); + iterator.seek(iterator.key()); + assert(iterator.key() != null); + assert(iterator.value() != null); + + System.out.println("iterator seek test passed."); + + iterator.close(); + System.out.println("iterator tests passed."); } catch (RocksDBException e) { System.err.println(e); } diff --git a/java/org/rocksdb/Iterator.java b/java/org/rocksdb/Iterator.java new file mode 100644 index 000000000..c34deab35 --- /dev/null +++ b/java/org/rocksdb/Iterator.java @@ -0,0 +1,146 @@ +// Copyright (c) 2014, 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; + +/** + * An iterator yields a sequence of key/value pairs from a source. + * The following class defines the interface. Multiple implementations + * are provided by this library. In particular, iterators are provided + * to access the contents of a Table or a DB. + * + * Multiple threads can invoke const methods on an Iterator without + * external synchronization, but if any of the threads may call a + * non-const method, all threads accessing the same Iterator must use + * external synchronization. + */ +public class Iterator { + private long nativeHandle_; + + public Iterator(long nativeHandle) { + nativeHandle_ = nativeHandle; + } + + /** + * An iterator is either positioned at a key/value pair, or + * not valid. This method returns true iff the iterator is valid. + * @return true if iterator is valid. + */ + public boolean isValid() { + assert(isInitialized()); + return isValid0(nativeHandle_); + } + + /** + * Position at the first key in the source. The iterator is Valid() + * after this call iff the source is not empty. + */ + public void seekToFirst() { + assert(isInitialized()); + seekToFirst0(nativeHandle_); + } + + /** + * Position at the last key in the source. The iterator is + * Valid() after this call iff the source is not empty. + */ + public void seekToLast() { + assert(isInitialized()); + seekToLast0(nativeHandle_); + } + + /** + * Moves to the next entry in the source. After this call, Valid() is + * true iff the iterator was not positioned at the last entry in the source. + * REQUIRES: Valid() + */ + public void next() { + assert(isInitialized()); + next0(nativeHandle_); + } + + /** + * Moves to the previous entry in the source. After this call, Valid() is + * true iff the iterator was not positioned at the first entry in source. + * REQUIRES: Valid() + */ + public void prev() { + assert(isInitialized()); + prev0(nativeHandle_); + } + + /** + * Return the key for the current entry. The underlying storage for + * the returned slice is valid only until the next modification of + * the iterator. + * REQUIRES: Valid() + * @return key for the current entry. + */ + public byte[] key() { + assert(isInitialized()); + return key0(nativeHandle_); + } + + /** + * Return the value for the current entry. The underlying storage for + * the returned slice is valid only until the next modification of + * the iterator. + * REQUIRES: !AtEnd() && !AtStart() + * @return value for the current entry. + */ + public byte[] value() { + assert(isInitialized()); + return value0(nativeHandle_); + } + + /** + * Position at the first key in the source that at or past target + * The iterator is Valid() after this call iff the source contains + * an entry that comes at or past target. + */ + public void seek(byte[] target) { + assert(isInitialized()); + seek0(nativeHandle_, target, target.length); + } + + /** + * If an error has occurred, return it. Else return an ok status. + * If non-blocking IO is requested and this operation cannot be + * satisfied without doing some IO, then this returns Status::Incomplete(). + * + */ + public void status() throws RocksDBException { + assert(isInitialized()); + status0(nativeHandle_); + } + + /** + * Deletes underlying C++ iterator pointer. + */ + public synchronized void close() { + if(nativeHandle_ != 0) { + close0(nativeHandle_); + } + } + + @Override protected void finalize() { + close(); + } + + private boolean isInitialized() { + return (nativeHandle_ != 0); + } + + private native boolean isValid0(long handle); + private native void close0(long handle); + private native void seekToFirst0(long handle); + private native void seekToLast0(long handle); + private native void next0(long handle); + private native void prev0(long handle); + private native byte[] key0(long handle); + private native byte[] value0(long handle); + private native void seek0(long handle, byte[] target, int targetLen); + private native void status0(long handle); +} diff --git a/java/org/rocksdb/Options.java b/java/org/rocksdb/Options.java index 64b56b87c..13b35e434 100644 --- a/java/org/rocksdb/Options.java +++ b/java/org/rocksdb/Options.java @@ -508,7 +508,7 @@ public class Options { return maxBackgroundCompactions(nativeHandle_); } - /** + /** * Creates statistics object which collects metrics about database operations. Statistics objects should not be shared between DB instances as it does not use any locks to prevent concurrent updates. diff --git a/java/org/rocksdb/RocksDB.java b/java/org/rocksdb/RocksDB.java index 9f93f9986..38426d50c 100644 --- a/java/org/rocksdb/RocksDB.java +++ b/java/org/rocksdb/RocksDB.java @@ -5,7 +5,6 @@ package org.rocksdb; -import java.util.*; import java.io.Closeable; import java.io.IOException; @@ -137,6 +136,20 @@ public class RocksDB { remove(nativeHandle_, writeOpt.nativeHandle_, key, key.length); } + /** + * Return a heap-allocated iterator over the contents of the database. + * The result of newIterator() is initially invalid (caller must + * call one of the Seek methods on the iterator before using it). + * + * Caller should close the iterator when it is no longer needed. + * The returned iterator should be closed before this db is closed. + * + * @return instance of iterator object. + */ + public Iterator newIterator() { + return new Iterator(iterator0(nativeHandle_)); + } + @Override protected void finalize() { close(); } @@ -170,6 +183,7 @@ public class RocksDB { protected native void remove( long handle, long writeOptHandle, byte[] key, int keyLen) throws RocksDBException; + protected native long iterator0(long optHandle); protected native void close0(); protected long nativeHandle_; diff --git a/java/rocksjni/iterator.cc b/java/rocksjni/iterator.cc new file mode 100644 index 000000000..f23ca1d87 --- /dev/null +++ b/java/rocksjni/iterator.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2014, 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. +// +// This file implements the "bridge" between Java and C++ and enables +// calling c++ rocksdb::Iterator methods from Java side. + +#include +#include +#include + +#include "include/org_rocksdb_Iterator.h" +#include "rocksjni/portal.h" +#include "rocksdb/iterator.h" + +/* + * Class: org_rocksdb_Iterator + * Method: isValid0 + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_Iterator_isValid0( + JNIEnv* env, jobject jobj, jlong handle) { + return reinterpret_cast(handle)->Valid(); +} + +/* + * Class: org_rocksdb_Iterator + * Method: close0 + * Signature: (J)V + */ +void Java_org_rocksdb_Iterator_seekToFirst0( + JNIEnv* env, jobject jobj, jlong handle) { + reinterpret_cast(handle)->SeekToFirst(); +} + +/* + * Class: org_rocksdb_Iterator + * Method: seekToFirst0 + * Signature: (J)V + */ +void Java_org_rocksdb_Iterator_seekToLast0( + JNIEnv* env, jobject jobj, jlong handle) { + reinterpret_cast(handle)->SeekToLast(); +} + +/* + * Class: org_rocksdb_Iterator + * Method: seekToLast0 + * Signature: (J)V + */ +void Java_org_rocksdb_Iterator_next0( + JNIEnv* env, jobject jobj, jlong handle) { + reinterpret_cast(handle)->Next(); +} + +/* + * Class: org_rocksdb_Iterator + * Method: next0 + * Signature: (J)V + */ +void Java_org_rocksdb_Iterator_prev0( + JNIEnv* env, jobject jobj, jlong handle) { + reinterpret_cast(handle)->Prev(); +} + +/* + * Class: org_rocksdb_Iterator + * Method: prev0 + * Signature: (J)V + */ +jbyteArray Java_org_rocksdb_Iterator_key0( + JNIEnv* env, jobject jobj, jlong handle) { + auto it = reinterpret_cast(handle); + rocksdb::Slice key_slice = it->key(); + + jbyteArray jkey = env->NewByteArray(key_slice.size()); + env->SetByteArrayRegion( + jkey, 0, key_slice.size(), + reinterpret_cast(key_slice.data())); + return jkey; +} + +/* + * Class: org_rocksdb_Iterator + * Method: key0 + * Signature: (J)[B + */ +jbyteArray Java_org_rocksdb_Iterator_value0( + JNIEnv* env, jobject jobj, jlong handle) { + auto it = reinterpret_cast(handle); + rocksdb::Slice value_slice = it->value(); + + jbyteArray jvalue = env->NewByteArray(value_slice.size()); + env->SetByteArrayRegion( + jvalue, 0, value_slice.size(), + reinterpret_cast(value_slice.data())); + return jvalue; +} + +/* + * Class: org_rocksdb_Iterator + * Method: value0 + * Signature: (J)[B + */ +void Java_org_rocksdb_Iterator_seek0( + JNIEnv* env, jobject jobj, jlong handle, + jbyteArray jtarget, jint jtarget_len) { + auto it = reinterpret_cast(handle); + jbyte* target = env->GetByteArrayElements(jtarget, 0); + rocksdb::Slice target_slice( + reinterpret_cast(target), jtarget_len); + + it->Seek(target_slice); + + env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT); +} + +/* + * Class: org_rocksdb_Iterator + * Method: seek0 + * Signature: (J[BI)V + */ +void Java_org_rocksdb_Iterator_status0( + JNIEnv* env, jobject jobj, jlong handle) { + auto it = reinterpret_cast(handle); + rocksdb::Status s = it->status(); + + if (s.ok()) { + return; + } + + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); +} + +/* + * Class: org_rocksdb_Iterator + * Method: status0 + * Signature: (J)V + */ +void Java_org_rocksdb_Iterator_close0( + JNIEnv* env, jobject jobj, jlong handle) { + auto it = reinterpret_cast(handle); + delete it; + + rocksdb::IteratorJni::setHandle(env, jobj, nullptr); +} diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index 024062c0b..a4e2dc21a 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -213,5 +213,38 @@ class BackupableDBOptionsJni { reinterpret_cast(op)); } }; + +class IteratorJni { + public: + // Get the java class id of org.rocksdb.Iteartor. + static jclass getJClass(JNIEnv* env) { + static jclass jclazz = env->FindClass("org/rocksdb/Iterator"); + assert(jclazz != nullptr); + return jclazz; + } + + // Get the field id of the member variable of org.rocksdb.Iterator + // that stores the pointer to rocksdb::Iterator. + static jfieldID getHandleFieldID(JNIEnv* env) { + static jfieldID fid = env->GetFieldID( + getJClass(env), "nativeHandle_", "J"); + assert(fid != nullptr); + return fid; + } + + // Get the pointer to rocksdb::Iterator. + static rocksdb::Iterator* getHandle(JNIEnv* env, jobject jobj) { + return reinterpret_cast( + env->GetLongField(jobj, getHandleFieldID(env))); + } + + // Pass the rocksdb::Iterator pointer to the java side. + static void setHandle( + JNIEnv* env, jobject jobj, rocksdb::Iterator* op) { + env->SetLongField( + jobj, getHandleFieldID(env), + reinterpret_cast(op)); + } +}; } // namespace rocksdb #endif // JAVA_ROCKSJNI_PORTAL_H_ diff --git a/java/rocksjni/rocksjni.cc b/java/rocksjni/rocksjni.cc index 6602f0cc7..f3f9c831b 100644 --- a/java/rocksjni/rocksjni.cc +++ b/java/rocksjni/rocksjni.cc @@ -296,3 +296,15 @@ void Java_org_rocksdb_RocksDB_close0( rocksdb::RocksDBJni::setHandle(env, java_db, nullptr); } + +/* + * Class: org_rocksdb_RocksDB + * Method: iterator0 + * Signature: (J)J + */ +jlong Java_org_rocksdb_RocksDB_iterator0( + JNIEnv* env, jobject jdb, jlong db_handle) { + auto db = reinterpret_cast(db_handle); + rocksdb::Iterator* iterator = db->NewIterator(rocksdb::ReadOptions()); + return reinterpret_cast(iterator); +} diff --git a/java/rocksjni/statistics.cc b/java/rocksjni/statistics.cc index 546b5fec7..bf170c6de 100644 --- a/java/rocksjni/statistics.cc +++ b/java/rocksjni/statistics.cc @@ -13,7 +13,6 @@ #include "include/org_rocksdb_Statistics.h" #include "rocksjni/portal.h" #include "rocksdb/statistics.h" -#include /* * Class: org_rocksdb_Statistics