diff --git a/Makefile b/Makefile index 4307773dcb..8712ac3b56 100644 --- a/Makefile +++ b/Makefile @@ -422,7 +422,7 @@ ldb: tools/ldb.o $(LIBOBJECTS) # --------------------------------------------------------------------------- # Jni stuff # --------------------------------------------------------------------------- -JNI_NATIVE_SOURCES = ./java/rocksjni/rocksjni.cc ./java/rocksjni/options.cc ./java/rocksjni/write_batch.cc +JNI_NATIVE_SOURCES = ./java/rocksjni/*.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 0f971433f6..c4b80b3e98 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 +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 NATIVE_INCLUDE = ./include ROCKSDB_JAR = rocksdbjni.jar @@ -21,7 +21,9 @@ sample: java @rm -rf /tmp/rocksdbjni_not_found test: java + javac org/rocksdb/test/*.java java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.WriteBatchTest + java -ea -Djava.library.path=.:../ -cp "$(ROCKSDB_JAR):.:./*" org.rocksdb.test.BackupableDBTest db_bench: java javac org/rocksdb/benchmark/*.java diff --git a/java/org/rocksdb/BackupableDB.java b/java/org/rocksdb/BackupableDB.java new file mode 100644 index 0000000000..100680ebd4 --- /dev/null +++ b/java/org/rocksdb/BackupableDB.java @@ -0,0 +1,84 @@ +// 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; + +/** + * A subclass of RocksDB which supports backup-related operations. + * + * @see BackupableDBOptions + */ +public class BackupableDB extends RocksDB { + /** + * Open a BackupableDB under the specified path. + * Note that the backup path should be set properly in the + * input BackupableDBOptions. + * + * @param opt options for db. + * @param bopt backup related options. + * @param the db path for storing data. The path for storing + * backup should be specified in the BackupableDBOptions. + * @return reference to the opened BackupableDB. + */ + public static BackupableDB open( + Options opt, BackupableDBOptions bopt, String db_path) + throws RocksDBException { + // since BackupableDB c++ will handle the life cycle of + // the returned RocksDB of RocksDB.open(), here we store + // it as a BackupableDB member variable to avoid GC. + BackupableDB bdb = new BackupableDB(RocksDB.open(opt, db_path)); + bdb.open(bdb.db_.nativeHandle_, bopt.nativeHandle_); + + return bdb; + } + + /** + * Captures the state of the database in the latest backup. + * Note that this function is not thread-safe. + * + * @param flushBeforeBackup if true, then all data will be flushed + * before creating backup. + */ + public void createNewBackup(boolean flushBeforeBackup) { + createNewBackup(nativeHandle_, flushBeforeBackup); + } + + + /** + * Close the BackupableDB instance and release resource. + * + * Internally, BackupableDB owns the rocksdb::DB pointer to its + * associated RocksDB. The release of that RocksDB pointer is + * handled in the destructor of the c++ rocksdb::BackupableDB and + * should be transparent to Java developers. + */ + @Override public synchronized void close() { + if (isOpened()) { + super.close0(); + } + } + + /** + * A protected construction that will be used in the static factory + * method BackupableDB.open(). + */ + protected BackupableDB(RocksDB db) { + super(); + db_ = db; + } + + @Override protected void finalize() { + close(); + } + + private boolean isOpened() { + return nativeHandle_ != 0; + } + + protected native void open(long rocksDBHandle, long backupDBOptionsHandle); + protected native void createNewBackup(long handle, boolean flag); + + private final RocksDB db_; +} diff --git a/java/org/rocksdb/BackupableDBOptions.java b/java/org/rocksdb/BackupableDBOptions.java new file mode 100644 index 0000000000..209c2bc478 --- /dev/null +++ b/java/org/rocksdb/BackupableDBOptions.java @@ -0,0 +1,52 @@ +// 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; + +/** + * BackupableDBOptions to control the behavior of a backupable database. + * It will be used during the creation of a BackupableDB. + * + * Note that dispose() must be called before an Options instance + * become out-of-scope to release the allocated memory in c++. + */ +public class BackupableDBOptions { + public BackupableDBOptions(String path) { + newBackupableDBOptions(path); + } + + /** + * Returns the path to the BackupableDB directory. + * + * @return the path to the BackupableDB directory. + */ + public String backupDir() { + assert(isInitialized()); + return backupDir(nativeHandle_); + } + + /** + * Release the memory allocated for the current instance + * in the c++ side. + */ + public synchronized void dispose() { + if (isInitialized()) { + dispose(nativeHandle_); + } + } + + @Override protected void finalize() { + dispose(); + } + + boolean isInitialized() { + return nativeHandle_ != 0; + } + + private native void newBackupableDBOptions(String path); + private native String backupDir(long handle); + private native void dispose(long handle); + long nativeHandle_; +} diff --git a/java/org/rocksdb/Options.java b/java/org/rocksdb/Options.java index 13c05bc3f0..256318eae1 100644 --- a/java/org/rocksdb/Options.java +++ b/java/org/rocksdb/Options.java @@ -228,6 +228,10 @@ public class Options { } } + @Override protected void finalize() { + dispose(); + } + private boolean isInitialized() { return (nativeHandle_ != 0); } diff --git a/java/org/rocksdb/RocksDB.java b/java/org/rocksdb/RocksDB.java index 896c37012b..1d6f0fb4e3 100644 --- a/java/org/rocksdb/RocksDB.java +++ b/java/org/rocksdb/RocksDB.java @@ -145,33 +145,33 @@ public class RocksDB { /** * Private constructor. */ - private RocksDB() { + protected RocksDB() { nativeHandle_ = 0; } // native methods - private native void open( + protected native void open( long optionsHandle, long cacheSize, String path) throws RocksDBException; - private native void put( + protected native void put( long handle, byte[] key, int keyLen, byte[] value, int valueLen) throws RocksDBException; - private native void put( + protected native void put( long handle, long writeOptHandle, byte[] key, int keyLen, byte[] value, int valueLen) throws RocksDBException; - private native void write( + protected native void write( long writeOptHandle, long batchHandle) throws RocksDBException; - private native int get( + protected native int get( long handle, byte[] key, int keyLen, byte[] value, int valueLen) throws RocksDBException; - private native byte[] get( + protected native byte[] get( long handle, byte[] key, int keyLen) throws RocksDBException; - private native void remove( + protected native void remove( long handle, byte[] key, int keyLen) throws RocksDBException; - private native void remove( + protected native void remove( long handle, long writeOptHandle, byte[] key, int keyLen) throws RocksDBException; - private native void close0(); + protected native void close0(); - private long nativeHandle_; + protected long nativeHandle_; } diff --git a/java/org/rocksdb/test/BackupableDBTest.java b/java/org/rocksdb/test/BackupableDBTest.java new file mode 100644 index 0000000000..8af51dd161 --- /dev/null +++ b/java/org/rocksdb/test/BackupableDBTest.java @@ -0,0 +1,41 @@ +// 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.test; + +import org.rocksdb.*; + +public class BackupableDBTest { + static final String db_path = "/tmp/backupablejni_db"; + static final String backup_path = "/tmp/backupablejni_db_backup"; + static { + System.loadLibrary("rocksdbjni"); + } + public static void main(String[] args) { + + Options opt = new Options(); + opt.setCreateIfMissing(true); + + BackupableDBOptions bopt = new BackupableDBOptions(backup_path); + BackupableDB bdb = null; + + try { + bdb = BackupableDB.open(opt, bopt, db_path); + bdb.put("hello".getBytes(), "BackupableDB".getBytes()); + bdb.createNewBackup(true); + byte[] value = bdb.get("hello".getBytes()); + assert(new String(value).equals("BackupableDB")); + } catch (RocksDBException e) { + System.err.format("[ERROR]: %s%n", e); + e.printStackTrace(); + } finally { + opt.dispose(); + bopt.dispose(); + if (bdb != null) { + bdb.close(); + } + } + } +} diff --git a/java/rocksjni/backupablejni.cc b/java/rocksjni/backupablejni.cc new file mode 100644 index 0000000000..8b57a0c628 --- /dev/null +++ b/java/rocksjni/backupablejni.cc @@ -0,0 +1,85 @@ +// 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::DB methods from Java side. + +#include +#include +#include +#include + +#include "include/org_rocksdb_BackupableDB.h" +#include "include/org_rocksdb_BackupableDBOptions.h" +#include "rocksjni/portal.h" +#include "utilities/backupable_db.h" + +/* + * Class: org_rocksdb_BackupableDB + * Method: open + * Signature: (JJ)V + */ +void Java_org_rocksdb_BackupableDB_open( + JNIEnv* env, jobject jbdb, jlong jdb_handle, jlong jopt_handle) { + auto db = reinterpret_cast(jdb_handle); + auto opt = reinterpret_cast(jopt_handle); + auto bdb = new rocksdb::BackupableDB(db, *opt); + + // as BackupableDB extends RocksDB on the java side, we can reuse + // the RocksDB portal here. + rocksdb::RocksDBJni::setHandle(env, jbdb, bdb); +} + +/* + * Class: org_rocksdb_BackupableDB + * Method: createNewBackup + * Signature: (JZ)V + */ +void Java_org_rocksdb_BackupableDB_createNewBackup( + JNIEnv* env, jobject jbdb, jlong jhandle, jboolean jflag) { + reinterpret_cast(jhandle)->CreateNewBackup(jflag); +} + +/////////////////////////////////////////////////////////////////////////// +// BackupDBOptions + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: newBackupableDBOptions + * Signature: (Ljava/lang/String;)V + */ +void Java_org_rocksdb_BackupableDBOptions_newBackupableDBOptions( + JNIEnv* env, jobject jobj, jstring jpath) { + const char* cpath = env->GetStringUTFChars(jpath, 0); + auto bopt = new rocksdb::BackupableDBOptions(cpath); + env->ReleaseStringUTFChars(jpath, cpath); + + rocksdb::BackupableDBOptionsJni::setHandle(env, jobj, bopt); +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: backupDir + * Signature: (J)Ljava/lang/String; + */ +jstring Java_org_rocksdb_BackupableDBOptions_backupDir( + JNIEnv* env, jobject jopt, jlong jhandle, jstring jpath) { + auto bopt = reinterpret_cast(jhandle); + return env->NewStringUTF(bopt->backup_dir.c_str()); +} + +/* + * Class: org_rocksdb_BackupableDBOptions + * Method: dispose + * Signature: (J)V + */ +void Java_org_rocksdb_BackupableDBOptions_dispose( + JNIEnv* env, jobject jopt, jlong jhandle) { + auto bopt = reinterpret_cast(jhandle); + assert(bopt); + delete bopt; + + rocksdb::BackupableDBOptionsJni::setHandle(env, jopt, nullptr); +} diff --git a/java/rocksjni/portal.h b/java/rocksjni/portal.h index 5b0524aece..847700c920 100644 --- a/java/rocksjni/portal.h +++ b/java/rocksjni/portal.h @@ -12,6 +12,7 @@ #include #include "rocksdb/db.h" +#include "utilities/backupable_db.h" namespace rocksdb { @@ -170,5 +171,38 @@ class WriteBatchJni { reinterpret_cast(wb)); } }; + +class BackupableDBOptionsJni { + public: + // Get the java class id of org.rocksdb.BackupableDBOptions. + static jclass getJClass(JNIEnv* env) { + static jclass jclazz = env->FindClass("org/rocksdb/BackupableDBOptions"); + assert(jclazz != nullptr); + return jclazz; + } + + // Get the field id of the member variable of org.rocksdb.BackupableDBOptions + // that stores the pointer to rocksdb::BackupableDBOptions + static jfieldID getHandleFieldID(JNIEnv* env) { + static jfieldID fid = env->GetFieldID( + getJClass(env), "nativeHandle_", "J"); + assert(fid != nullptr); + return fid; + } + + // Get the pointer to rocksdb::BackupableDBOptions + static rocksdb::BackupableDBOptions* getHandle(JNIEnv* env, jobject jobj) { + return reinterpret_cast( + env->GetLongField(jobj, getHandleFieldID(env))); + } + + // Pass the rocksdb::BackupableDBOptions pointer to the java side. + static void setHandle( + JNIEnv* env, jobject jobj, rocksdb::BackupableDBOptions* op) { + env->SetLongField( + jobj, getHandleFieldID(env), + reinterpret_cast(op)); + } +}; } // namespace rocksdb #endif // JAVA_ROCKSJNI_PORTAL_H_