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