// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). // // This file implements the callback "bridge" between Java and C++ for // ROCKSDB_NAMESPACE::Comparator. #include "rocksjni/writebatchhandlerjnicallback.h" #include "rocksjni/portal.h" namespace ROCKSDB_NAMESPACE { WriteBatchHandlerJniCallback::WriteBatchHandlerJniCallback( JNIEnv* env, jobject jWriteBatchHandler) : JniCallback(env, jWriteBatchHandler), m_env(env) { m_jPutCfMethodId = WriteBatchHandlerJni::getPutCfMethodId(env); if (m_jPutCfMethodId == nullptr) { // exception thrown return; } m_jPutMethodId = WriteBatchHandlerJni::getPutMethodId(env); if (m_jPutMethodId == nullptr) { // exception thrown return; } m_jMergeCfMethodId = WriteBatchHandlerJni::getMergeCfMethodId(env); if (m_jMergeCfMethodId == nullptr) { // exception thrown return; } m_jMergeMethodId = WriteBatchHandlerJni::getMergeMethodId(env); if (m_jMergeMethodId == nullptr) { // exception thrown return; } m_jDeleteCfMethodId = WriteBatchHandlerJni::getDeleteCfMethodId(env); if (m_jDeleteCfMethodId == nullptr) { // exception thrown return; } m_jDeleteMethodId = WriteBatchHandlerJni::getDeleteMethodId(env); if (m_jDeleteMethodId == nullptr) { // exception thrown return; } m_jSingleDeleteCfMethodId = WriteBatchHandlerJni::getSingleDeleteCfMethodId(env); if (m_jSingleDeleteCfMethodId == nullptr) { // exception thrown return; } m_jSingleDeleteMethodId = WriteBatchHandlerJni::getSingleDeleteMethodId(env); if (m_jSingleDeleteMethodId == nullptr) { // exception thrown return; } m_jDeleteRangeCfMethodId = WriteBatchHandlerJni::getDeleteRangeCfMethodId(env); if (m_jDeleteRangeCfMethodId == nullptr) { // exception thrown return; } m_jDeleteRangeMethodId = WriteBatchHandlerJni::getDeleteRangeMethodId(env); if (m_jDeleteRangeMethodId == nullptr) { // exception thrown return; } m_jLogDataMethodId = WriteBatchHandlerJni::getLogDataMethodId(env); if (m_jLogDataMethodId == nullptr) { // exception thrown return; } m_jPutBlobIndexCfMethodId = WriteBatchHandlerJni::getPutBlobIndexCfMethodId(env); if (m_jPutBlobIndexCfMethodId == nullptr) { // exception thrown return; } m_jMarkBeginPrepareMethodId = WriteBatchHandlerJni::getMarkBeginPrepareMethodId(env); if (m_jMarkBeginPrepareMethodId == nullptr) { // exception thrown return; } m_jMarkEndPrepareMethodId = WriteBatchHandlerJni::getMarkEndPrepareMethodId(env); if (m_jMarkEndPrepareMethodId == nullptr) { // exception thrown return; } m_jMarkNoopMethodId = WriteBatchHandlerJni::getMarkNoopMethodId(env); if (m_jMarkNoopMethodId == nullptr) { // exception thrown return; } m_jMarkRollbackMethodId = WriteBatchHandlerJni::getMarkRollbackMethodId(env); if (m_jMarkRollbackMethodId == nullptr) { // exception thrown return; } m_jMarkCommitMethodId = WriteBatchHandlerJni::getMarkCommitMethodId(env); if (m_jMarkCommitMethodId == nullptr) { // exception thrown return; } m_jMarkCommitWithTimestampMethodId = WriteBatchHandlerJni::getMarkCommitWithTimestampMethodId(env); if (m_jMarkCommitWithTimestampMethodId == nullptr) { // exception thrown return; } m_jContinueMethodId = WriteBatchHandlerJni::getContinueMethodId(env); if (m_jContinueMethodId == nullptr) { // exception thrown return; } } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::PutCF( uint32_t column_family_id, const Slice& key, const Slice& value) { auto put = [this, column_family_id](jbyteArray j_key, jbyteArray j_value) { m_env->CallVoidMethod(m_jcallback_obj, m_jPutCfMethodId, static_cast(column_family_id), j_key, j_value); }; auto status = WriteBatchHandlerJniCallback::kv_op(key, value, put); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } void WriteBatchHandlerJniCallback::Put(const Slice& key, const Slice& value) { auto put = [this](jbyteArray j_key, jbyteArray j_value) { m_env->CallVoidMethod(m_jcallback_obj, m_jPutMethodId, j_key, j_value); }; WriteBatchHandlerJniCallback::kv_op(key, value, put); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MergeCF( uint32_t column_family_id, const Slice& key, const Slice& value) { auto merge = [this, column_family_id](jbyteArray j_key, jbyteArray j_value) { m_env->CallVoidMethod(m_jcallback_obj, m_jMergeCfMethodId, static_cast(column_family_id), j_key, j_value); }; auto status = WriteBatchHandlerJniCallback::kv_op(key, value, merge); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } void WriteBatchHandlerJniCallback::Merge(const Slice& key, const Slice& value) { auto merge = [this](jbyteArray j_key, jbyteArray j_value) { m_env->CallVoidMethod(m_jcallback_obj, m_jMergeMethodId, j_key, j_value); }; WriteBatchHandlerJniCallback::kv_op(key, value, merge); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::DeleteCF( uint32_t column_family_id, const Slice& key) { auto remove = [this, column_family_id](jbyteArray j_key) { m_env->CallVoidMethod(m_jcallback_obj, m_jDeleteCfMethodId, static_cast(column_family_id), j_key); }; auto status = WriteBatchHandlerJniCallback::k_op(key, remove); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } void WriteBatchHandlerJniCallback::Delete(const Slice& key) { auto remove = [this](jbyteArray j_key) { m_env->CallVoidMethod(m_jcallback_obj, m_jDeleteMethodId, j_key); }; WriteBatchHandlerJniCallback::k_op(key, remove); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::SingleDeleteCF( uint32_t column_family_id, const Slice& key) { auto singleDelete = [this, column_family_id](jbyteArray j_key) { m_env->CallVoidMethod(m_jcallback_obj, m_jSingleDeleteCfMethodId, static_cast(column_family_id), j_key); }; auto status = WriteBatchHandlerJniCallback::k_op(key, singleDelete); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } void WriteBatchHandlerJniCallback::SingleDelete(const Slice& key) { auto singleDelete = [this](jbyteArray j_key) { m_env->CallVoidMethod(m_jcallback_obj, m_jSingleDeleteMethodId, j_key); }; WriteBatchHandlerJniCallback::k_op(key, singleDelete); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::DeleteRangeCF( uint32_t column_family_id, const Slice& beginKey, const Slice& endKey) { auto deleteRange = [this, column_family_id](jbyteArray j_beginKey, jbyteArray j_endKey) { m_env->CallVoidMethod(m_jcallback_obj, m_jDeleteRangeCfMethodId, static_cast(column_family_id), j_beginKey, j_endKey); }; auto status = WriteBatchHandlerJniCallback::kv_op(beginKey, endKey, deleteRange); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } void WriteBatchHandlerJniCallback::DeleteRange(const Slice& beginKey, const Slice& endKey) { auto deleteRange = [this](jbyteArray j_beginKey, jbyteArray j_endKey) { m_env->CallVoidMethod(m_jcallback_obj, m_jDeleteRangeMethodId, j_beginKey, j_endKey); }; WriteBatchHandlerJniCallback::kv_op(beginKey, endKey, deleteRange); } void WriteBatchHandlerJniCallback::LogData(const Slice& blob) { auto logData = [this](jbyteArray j_blob) { m_env->CallVoidMethod(m_jcallback_obj, m_jLogDataMethodId, j_blob); }; WriteBatchHandlerJniCallback::k_op(blob, logData); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::PutBlobIndexCF( uint32_t column_family_id, const Slice& key, const Slice& value) { auto putBlobIndex = [this, column_family_id](jbyteArray j_key, jbyteArray j_value) { m_env->CallVoidMethod(m_jcallback_obj, m_jPutBlobIndexCfMethodId, static_cast(column_family_id), j_key, j_value); }; auto status = WriteBatchHandlerJniCallback::kv_op(key, value, putBlobIndex); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MarkBeginPrepare( bool unprepare) { #ifndef DEBUG (void)unprepare; #else assert(!unprepare); #endif m_env->CallVoidMethod(m_jcallback_obj, m_jMarkBeginPrepareMethodId); // check for Exception, in-particular RocksDBException if (m_env->ExceptionCheck()) { // exception thrown jthrowable exception = m_env->ExceptionOccurred(); std::unique_ptr status = ROCKSDB_NAMESPACE::RocksDBExceptionJni::toCppStatus(m_env, exception); if (status == nullptr) { // unkown status or exception occurred extracting status m_env->ExceptionDescribe(); return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) probably need a // better error code here } else { m_env->ExceptionClear(); // clear the exception, as we have extracted the // status return ROCKSDB_NAMESPACE::Status(*status); } } return ROCKSDB_NAMESPACE::Status::OK(); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MarkEndPrepare( const Slice& xid) { auto markEndPrepare = [this](jbyteArray j_xid) { m_env->CallVoidMethod(m_jcallback_obj, m_jMarkEndPrepareMethodId, j_xid); }; auto status = WriteBatchHandlerJniCallback::k_op(xid, markEndPrepare); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MarkNoop( bool empty_batch) { m_env->CallVoidMethod(m_jcallback_obj, m_jMarkNoopMethodId, static_cast(empty_batch)); // check for Exception, in-particular RocksDBException if (m_env->ExceptionCheck()) { // exception thrown jthrowable exception = m_env->ExceptionOccurred(); std::unique_ptr status = ROCKSDB_NAMESPACE::RocksDBExceptionJni::toCppStatus(m_env, exception); if (status == nullptr) { // unkown status or exception occurred extracting status m_env->ExceptionDescribe(); return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) probably need a // better error code here } else { m_env->ExceptionClear(); // clear the exception, as we have extracted the // status return ROCKSDB_NAMESPACE::Status(*status); } } return ROCKSDB_NAMESPACE::Status::OK(); } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MarkRollback( const Slice& xid) { auto markRollback = [this](jbyteArray j_xid) { m_env->CallVoidMethod(m_jcallback_obj, m_jMarkRollbackMethodId, j_xid); }; auto status = WriteBatchHandlerJniCallback::k_op(xid, markRollback); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MarkCommit( const Slice& xid) { auto markCommit = [this](jbyteArray j_xid) { m_env->CallVoidMethod(m_jcallback_obj, m_jMarkCommitMethodId, j_xid); }; auto status = WriteBatchHandlerJniCallback::k_op(xid, markCommit); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } ROCKSDB_NAMESPACE::Status WriteBatchHandlerJniCallback::MarkCommitWithTimestamp( const Slice& xid, const Slice& ts) { auto markCommitWithTimestamp = [this](jbyteArray j_xid, jbyteArray j_ts) { m_env->CallVoidMethod(m_jcallback_obj, m_jMarkCommitWithTimestampMethodId, j_xid, j_ts); }; auto status = WriteBatchHandlerJniCallback::kv_op(xid, ts, markCommitWithTimestamp); if (status == nullptr) { return ROCKSDB_NAMESPACE::Status::OK(); // TODO(AR) what to do if there is // an Exception but we don't know // the ROCKSDB_NAMESPACE::Status? } else { return ROCKSDB_NAMESPACE::Status(*status); } } bool WriteBatchHandlerJniCallback::Continue() { jboolean jContinue = m_env->CallBooleanMethod(m_jcallback_obj, m_jContinueMethodId); if (m_env->ExceptionCheck()) { // exception thrown m_env->ExceptionDescribe(); } return static_cast(jContinue == JNI_TRUE); } std::unique_ptr WriteBatchHandlerJniCallback::kv_op( const Slice& key, const Slice& value, std::function kvFn) { const jbyteArray j_key = JniUtil::copyBytes(m_env, key); if (j_key == nullptr) { // exception thrown if (m_env->ExceptionCheck()) { m_env->ExceptionDescribe(); } return nullptr; } const jbyteArray j_value = JniUtil::copyBytes(m_env, value); if (j_value == nullptr) { // exception thrown if (m_env->ExceptionCheck()) { m_env->ExceptionDescribe(); } if (j_key != nullptr) { m_env->DeleteLocalRef(j_key); } return nullptr; } kvFn(j_key, j_value); // check for Exception, in-particular RocksDBException if (m_env->ExceptionCheck()) { if (j_value != nullptr) { m_env->DeleteLocalRef(j_value); } if (j_key != nullptr) { m_env->DeleteLocalRef(j_key); } // exception thrown jthrowable exception = m_env->ExceptionOccurred(); std::unique_ptr status = ROCKSDB_NAMESPACE::RocksDBExceptionJni::toCppStatus(m_env, exception); if (status == nullptr) { // unkown status or exception occurred extracting status m_env->ExceptionDescribe(); return nullptr; } else { m_env->ExceptionClear(); // clear the exception, as we have extracted the // status return status; } } if (j_value != nullptr) { m_env->DeleteLocalRef(j_value); } if (j_key != nullptr) { m_env->DeleteLocalRef(j_key); } // all OK return std::unique_ptr( new ROCKSDB_NAMESPACE::Status(ROCKSDB_NAMESPACE::Status::OK())); } std::unique_ptr WriteBatchHandlerJniCallback::k_op( const Slice& key, std::function kFn) { const jbyteArray j_key = JniUtil::copyBytes(m_env, key); if (j_key == nullptr) { // exception thrown if (m_env->ExceptionCheck()) { m_env->ExceptionDescribe(); } return nullptr; } kFn(j_key); // check for Exception, in-particular RocksDBException if (m_env->ExceptionCheck()) { if (j_key != nullptr) { m_env->DeleteLocalRef(j_key); } // exception thrown jthrowable exception = m_env->ExceptionOccurred(); std::unique_ptr status = ROCKSDB_NAMESPACE::RocksDBExceptionJni::toCppStatus(m_env, exception); if (status == nullptr) { // unkown status or exception occurred extracting status m_env->ExceptionDescribe(); return nullptr; } else { m_env->ExceptionClear(); // clear the exception, as we have extracted the // status return status; } } if (j_key != nullptr) { m_env->DeleteLocalRef(j_key); } // all OK return std::unique_ptr( new ROCKSDB_NAMESPACE::Status(ROCKSDB_NAMESPACE::Status::OK())); } } // namespace ROCKSDB_NAMESPACE