1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // This file implements the "bridge" between Java and C++ and enables
7 // calling c++ rocksdb::TtlDB methods.
8 // from Java side.
9
10 #include <jni.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <memory>
14 #include <string>
15 #include <vector>
16
17 #include "include/org_rocksdb_TtlDB.h"
18 #include "rocksdb/utilities/db_ttl.h"
19 #include "rocksjni/portal.h"
20
21 /*
22 * Class: org_rocksdb_TtlDB
23 * Method: open
24 * Signature: (JLjava/lang/String;IZ)J
25 */
Java_org_rocksdb_TtlDB_open(JNIEnv * env,jclass,jlong joptions_handle,jstring jdb_path,jint jttl,jboolean jread_only)26 jlong Java_org_rocksdb_TtlDB_open(
27 JNIEnv* env, jclass, jlong joptions_handle, jstring jdb_path, jint jttl,
28 jboolean jread_only) {
29 const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
30 if (db_path == nullptr) {
31 // exception thrown: OutOfMemoryError
32 return 0;
33 }
34
35 auto* opt = reinterpret_cast<rocksdb::Options*>(joptions_handle);
36 rocksdb::DBWithTTL* db = nullptr;
37 rocksdb::Status s =
38 rocksdb::DBWithTTL::Open(*opt, db_path, &db, jttl, jread_only);
39 env->ReleaseStringUTFChars(jdb_path, db_path);
40
41 // as TTLDB extends RocksDB on the java side, we can reuse
42 // the RocksDB portal here.
43 if (s.ok()) {
44 return reinterpret_cast<jlong>(db);
45 } else {
46 rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
47 return 0;
48 }
49 }
50
51 /*
52 * Class: org_rocksdb_TtlDB
53 * Method: openCF
54 * Signature: (JLjava/lang/String;[[B[J[IZ)[J
55 */
Java_org_rocksdb_TtlDB_openCF(JNIEnv * env,jclass,jlong jopt_handle,jstring jdb_path,jobjectArray jcolumn_names,jlongArray jcolumn_options,jintArray jttls,jboolean jread_only)56 jlongArray Java_org_rocksdb_TtlDB_openCF(
57 JNIEnv* env, jclass, jlong jopt_handle, jstring jdb_path,
58 jobjectArray jcolumn_names, jlongArray jcolumn_options,
59 jintArray jttls, jboolean jread_only) {
60 const char* db_path = env->GetStringUTFChars(jdb_path, nullptr);
61 if (db_path == nullptr) {
62 // exception thrown: OutOfMemoryError
63 return 0;
64 }
65
66 const jsize len_cols = env->GetArrayLength(jcolumn_names);
67 jlong* jco = env->GetLongArrayElements(jcolumn_options, nullptr);
68 if (jco == nullptr) {
69 // exception thrown: OutOfMemoryError
70 env->ReleaseStringUTFChars(jdb_path, db_path);
71 return nullptr;
72 }
73
74 std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
75 jboolean has_exception = JNI_FALSE;
76 rocksdb::JniUtil::byteStrings<std::string>(
77 env, jcolumn_names,
78 [](const char* str_data, const size_t str_len) {
79 return std::string(str_data, str_len);
80 },
81 [&jco, &column_families](size_t idx, std::string cf_name) {
82 rocksdb::ColumnFamilyOptions* cf_options =
83 reinterpret_cast<rocksdb::ColumnFamilyOptions*>(jco[idx]);
84 column_families.push_back(
85 rocksdb::ColumnFamilyDescriptor(cf_name, *cf_options));
86 },
87 &has_exception);
88
89 env->ReleaseLongArrayElements(jcolumn_options, jco, JNI_ABORT);
90
91 if (has_exception == JNI_TRUE) {
92 // exception occurred
93 env->ReleaseStringUTFChars(jdb_path, db_path);
94 return nullptr;
95 }
96
97 std::vector<int32_t> ttl_values;
98 jint* jttlv = env->GetIntArrayElements(jttls, nullptr);
99 if (jttlv == nullptr) {
100 // exception thrown: OutOfMemoryError
101 env->ReleaseStringUTFChars(jdb_path, db_path);
102 return nullptr;
103 }
104 const jsize len_ttls = env->GetArrayLength(jttls);
105 for (jsize i = 0; i < len_ttls; i++) {
106 ttl_values.push_back(jttlv[i]);
107 }
108 env->ReleaseIntArrayElements(jttls, jttlv, JNI_ABORT);
109
110 auto* opt = reinterpret_cast<rocksdb::DBOptions*>(jopt_handle);
111 std::vector<rocksdb::ColumnFamilyHandle*> handles;
112 rocksdb::DBWithTTL* db = nullptr;
113 rocksdb::Status s = rocksdb::DBWithTTL::Open(
114 *opt, db_path, column_families, &handles, &db, ttl_values, jread_only);
115
116 // we have now finished with db_path
117 env->ReleaseStringUTFChars(jdb_path, db_path);
118
119 // check if open operation was successful
120 if (s.ok()) {
121 const jsize resultsLen = 1 + len_cols; // db handle + column family handles
122 std::unique_ptr<jlong[]> results =
123 std::unique_ptr<jlong[]>(new jlong[resultsLen]);
124 results[0] = reinterpret_cast<jlong>(db);
125 for (int i = 1; i <= len_cols; i++) {
126 results[i] = reinterpret_cast<jlong>(handles[i - 1]);
127 }
128
129 jlongArray jresults = env->NewLongArray(resultsLen);
130 if (jresults == nullptr) {
131 // exception thrown: OutOfMemoryError
132 return nullptr;
133 }
134
135 env->SetLongArrayRegion(jresults, 0, resultsLen, results.get());
136 if (env->ExceptionCheck()) {
137 // exception thrown: ArrayIndexOutOfBoundsException
138 env->DeleteLocalRef(jresults);
139 return nullptr;
140 }
141
142 return jresults;
143 } else {
144 rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
145 return NULL;
146 }
147 }
148
149 /*
150 * Class: org_rocksdb_TtlDB
151 * Method: disposeInternal
152 * Signature: (J)V
153 */
Java_org_rocksdb_TtlDB_disposeInternal(JNIEnv *,jobject,jlong jhandle)154 void Java_org_rocksdb_TtlDB_disposeInternal(
155 JNIEnv*, jobject, jlong jhandle) {
156 auto* ttl_db = reinterpret_cast<rocksdb::DBWithTTL*>(jhandle);
157 assert(ttl_db != nullptr);
158 delete ttl_db;
159 }
160
161 /*
162 * Class: org_rocksdb_TtlDB
163 * Method: closeDatabase
164 * Signature: (J)V
165 */
Java_org_rocksdb_TtlDB_closeDatabase(JNIEnv *,jclass,jlong)166 void Java_org_rocksdb_TtlDB_closeDatabase(
167 JNIEnv* /* env */, jclass, jlong /* jhandle */) {
168 //auto* ttl_db = reinterpret_cast<rocksdb::DBWithTTL*>(jhandle);
169 //assert(ttl_db != nullptr);
170 //rocksdb::Status s = ttl_db->Close();
171 //rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
172
173 //TODO(AR) this is disabled until https://github.com/facebook/rocksdb/issues/4818 is resolved!
174 }
175
176 /*
177 * Class: org_rocksdb_TtlDB
178 * Method: createColumnFamilyWithTtl
179 * Signature: (JLorg/rocksdb/ColumnFamilyDescriptor;[BJI)J;
180 */
Java_org_rocksdb_TtlDB_createColumnFamilyWithTtl(JNIEnv * env,jobject,jlong jdb_handle,jbyteArray jcolumn_name,jlong jcolumn_options,jint jttl)181 jlong Java_org_rocksdb_TtlDB_createColumnFamilyWithTtl(
182 JNIEnv* env, jobject, jlong jdb_handle, jbyteArray jcolumn_name,
183 jlong jcolumn_options, jint jttl) {
184 jbyte* cfname = env->GetByteArrayElements(jcolumn_name, nullptr);
185 if (cfname == nullptr) {
186 // exception thrown: OutOfMemoryError
187 return 0;
188 }
189 const jsize len = env->GetArrayLength(jcolumn_name);
190
191 auto* cfOptions =
192 reinterpret_cast<rocksdb::ColumnFamilyOptions*>(jcolumn_options);
193
194 auto* db_handle = reinterpret_cast<rocksdb::DBWithTTL*>(jdb_handle);
195 rocksdb::ColumnFamilyHandle* handle;
196 rocksdb::Status s = db_handle->CreateColumnFamilyWithTtl(
197 *cfOptions, std::string(reinterpret_cast<char*>(cfname), len), &handle,
198 jttl);
199
200 env->ReleaseByteArrayElements(jcolumn_name, cfname, 0);
201
202 if (s.ok()) {
203 return reinterpret_cast<jlong>(handle);
204 }
205 rocksdb::RocksDBExceptionJni::ThrowNew(env, s);
206 return 0;
207 }
208