diff --git a/Makefile b/Makefile index 772e35895..bf0700177 100644 --- a/Makefile +++ b/Makefile @@ -402,7 +402,7 @@ ldb: tools/ldb.o $(LIBOBJECTS) # --------------------------------------------------------------------------- # Jni stuff # --------------------------------------------------------------------------- -JNI_NATIVE_SOURCES = ./java/rocksjni/rocksjni.cc +JNI_NATIVE_SOURCES = ./java/rocksjni/rocksjni.cc ./java/rocksjni/options.cc JAVA_INCLUDE = -I/usr/lib/jvm/java-openjdk/include/ -I/usr/lib/jvm/java-openjdk/include/linux ROCKSDBJNILIB = ./java/librocksdbjni.so diff --git a/java/Makefile b/java/Makefile index 794ec1439..8168d3418 100644 --- a/java/Makefile +++ b/java/Makefile @@ -1,4 +1,4 @@ -NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB +NATIVE_JAVA_CLASSES = org.rocksdb.RocksDB org.rocksdb.Options NATIVE_INCLUDE = ./include ROCKSDB_JAR = rocksdbjni.jar @@ -14,4 +14,8 @@ java: sample: javac -cp $(ROCKSDB_JAR) RocksDBSample.java - java -ea -Djava.library.path=.:../ -cp ".:./*" RocksDBSample /tmp/rocksdbjni/ + @rm -rf /tmp/rocksdbjni + @rm -rf /tmp/rocksdbjni_not_found + java -ea -Djava.library.path=.:../ -cp ".:./*" RocksDBSample /tmp/rocksdbjni + @rm -rf /tmp/rocksdbjni + @rm -rf /tmp/rocksdbjni_not_found diff --git a/java/RocksDBSample.java b/java/RocksDBSample.java index 3c1c1cc83..4e06bb29d 100644 --- a/java/RocksDBSample.java +++ b/java/RocksDBSample.java @@ -19,9 +19,34 @@ public class RocksDBSample { return; } String db_path = args[0]; + String db_path_not_found = db_path + "_not_found"; System.out.println("RocksDBSample"); RocksDB db = null; + Options options = new Options(); + try { + db = RocksDB.open(options, db_path_not_found); + assert(false); + } catch (RocksDBException e) { + System.out.format("caught the expceted exception -- %s\n", e); + assert(db == null); + } + + options.setCreateIfMissing(true); + try { + db = RocksDB.open(options, db_path_not_found); + db.put("hello".getBytes(), "world".getBytes()); + byte[] value = db.get("hello".getBytes()); + assert("world".equals(new String(value))); + } catch (RocksDBException e) { + System.out.format("[ERROR] caught the unexpceted exception -- %s\n", e); + assert(db == null); + assert(false); + } + + // be sure to release the c++ pointer + options.dispose(); + db.close(); try { db = RocksDB.open(db_path); diff --git a/java/org/rocksdb/Options.java b/java/org/rocksdb/Options.java new file mode 100644 index 000000000..af759ae8d --- /dev/null +++ b/java/org/rocksdb/Options.java @@ -0,0 +1,69 @@ +// 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; + +/** + * Options to control the behavior of a database. It will be used + * during the creation of a RocksDB (i.e., RocksDB::Open()). + * + * Note that dispose() must be called before an Options instance + * become out-of-scope to release the allocated memory in c++. + */ +public class Options { + /** + * Construct options for opening a RocksDB. + * + * This constructor will create (by allocating a block of memory) + * an rocksdb::Options in the c++ side. + */ + public Options() { + nativeHandle_ = 0; + newOptions(); + } + + /** + * If this value is set to true, then the database will be created + * if it is missing during RocksDB::Open(). + * Default: false + * + * @param flag a flag indicating whether to create a database the + * specified database in RocksDB::Open() operation is missing. + * @see RocksDB::Open() + */ + public void setCreateIfMissing(boolean flag) { + assert(nativeHandle_ != 0); + setCreateIfMissing(nativeHandle_, flag); + } + + /** + * Return true if the create_if_missing flag is set to true. + * If true, the database will be created if it is missing. + * + * @return return true if the create_if_missing flag is set to true. + * @see setCreateIfMissing() + */ + public boolean craeteIfMissing() { + assert(nativeHandle_ != 0); + return createIfMissing(nativeHandle_); + } + + /** + * Release the memory allocated for the current instance + * in the c++ side. + */ + public synchronized void dispose() { + if (nativeHandle_ != 0) { + dispose0(); + } + } + + private native void newOptions(); + private native void dispose0(); + private native void setCreateIfMissing(long handle, boolean flag); + private native boolean createIfMissing(long handle); + + long nativeHandle_; +} diff --git a/java/org/rocksdb/RocksDB.java b/java/org/rocksdb/RocksDB.java index 7b5f80beb..b08694081 100644 --- a/java/org/rocksdb/RocksDB.java +++ b/java/org/rocksdb/RocksDB.java @@ -20,12 +20,16 @@ public class RocksDB { public static final int NOT_FOUND = -1; /** * The factory constructor of RocksDB that opens a RocksDB instance given - * the path to the database. + * the path to the database using the default options w/ createIfMissing + * set to true. * * @param path the path to the rocksdb. * @param status an out value indicating the status of the Open(). * @return a rocksdb instance on success, null if the specified rocksdb can * not be opened. + * + * @see Options.setCreateIfMissing() + * @see Options.createIfMissing() */ public static RocksDB open(String path) throws RocksDBException { RocksDB db = new RocksDB(); @@ -33,6 +37,17 @@ public class RocksDB { return db; } + /** + * The factory constructor of RocksDB that opens a RocksDB instance given + * the path to the database using the specified options and db path. + */ + public static RocksDB open(Options options, String path) + throws RocksDBException { + RocksDB db = new RocksDB(); + db.open(options.nativeHandle_, path); + return db; + } + public synchronized void close() { if (nativeHandle_ != 0) { close0(); @@ -93,6 +108,7 @@ public class RocksDB { // native methods private native void open0(String path) throws RocksDBException; + private native void open(long optionsHandle, String path) throws RocksDBException; private native void put( byte[] key, int keyLen, byte[] value, int valueLen) throws RocksDBException; diff --git a/java/rocksjni/options.cc b/java/rocksjni/options.cc new file mode 100644 index 000000000..ef308bc4d --- /dev/null +++ b/java/rocksjni/options.cc @@ -0,0 +1,57 @@ +// 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++ for rocksdb::Options. + +#include +#include +#include +#include + +#include "include/org_rocksdb_Options.h" +#include "rocksjni/portal.h" +#include "rocksdb/db.h" + +/* + * Class: org_rocksdb_Options + * Method: newOptions + * Signature: ()V + */ +void Java_org_rocksdb_Options_newOptions(JNIEnv* env, jobject jobj) { + rocksdb::Options* op = new rocksdb::Options(); + rocksdb::OptionsJni::setHandle(env, jobj, op); +} + +/* + * Class: org_rocksdb_Options + * Method: dispose0 + * Signature: ()V + */ +void Java_org_rocksdb_Options_dispose0(JNIEnv* env, jobject jobj) { + rocksdb::Options* op = rocksdb::OptionsJni::getHandle(env, jobj); + delete op; + + rocksdb::OptionsJni::setHandle(env, jobj, op); +} + +/* + * Class: org_rocksdb_Options + * Method: setCreateIfMissing + * Signature: (JZ)V + */ +void Java_org_rocksdb_Options_setCreateIfMissing( + JNIEnv* env, jobject jobj, jlong jhandle, jboolean flag) { + reinterpret_cast(jhandle)->create_if_missing = flag; +} + +/* + * Class: org_rocksdb_Options + * Method: createIfMissing + * Signature: (J)Z + */ +jboolean Java_org_rocksdb_Options_createIfMissing( + JNIEnv* env, jobject jobj, jlong jhandle) { + return reinterpret_cast(jhandle)->create_if_missing; +} diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index d277460c5..a90b82514 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -77,5 +77,37 @@ class RocksDBExceptionJni { } }; +class OptionsJni { + public: + // Get the java class id of org.rocksdb.Options. + static jclass getJClass(JNIEnv* env) { + static jclass jclazz = env->FindClass("org/rocksdb/Options"); + assert(jclazz != nullptr); + return jclazz; + } + + // Get the field id of the member variable of org.rocksdb.Options + // that stores the pointer to rocksdb::Options + static jfieldID getHandleFieldID(JNIEnv* env) { + static jfieldID fid = env->GetFieldID( + getJClass(env), "nativeHandle_", "J"); + assert(fid != nullptr); + return fid; + } + + // Get the pointer to rocksdb::Options + static rocksdb::Options* getHandle(JNIEnv* env, jobject jobj) { + return reinterpret_cast( + env->GetLongField(jobj, getHandleFieldID(env))); + } + + // Pass the rocksdb::Options pointer to the java side. + static void setHandle(JNIEnv* env, jobject jobj, rocksdb::Options* 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 d43fa5719..b5d42c0c7 100644 --- a/java/rocksjni/rocksjni.cc +++ b/java/rocksjni/rocksjni.cc @@ -15,27 +15,43 @@ #include "rocksjni/portal.h" #include "rocksdb/db.h" +void rocksdb_open_helper( + JNIEnv* env, jobject java_db, jstring jdb_path, const rocksdb::Options& opt) { + rocksdb::DB* db; + + const char* db_path = env->GetStringUTFChars(jdb_path, 0); + rocksdb::Status s = rocksdb::DB::Open(opt, db_path, &db); + env->ReleaseStringUTFChars(jdb_path, db_path); + + if (s.ok()) { + rocksdb::RocksDBJni::setHandle(env, java_db, db); + return; + } + rocksdb::RocksDBExceptionJni::ThrowNew(env, s); +} + /* * Class: org_rocksdb_RocksDB * Method: open0 * Signature: (Ljava/lang/String;)V */ void Java_org_rocksdb_RocksDB_open0( - JNIEnv* env, jobject java_db, jstring jdb_path) { - rocksdb::DB* db; + JNIEnv* env, jobject jdb, jstring jdb_path) { rocksdb::Options options; options.create_if_missing = true; - jboolean isCopy = false; - const char* db_path = env->GetStringUTFChars(jdb_path, &isCopy); - rocksdb::Status s = rocksdb::DB::Open(options, db_path, &db); - env->ReleaseStringUTFChars(jdb_path, db_path); + rocksdb_open_helper(env, jdb, jdb_path, options); +} - if (s.ok()) { - rocksdb::RocksDBJni::setHandle(env, java_db, db); - return; - } - rocksdb::RocksDBExceptionJni::ThrowNew(env, s); +/* + * Class: org_rocksdb_RocksDB + * Method: open + * Signature: (JLjava/lang/String;)V + */ +void Java_org_rocksdb_RocksDB_open( + JNIEnv* env, jobject jdb, jlong jopt_handle, jstring jdb_path) { + auto options = reinterpret_cast(jopt_handle); + rocksdb_open_helper(env, jdb, jdb_path, *options); } /*