hubgeter commented on code in PR #56763:
URL: https://github.com/apache/doris/pull/56763#discussion_r2626028513


##########
be/src/util/jni-util.h:
##########
@@ -72,138 +59,1114 @@ class JniUtil {
                 return status;
             }
         }
-        if (*env == nullptr) {
+        if (*env == nullptr) [[unlikely]] {
             return Status::JniError("Failed to get JNIEnv: it is nullptr.");
         }
         return Status::OK();
     }
 
-    //jclass is generally a local reference.
-    //Method ID and field ID values are forever.
-    //If you want to use the jclass across multiple threads or multiple calls 
into the JNI code you need
-    // to create a global reference to it with GetGlobalClassRef().
-    static Status GetGlobalClassRef(JNIEnv* env, const char* class_str,
-                                    jclass* class_ref) WARN_UNUSED_RESULT;
-
-    static Status LocalToGlobalRef(JNIEnv* env, jobject local_ref,
-                                   jobject* global_ref) WARN_UNUSED_RESULT;
+    static Status Init() { return init_throw_exception(); }
 
     static Status GetJniExceptionMsg(JNIEnv* env, bool log_stack = true,
                                      const std::string& prefix = "") 
WARN_UNUSED_RESULT;
 
-    static jclass jni_util_class() { return jni_util_cl_; }
-    static jmethodID throwable_to_stack_trace_id() { return 
throwable_to_stack_trace_id_; }
+private:
+    static Status GetJNIEnvSlowPath(JNIEnv** env);
+    static Status init_throw_exception() {
+        JNIEnv* env = nullptr;
+        RETURN_IF_ERROR(Jni::Env::Get(&env));
+
+        if (env == nullptr) {
+            return Status::JniError("Failed to get/create JVM");
+        }
+        // Find JniUtil class and create a global ref.
+        jclass local_jni_util_cl = 
env->FindClass("org/apache/doris/common/jni/utils/JniUtil");
+        if (local_jni_util_cl == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find JniUtil class.");
+        }
+        jni_util_cl_ = 
reinterpret_cast<jclass>(env->NewGlobalRef(local_jni_util_cl));
+        if (jni_util_cl_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to create global reference to 
JniUtil class.");
+        }
+        env->DeleteLocalRef(local_jni_util_cl);
+        if (env->ExceptionOccurred()) {
+            return Status::JniError("Failed to delete local reference to 
JniUtil class.");
+        }
+
+        // Throwable toString()
+        throwable_to_string_id_ = env->GetStaticMethodID(
+                jni_util_cl_, "throwableToString", 
"(Ljava/lang/Throwable;)Ljava/lang/String;");
+        if (throwable_to_string_id_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find JniUtil.throwableToString 
method.");
+        }
 
-    static const int64_t INITIAL_RESERVED_BUFFER_SIZE = 1024;
-    // TODO: we need a heuristic strategy to increase buffer size for 
variable-size output.
-    static inline int64_t IncreaseReservedBufferSize(int n) {
-        return INITIAL_RESERVED_BUFFER_SIZE << n;
+        // throwableToStackTrace()
+        throwable_to_stack_trace_id_ = env->GetStaticMethodID(
+                jni_util_cl_, "throwableToStackTrace", 
"(Ljava/lang/Throwable;)Ljava/lang/String;");
+        if (throwable_to_stack_trace_id_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find 
JniUtil.throwableToFullStackTrace method.");
+        }
+        return Status::OK();
     }
-    static Status get_jni_scanner_class(JNIEnv* env, const char* classname, 
jclass* loaded_class);
-    static Status convert_to_java_map(JNIEnv* env, const std::map<std::string, 
std::string>& map,
-                                      jobject* hashmap_object);
-    static Status convert_to_cpp_map(JNIEnv* env, jobject map,
-                                     std::map<std::string, std::string>* 
resultMap);
-    static size_t get_max_jni_heap_memory_size();
-    static Status clean_udf_class_load_cache(const std::string& 
function_signature);
 
 private:
-    static void parse_max_heap_memory_size_from_jvm(JNIEnv* env);
-    static Status GetJNIEnvSlowPath(JNIEnv** env);
-    static Status init_jni_scanner_loader(JNIEnv* env);
+    // Thread-local cache of the JNIEnv for this thread.
+    static __thread JNIEnv* tls_env_;
 
-    static bool jvm_inited_;
-    static jclass internal_exc_cl_;
-    static jclass jni_native_method_exc_cl_;
+    //for exception
     static jclass jni_util_cl_;
     static jmethodID throwable_to_string_id_;
     static jmethodID throwable_to_stack_trace_id_;
-    static jmethodID get_jvm_metrics_id_;
-    static jmethodID get_jvm_threads_id_;
-    static jmethodID get_jmx_json_;
-    // JNI scanner loader
-    static jobject jni_scanner_loader_obj_;
-    static jmethodID jni_scanner_loader_method_;
-    // Thread-local cache of the JNIEnv for this thread.
-    static __thread JNIEnv* tls_env_;
-    static jlong max_jvm_heap_memory_size_;
-    static jmethodID _clean_udf_cache_method_id;
 };
 
-/// Helper class for lifetime management of chars from JNI, releasing JNI 
chars when
-/// destructed
-class JniUtfCharGuard {
+enum RefType { Local, Global };
+
+enum BufferType { Chars, ByteArray };
+
+template <RefType Ref>
+struct RefHelper {};
+
+template <>
+struct RefHelper<Local> {
+    static jobject create(JNIEnv* env, jobject obj) { return 
env->NewLocalRef(obj); }
+
+    static void destroy(JNIEnv* env, jobject obj) { env->DeleteLocalRef(obj); }
+
+    static Status get_env(JNIEnv** env) {
+        // Get the JNIEnv* corresponding to current thread.
+        return Jni::Env::Get(env);
+    }
+};
+
+template <>
+struct RefHelper<Global> {
+    static jobject create(JNIEnv* env, jobject obj) { return 
env->NewGlobalRef(obj); }
+
+    static void destroy(JNIEnv* env, jobject obj) { env->DeleteGlobalRef(obj); 
}
+
+    static Status get_env(JNIEnv** env) { return Jni::Env::Get(env); }
+};
+
+template <RefType Ref>
+class Object;
+
+template <RefType Ref>
+class Class;
+
+class MethodId {
+public:
+    MethodId() = default;
+    bool uninitialized() const { return _id == nullptr; }
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+private:
+    jmethodID _id = nullptr;
+};
+
+class FieldId {
+public:
+    FieldId() = default;
+    bool uninitialized() const { return _id == nullptr; }
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+private:
+    jfieldID _id = nullptr;
+};
+
+enum CallTag {
+    ObjectMethod,
+    IntMethod,
+    LongMethod,
+    VoidMethod,
+    BooleanMethod,

Review Comment:
   I think the additions so far are sufficient for the vast majority of 
scenarios, and there's no need to add other tag that's never been used.



##########
be/src/util/jni-util.h:
##########
@@ -72,138 +59,1114 @@ class JniUtil {
                 return status;
             }
         }
-        if (*env == nullptr) {
+        if (*env == nullptr) [[unlikely]] {
             return Status::JniError("Failed to get JNIEnv: it is nullptr.");
         }
         return Status::OK();
     }
 
-    //jclass is generally a local reference.
-    //Method ID and field ID values are forever.
-    //If you want to use the jclass across multiple threads or multiple calls 
into the JNI code you need
-    // to create a global reference to it with GetGlobalClassRef().
-    static Status GetGlobalClassRef(JNIEnv* env, const char* class_str,
-                                    jclass* class_ref) WARN_UNUSED_RESULT;
-
-    static Status LocalToGlobalRef(JNIEnv* env, jobject local_ref,
-                                   jobject* global_ref) WARN_UNUSED_RESULT;
+    static Status Init() { return init_throw_exception(); }
 
     static Status GetJniExceptionMsg(JNIEnv* env, bool log_stack = true,
                                      const std::string& prefix = "") 
WARN_UNUSED_RESULT;
 
-    static jclass jni_util_class() { return jni_util_cl_; }
-    static jmethodID throwable_to_stack_trace_id() { return 
throwable_to_stack_trace_id_; }
+private:
+    static Status GetJNIEnvSlowPath(JNIEnv** env);
+    static Status init_throw_exception() {
+        JNIEnv* env = nullptr;
+        RETURN_IF_ERROR(Jni::Env::Get(&env));
+
+        if (env == nullptr) {
+            return Status::JniError("Failed to get/create JVM");
+        }
+        // Find JniUtil class and create a global ref.
+        jclass local_jni_util_cl = 
env->FindClass("org/apache/doris/common/jni/utils/JniUtil");
+        if (local_jni_util_cl == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find JniUtil class.");
+        }
+        jni_util_cl_ = 
reinterpret_cast<jclass>(env->NewGlobalRef(local_jni_util_cl));
+        if (jni_util_cl_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to create global reference to 
JniUtil class.");
+        }
+        env->DeleteLocalRef(local_jni_util_cl);
+        if (env->ExceptionOccurred()) {
+            return Status::JniError("Failed to delete local reference to 
JniUtil class.");
+        }
+
+        // Throwable toString()
+        throwable_to_string_id_ = env->GetStaticMethodID(
+                jni_util_cl_, "throwableToString", 
"(Ljava/lang/Throwable;)Ljava/lang/String;");
+        if (throwable_to_string_id_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find JniUtil.throwableToString 
method.");
+        }
 
-    static const int64_t INITIAL_RESERVED_BUFFER_SIZE = 1024;
-    // TODO: we need a heuristic strategy to increase buffer size for 
variable-size output.
-    static inline int64_t IncreaseReservedBufferSize(int n) {
-        return INITIAL_RESERVED_BUFFER_SIZE << n;
+        // throwableToStackTrace()
+        throwable_to_stack_trace_id_ = env->GetStaticMethodID(
+                jni_util_cl_, "throwableToStackTrace", 
"(Ljava/lang/Throwable;)Ljava/lang/String;");
+        if (throwable_to_stack_trace_id_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find 
JniUtil.throwableToFullStackTrace method.");
+        }
+        return Status::OK();
     }
-    static Status get_jni_scanner_class(JNIEnv* env, const char* classname, 
jclass* loaded_class);
-    static Status convert_to_java_map(JNIEnv* env, const std::map<std::string, 
std::string>& map,
-                                      jobject* hashmap_object);
-    static Status convert_to_cpp_map(JNIEnv* env, jobject map,
-                                     std::map<std::string, std::string>* 
resultMap);
-    static size_t get_max_jni_heap_memory_size();
-    static Status clean_udf_class_load_cache(const std::string& 
function_signature);
 
 private:
-    static void parse_max_heap_memory_size_from_jvm(JNIEnv* env);
-    static Status GetJNIEnvSlowPath(JNIEnv** env);
-    static Status init_jni_scanner_loader(JNIEnv* env);
+    // Thread-local cache of the JNIEnv for this thread.
+    static __thread JNIEnv* tls_env_;
 
-    static bool jvm_inited_;
-    static jclass internal_exc_cl_;
-    static jclass jni_native_method_exc_cl_;
+    //for exception
     static jclass jni_util_cl_;
     static jmethodID throwable_to_string_id_;
     static jmethodID throwable_to_stack_trace_id_;
-    static jmethodID get_jvm_metrics_id_;
-    static jmethodID get_jvm_threads_id_;
-    static jmethodID get_jmx_json_;
-    // JNI scanner loader
-    static jobject jni_scanner_loader_obj_;
-    static jmethodID jni_scanner_loader_method_;
-    // Thread-local cache of the JNIEnv for this thread.
-    static __thread JNIEnv* tls_env_;
-    static jlong max_jvm_heap_memory_size_;
-    static jmethodID _clean_udf_cache_method_id;
 };
 
-/// Helper class for lifetime management of chars from JNI, releasing JNI 
chars when
-/// destructed
-class JniUtfCharGuard {
+enum RefType { Local, Global };
+
+enum BufferType { Chars, ByteArray };
+
+template <RefType Ref>
+struct RefHelper {};
+
+template <>
+struct RefHelper<Local> {
+    static jobject create(JNIEnv* env, jobject obj) { return 
env->NewLocalRef(obj); }
+
+    static void destroy(JNIEnv* env, jobject obj) { env->DeleteLocalRef(obj); }
+
+    static Status get_env(JNIEnv** env) {
+        // Get the JNIEnv* corresponding to current thread.
+        return Jni::Env::Get(env);
+    }
+};
+
+template <>
+struct RefHelper<Global> {
+    static jobject create(JNIEnv* env, jobject obj) { return 
env->NewGlobalRef(obj); }
+
+    static void destroy(JNIEnv* env, jobject obj) { env->DeleteGlobalRef(obj); 
}
+
+    static Status get_env(JNIEnv** env) { return Jni::Env::Get(env); }
+};
+
+template <RefType Ref>
+class Object;
+
+template <RefType Ref>
+class Class;
+
+class MethodId {
+public:
+    MethodId() = default;
+    bool uninitialized() const { return _id == nullptr; }
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+private:
+    jmethodID _id = nullptr;
+};
+
+class FieldId {
+public:
+    FieldId() = default;
+    bool uninitialized() const { return _id == nullptr; }
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+private:
+    jfieldID _id = nullptr;
+};
+
+enum CallTag {
+    ObjectMethod,
+    IntMethod,
+    LongMethod,
+    VoidMethod,
+    BooleanMethod,
+
+    StaticObjectMethod,
+    StaticIntMethod,
+    StaticLongMethod,
+    StaticVoidMethod,
+
+    NewObject,
+
+    NonvirtualVoidMethod,
+    NonvirtualObjectMethod,
+    NonvirtualIntMethod,
+    NonvirtualBooleanMethod,
+};
+
+template <CallTag Tag>
+struct CallHelper {};
+
+template <>
+struct CallHelper<ObjectMethod> {
+    static jobject call_impl(JNIEnv* env, jobject obj, jmethodID methodID, 
const jvalue* args) {
+        return env->CallObjectMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<IntMethod> {
+    static jint call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const 
jvalue* args) {
+        return env->CallIntMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jint;
+};
+
+template <>
+struct CallHelper<LongMethod> {
+    static jlong call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const 
jvalue* args) {
+        return env->CallLongMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jlong;
+};
+
+template <>
+struct CallHelper<VoidMethod> {
+    static void call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const 
jvalue* args) {
+        env->CallVoidMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = void;
+};
+
+template <>
+struct CallHelper<BooleanMethod> {
+    static jboolean call_impl(JNIEnv* env, jobject obj, jmethodID methodID, 
const jvalue* args) {
+        return env->CallBooleanMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jboolean;
+};
+
+template <>
+struct CallHelper<StaticObjectMethod> {
+    static jobject call_impl(JNIEnv* env, jclass cls, jmethodID methodID, 
const jvalue* args) {
+        return env->CallStaticObjectMethodA(cls, methodID, args);
+    }
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<StaticIntMethod> {
+    static jint call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const 
jvalue* args) {
+        return env->CallStaticIntMethodA(cls, methodID, args);
+    }
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jint;
+};
+
+template <>
+struct CallHelper<StaticLongMethod> {
+    static jlong call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const 
jvalue* args) {
+        return env->CallStaticLongMethodA(cls, methodID, args);
+    }
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jlong;
+};
+
+template <>
+struct CallHelper<StaticVoidMethod> {
+    static void call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const 
jvalue* args) {
+        return env->CallStaticVoidMethodA(cls, methodID, args);
+    }
+
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = void;
+};
+
+template <>
+struct CallHelper<NewObject> {
+    static jobject call_impl(JNIEnv* env, jclass cls, jmethodID methodID, 
const jvalue* args) {
+        return env->NewObjectA(cls, methodID, args);
+    }
+
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<NonvirtualVoidMethod> {
+    static void call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID 
methodID,
+                          const jvalue* args) {
+        return env->CallNonvirtualVoidMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = void;
+};
+
+template <>
+struct CallHelper<NonvirtualObjectMethod> {
+    static jobject call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID 
methodID,
+                             const jvalue* args) {
+        return env->CallNonvirtualObjectMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<NonvirtualIntMethod> {
+    static jint call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID 
methodID,
+                          const jvalue* args) {
+        return env->CallNonvirtualIntMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jint;
+};
+
+template <>
+struct CallHelper<NonvirtualBooleanMethod> {
+    static jboolean call_impl(JNIEnv* env, jobject obj, jclass clazz, 
jmethodID methodID,
+                              const jvalue* args) {
+        return env->CallNonvirtualBooleanMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jboolean;
+};
+
+template <CallTag tag>
+class FunctionCall {
+public:
+    FunctionCall(FunctionCall&& other) noexcept = default;
+
+    static FunctionCall instance(JNIEnv* env, typename 
CallHelper<tag>::BASE_TYPE base,
+                                 jmethodID method_id) {
+        return FunctionCall(env, base, method_id);
+    }
+
+    /// Pass a primitive arg (eg an integer).
+    /// Multiple arguments may be passed by repeated calls.
+    template <class T>
+        requires std::disjunction_v<std::is_same<T, jboolean>, std::is_same<T, 
jbyte>,
+                                    std::is_same<T, jchar>, std::is_same<T, 
jshort>,
+                                    std::is_same<T, jint>, std::is_same<T, 
jlong>,
+                                    std::is_same<T, jfloat>, std::is_same<T, 
jdouble>>
+    FunctionCall& with_arg(T arg) {
+        jvalue v;
+        std::memset(&v, 0, sizeof(v));
+        if constexpr (std::is_same_v<T, jboolean>) {
+            v.z = arg;
+        } else if constexpr (std::is_same_v<T, jbyte>) {
+            v.b = arg;
+        } else if constexpr (std::is_same_v<T, jchar>) {
+            v.c = arg;
+        } else if constexpr (std::is_same_v<T, jshort>) {
+            v.s = arg;
+        } else if constexpr (std::is_same_v<T, jint>) {
+            v.i = arg;
+        } else if constexpr (std::is_same_v<T, jlong>) {
+            v.j = arg;
+        } else if constexpr (std::is_same_v<T, jfloat>) {
+            v.f = arg;
+        } else if constexpr (std::is_same_v<T, jdouble>) {
+            v.d = arg;
+        } else {
+            static_assert(false);
+        }
+        _args.push_back(v);
+        return *this;
+    }
+
+    template <RefType Ref>
+    FunctionCall& with_arg(const Object<Ref>& obj) WARN_UNUSED_RESULT;
+
+    template <typename ReturnType>
+        requires(std::is_same_v<typename CallHelper<tag>::RETURN_TYPE, 
ReturnType>)
+    Status call(ReturnType* result) {
+        *result = CallHelper<tag>::call_impl(_env, _base, _method, 
_args.data());
+        RETURN_ERROR_IF_EXC(_env);
+        return Status::OK();
+    }
+
+    Status call(Object<Local>* result);
+
+    Status call(Object<Global>* result);
+
+    Status call() {
+        using return_type = typename CallHelper<tag>::RETURN_TYPE;
+        if constexpr (std::disjunction_v<
+                              std::is_same<return_type, jboolean>, 
std::is_same<return_type, jbyte>,
+                              std::is_same<return_type, jchar>, 
std::is_same<return_type, jshort>,
+                              std::is_same<return_type, jint>, 
std::is_same<return_type, jlong>,
+                              std::is_same<return_type, jfloat>, 
std::is_same<return_type, jdouble>,
+                              std::is_same<return_type, void>>) {
+            CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
+            RETURN_ERROR_IF_EXC(_env);
+        } else if constexpr (std::is_same_v<return_type, jobject>) {
+            jobject tmp = CallHelper<tag>::call_impl(_env, _base, _method, 
_args.data());
+            RETURN_ERROR_IF_EXC(_env);
+            _env->DeleteLocalRef(tmp);

Review Comment:
   fix



##########
be/src/util/jni-util.h:
##########
@@ -72,138 +59,1114 @@ class JniUtil {
                 return status;
             }
         }
-        if (*env == nullptr) {
+        if (*env == nullptr) [[unlikely]] {
             return Status::JniError("Failed to get JNIEnv: it is nullptr.");
         }
         return Status::OK();
     }
 
-    //jclass is generally a local reference.
-    //Method ID and field ID values are forever.
-    //If you want to use the jclass across multiple threads or multiple calls 
into the JNI code you need
-    // to create a global reference to it with GetGlobalClassRef().
-    static Status GetGlobalClassRef(JNIEnv* env, const char* class_str,
-                                    jclass* class_ref) WARN_UNUSED_RESULT;
-
-    static Status LocalToGlobalRef(JNIEnv* env, jobject local_ref,
-                                   jobject* global_ref) WARN_UNUSED_RESULT;
+    static Status Init() { return init_throw_exception(); }
 
     static Status GetJniExceptionMsg(JNIEnv* env, bool log_stack = true,
                                      const std::string& prefix = "") 
WARN_UNUSED_RESULT;
 
-    static jclass jni_util_class() { return jni_util_cl_; }
-    static jmethodID throwable_to_stack_trace_id() { return 
throwable_to_stack_trace_id_; }
+private:
+    static Status GetJNIEnvSlowPath(JNIEnv** env);
+    static Status init_throw_exception() {
+        JNIEnv* env = nullptr;
+        RETURN_IF_ERROR(Jni::Env::Get(&env));
+
+        if (env == nullptr) {
+            return Status::JniError("Failed to get/create JVM");
+        }
+        // Find JniUtil class and create a global ref.
+        jclass local_jni_util_cl = 
env->FindClass("org/apache/doris/common/jni/utils/JniUtil");
+        if (local_jni_util_cl == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find JniUtil class.");
+        }
+        jni_util_cl_ = 
reinterpret_cast<jclass>(env->NewGlobalRef(local_jni_util_cl));
+        if (jni_util_cl_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to create global reference to 
JniUtil class.");
+        }
+        env->DeleteLocalRef(local_jni_util_cl);
+        if (env->ExceptionOccurred()) {
+            return Status::JniError("Failed to delete local reference to 
JniUtil class.");
+        }
+
+        // Throwable toString()
+        throwable_to_string_id_ = env->GetStaticMethodID(
+                jni_util_cl_, "throwableToString", 
"(Ljava/lang/Throwable;)Ljava/lang/String;");
+        if (throwable_to_string_id_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find JniUtil.throwableToString 
method.");
+        }
 
-    static const int64_t INITIAL_RESERVED_BUFFER_SIZE = 1024;
-    // TODO: we need a heuristic strategy to increase buffer size for 
variable-size output.
-    static inline int64_t IncreaseReservedBufferSize(int n) {
-        return INITIAL_RESERVED_BUFFER_SIZE << n;
+        // throwableToStackTrace()
+        throwable_to_stack_trace_id_ = env->GetStaticMethodID(
+                jni_util_cl_, "throwableToStackTrace", 
"(Ljava/lang/Throwable;)Ljava/lang/String;");
+        if (throwable_to_stack_trace_id_ == nullptr) {
+            if (env->ExceptionOccurred()) {
+                env->ExceptionDescribe();
+            }
+            return Status::JniError("Failed to find 
JniUtil.throwableToFullStackTrace method.");
+        }
+        return Status::OK();
     }
-    static Status get_jni_scanner_class(JNIEnv* env, const char* classname, 
jclass* loaded_class);
-    static Status convert_to_java_map(JNIEnv* env, const std::map<std::string, 
std::string>& map,
-                                      jobject* hashmap_object);
-    static Status convert_to_cpp_map(JNIEnv* env, jobject map,
-                                     std::map<std::string, std::string>* 
resultMap);
-    static size_t get_max_jni_heap_memory_size();
-    static Status clean_udf_class_load_cache(const std::string& 
function_signature);
 
 private:
-    static void parse_max_heap_memory_size_from_jvm(JNIEnv* env);
-    static Status GetJNIEnvSlowPath(JNIEnv** env);
-    static Status init_jni_scanner_loader(JNIEnv* env);
+    // Thread-local cache of the JNIEnv for this thread.
+    static __thread JNIEnv* tls_env_;
 
-    static bool jvm_inited_;
-    static jclass internal_exc_cl_;
-    static jclass jni_native_method_exc_cl_;
+    //for exception
     static jclass jni_util_cl_;
     static jmethodID throwable_to_string_id_;
     static jmethodID throwable_to_stack_trace_id_;
-    static jmethodID get_jvm_metrics_id_;
-    static jmethodID get_jvm_threads_id_;
-    static jmethodID get_jmx_json_;
-    // JNI scanner loader
-    static jobject jni_scanner_loader_obj_;
-    static jmethodID jni_scanner_loader_method_;
-    // Thread-local cache of the JNIEnv for this thread.
-    static __thread JNIEnv* tls_env_;
-    static jlong max_jvm_heap_memory_size_;
-    static jmethodID _clean_udf_cache_method_id;
 };
 
-/// Helper class for lifetime management of chars from JNI, releasing JNI 
chars when
-/// destructed
-class JniUtfCharGuard {
+enum RefType { Local, Global };
+
+enum BufferType { Chars, ByteArray };
+
+template <RefType Ref>
+struct RefHelper {};
+
+template <>
+struct RefHelper<Local> {
+    static jobject create(JNIEnv* env, jobject obj) { return 
env->NewLocalRef(obj); }
+
+    static void destroy(JNIEnv* env, jobject obj) { env->DeleteLocalRef(obj); }
+
+    static Status get_env(JNIEnv** env) {
+        // Get the JNIEnv* corresponding to current thread.
+        return Jni::Env::Get(env);
+    }
+};
+
+template <>
+struct RefHelper<Global> {
+    static jobject create(JNIEnv* env, jobject obj) { return 
env->NewGlobalRef(obj); }
+
+    static void destroy(JNIEnv* env, jobject obj) { env->DeleteGlobalRef(obj); 
}
+
+    static Status get_env(JNIEnv** env) { return Jni::Env::Get(env); }
+};
+
+template <RefType Ref>
+class Object;
+
+template <RefType Ref>
+class Class;
+
+class MethodId {
+public:
+    MethodId() = default;
+    bool uninitialized() const { return _id == nullptr; }
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+private:
+    jmethodID _id = nullptr;
+};
+
+class FieldId {
+public:
+    FieldId() = default;
+    bool uninitialized() const { return _id == nullptr; }
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+private:
+    jfieldID _id = nullptr;
+};
+
+enum CallTag {
+    ObjectMethod,
+    IntMethod,
+    LongMethod,
+    VoidMethod,
+    BooleanMethod,
+
+    StaticObjectMethod,
+    StaticIntMethod,
+    StaticLongMethod,
+    StaticVoidMethod,
+
+    NewObject,
+
+    NonvirtualVoidMethod,
+    NonvirtualObjectMethod,
+    NonvirtualIntMethod,
+    NonvirtualBooleanMethod,
+};
+
+template <CallTag Tag>
+struct CallHelper {};
+
+template <>
+struct CallHelper<ObjectMethod> {
+    static jobject call_impl(JNIEnv* env, jobject obj, jmethodID methodID, 
const jvalue* args) {
+        return env->CallObjectMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<IntMethod> {
+    static jint call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const 
jvalue* args) {
+        return env->CallIntMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jint;
+};
+
+template <>
+struct CallHelper<LongMethod> {
+    static jlong call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const 
jvalue* args) {
+        return env->CallLongMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jlong;
+};
+
+template <>
+struct CallHelper<VoidMethod> {
+    static void call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const 
jvalue* args) {
+        env->CallVoidMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = void;
+};
+
+template <>
+struct CallHelper<BooleanMethod> {
+    static jboolean call_impl(JNIEnv* env, jobject obj, jmethodID methodID, 
const jvalue* args) {
+        return env->CallBooleanMethodA(obj, methodID, args);
+    }
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jboolean;
+};
+
+template <>
+struct CallHelper<StaticObjectMethod> {
+    static jobject call_impl(JNIEnv* env, jclass cls, jmethodID methodID, 
const jvalue* args) {
+        return env->CallStaticObjectMethodA(cls, methodID, args);
+    }
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<StaticIntMethod> {
+    static jint call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const 
jvalue* args) {
+        return env->CallStaticIntMethodA(cls, methodID, args);
+    }
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jint;
+};
+
+template <>
+struct CallHelper<StaticLongMethod> {
+    static jlong call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const 
jvalue* args) {
+        return env->CallStaticLongMethodA(cls, methodID, args);
+    }
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jlong;
+};
+
+template <>
+struct CallHelper<StaticVoidMethod> {
+    static void call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const 
jvalue* args) {
+        return env->CallStaticVoidMethodA(cls, methodID, args);
+    }
+
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = void;
+};
+
+template <>
+struct CallHelper<NewObject> {
+    static jobject call_impl(JNIEnv* env, jclass cls, jmethodID methodID, 
const jvalue* args) {
+        return env->NewObjectA(cls, methodID, args);
+    }
+
+    using BASE_TYPE = jclass;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<NonvirtualVoidMethod> {
+    static void call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID 
methodID,
+                          const jvalue* args) {
+        return env->CallNonvirtualVoidMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = void;
+};
+
+template <>
+struct CallHelper<NonvirtualObjectMethod> {
+    static jobject call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID 
methodID,
+                             const jvalue* args) {
+        return env->CallNonvirtualObjectMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jobject;
+};
+
+template <>
+struct CallHelper<NonvirtualIntMethod> {
+    static jint call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID 
methodID,
+                          const jvalue* args) {
+        return env->CallNonvirtualIntMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jint;
+};
+
+template <>
+struct CallHelper<NonvirtualBooleanMethod> {
+    static jboolean call_impl(JNIEnv* env, jobject obj, jclass clazz, 
jmethodID methodID,
+                              const jvalue* args) {
+        return env->CallNonvirtualBooleanMethodA(obj, clazz, methodID, args);
+    }
+
+    using BASE_TYPE = jobject;
+    using RETURN_TYPE = jboolean;
+};
+
+template <CallTag tag>
+class FunctionCall {
+public:
+    FunctionCall(FunctionCall&& other) noexcept = default;
+
+    static FunctionCall instance(JNIEnv* env, typename 
CallHelper<tag>::BASE_TYPE base,
+                                 jmethodID method_id) {
+        return FunctionCall(env, base, method_id);
+    }
+
+    /// Pass a primitive arg (eg an integer).
+    /// Multiple arguments may be passed by repeated calls.
+    template <class T>
+        requires std::disjunction_v<std::is_same<T, jboolean>, std::is_same<T, 
jbyte>,
+                                    std::is_same<T, jchar>, std::is_same<T, 
jshort>,
+                                    std::is_same<T, jint>, std::is_same<T, 
jlong>,
+                                    std::is_same<T, jfloat>, std::is_same<T, 
jdouble>>
+    FunctionCall& with_arg(T arg) {
+        jvalue v;
+        std::memset(&v, 0, sizeof(v));
+        if constexpr (std::is_same_v<T, jboolean>) {
+            v.z = arg;
+        } else if constexpr (std::is_same_v<T, jbyte>) {
+            v.b = arg;
+        } else if constexpr (std::is_same_v<T, jchar>) {
+            v.c = arg;
+        } else if constexpr (std::is_same_v<T, jshort>) {
+            v.s = arg;
+        } else if constexpr (std::is_same_v<T, jint>) {
+            v.i = arg;
+        } else if constexpr (std::is_same_v<T, jlong>) {
+            v.j = arg;
+        } else if constexpr (std::is_same_v<T, jfloat>) {
+            v.f = arg;
+        } else if constexpr (std::is_same_v<T, jdouble>) {
+            v.d = arg;
+        } else {
+            static_assert(false);
+        }
+        _args.push_back(v);
+        return *this;
+    }
+
+    template <RefType Ref>
+    FunctionCall& with_arg(const Object<Ref>& obj) WARN_UNUSED_RESULT;
+
+    template <typename ReturnType>
+        requires(std::is_same_v<typename CallHelper<tag>::RETURN_TYPE, 
ReturnType>)
+    Status call(ReturnType* result) {
+        *result = CallHelper<tag>::call_impl(_env, _base, _method, 
_args.data());
+        RETURN_ERROR_IF_EXC(_env);
+        return Status::OK();
+    }
+
+    Status call(Object<Local>* result);
+
+    Status call(Object<Global>* result);
+
+    Status call() {
+        using return_type = typename CallHelper<tag>::RETURN_TYPE;
+        if constexpr (std::disjunction_v<
+                              std::is_same<return_type, jboolean>, 
std::is_same<return_type, jbyte>,
+                              std::is_same<return_type, jchar>, 
std::is_same<return_type, jshort>,
+                              std::is_same<return_type, jint>, 
std::is_same<return_type, jlong>,
+                              std::is_same<return_type, jfloat>, 
std::is_same<return_type, jdouble>,
+                              std::is_same<return_type, void>>) {
+            CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
+            RETURN_ERROR_IF_EXC(_env);
+        } else if constexpr (std::is_same_v<return_type, jobject>) {
+            jobject tmp = CallHelper<tag>::call_impl(_env, _base, _method, 
_args.data());
+            RETURN_ERROR_IF_EXC(_env);
+            _env->DeleteLocalRef(tmp);
+        } else {
+            static_assert(false);
+        }
+        return Status::OK();
+    }
+
+protected:
+    explicit FunctionCall(JNIEnv* env, typename CallHelper<tag>::BASE_TYPE 
base,
+                          jmethodID method_id)
+            : _env(env), _base(base), _method(method_id) {}
+
+    JNIEnv* _env = nullptr;
+    CallHelper<tag>::BASE_TYPE _base; // is jobject/jclass  not need 
new/delete local/global ref.
+    const jmethodID _method = nullptr;
+    std::vector<jvalue> _args;
+    Status _st = Status::OK();
+    DISALLOW_COPY_AND_ASSIGN(FunctionCall);
+};
+
+template <CallTag tag>
+class NonvirtaulFunctionCall : public FunctionCall<tag> {
 public:
-    /// Construct a JniUtfCharGuards holding nothing
-    JniUtfCharGuard() : utf_chars(nullptr) {}
+    NonvirtaulFunctionCall(NonvirtaulFunctionCall&& other) noexcept = default;
+
+    static NonvirtaulFunctionCall instance(JNIEnv* env, typename 
CallHelper<tag>::BASE_TYPE base,
+                                           jclass cls, jmethodID method_id) {
+        return NonvirtaulFunctionCall(env, base, cls, method_id);
+    }
+
+    // no override
+    template <class T>
+        requires std::disjunction_v<std::is_same<T, jboolean>, std::is_same<T, 
jbyte>,
+                                    std::is_same<T, jchar>, std::is_same<T, 
jshort>,
+                                    std::is_same<T, jint>, std::is_same<T, 
jlong>,
+                                    std::is_same<T, jfloat>, std::is_same<T, 
jdouble>>
+    NonvirtaulFunctionCall& with_arg(T arg) {
+        jvalue v;
+        std::memset(&v, 0, sizeof(v));
+        if constexpr (std::is_same_v<T, jboolean>) {
+            v.z = arg;
+        } else if constexpr (std::is_same_v<T, jbyte>) {
+            v.b = arg;
+        } else if constexpr (std::is_same_v<T, jchar>) {
+            v.c = arg;
+        } else if constexpr (std::is_same_v<T, jshort>) {
+            v.s = arg;
+        } else if constexpr (std::is_same_v<T, jint>) {
+            v.i = arg;
+        } else if constexpr (std::is_same_v<T, jlong>) {
+            v.j = arg;
+        } else if constexpr (std::is_same_v<T, jfloat>) {
+            v.f = arg;
+        } else if constexpr (std::is_same_v<T, jdouble>) {
+            v.d = arg;
+        } else {
+            static_assert(false);
+        }
+        this->_args.push_back(v);
+        return *this;
+    }
 
-    /// Release the held char sequence if there is one.
-    ~JniUtfCharGuard() {
-        if (utf_chars != nullptr) env->ReleaseStringUTFChars(jstr, utf_chars);
+    template <RefType Ref>
+    NonvirtaulFunctionCall& with_arg(const Object<Ref>& obj) 
WARN_UNUSED_RESULT;
+
+    // no override
+    Status call() {
+        using return_type = typename CallHelper<tag>::RETURN_TYPE;
+        if constexpr (std::disjunction_v<
+                              std::is_same<return_type, jboolean>, 
std::is_same<return_type, jbyte>,
+                              std::is_same<return_type, jchar>, 
std::is_same<return_type, jshort>,
+                              std::is_same<return_type, jint>, 
std::is_same<return_type, jlong>,
+                              std::is_same<return_type, jfloat>, 
std::is_same<return_type, jdouble>,
+                              std::is_same<return_type, void>>) {
+            CallHelper<tag>::call_impl(this->_env, this->_base, _cls, 
this->_method,
+                                       this->_args.data());
+            RETURN_ERROR_IF_EXC(this->_env);
+        } else if constexpr (std::is_same_v<return_type, jobject>) {
+            jobject tmp = CallHelper<tag>::call_impl(this->_env, this->_base, 
_cls, this->_method,
+                                                     this->_args.data());
+            RETURN_ERROR_IF_EXC(this->_env);
+            this->_env->DeleteLocalRef(tmp);
+        } else {
+            static_assert(false);
+        }
+        return Status::OK();
     }
 
-    /// Try to get chars from jstr. If error is returned, utf_chars and get() 
remain
-    /// to be nullptr, otherwise they point to a valid char sequence. The char 
sequence
-    /// lives as long as this guard. jstr should not be null.
-    static Status create(JNIEnv* env, jstring jstr, JniUtfCharGuard* out);
+    Status call(Object<Local>* result);
 
-    /// Get the char sequence. Returns nullptr if the guard does hold a char 
sequence.
-    const char* get() { return utf_chars; }
+    template <typename ReturnType>
+        requires(std::is_same_v<typename CallHelper<tag>::RETURN_TYPE, 
ReturnType>)
+    Status call(ReturnType* result) {
+        *result = CallHelper<tag>::call_impl(this->_env, this->_base, _cls, 
this->_method,
+                                             this->_args.data());
+        RETURN_ERROR_IF_EXC(this->_env);
+        return Status::OK();
+    }
 
 private:
-    JNIEnv* env = nullptr;
-    jstring jstr;
-    const char* utf_chars = nullptr;
-    DISALLOW_COPY_AND_ASSIGN(JniUtfCharGuard);
+    explicit NonvirtaulFunctionCall(JNIEnv* env, typename 
CallHelper<tag>::BASE_TYPE base,
+                                    jclass cls, jmethodID method_id)
+            : FunctionCall<tag>(env, base, method_id), _cls(cls) {}
+    jclass _cls;
+    DISALLOW_COPY_AND_ASSIGN(NonvirtaulFunctionCall);
+};
+
+template <RefType Ref>
+class Object {
+    // env->GetObjectRefType
+public:
+    Object() = default;
+
+    template <RefType U>
+    friend class Object;
+
+    template <RefType U>
+    friend class Class;
+
+    template <RefType U>
+    friend class String;
+
+    template <RefType U>
+    friend class Array;
+
+    template <BufferType bufferfType, RefType U>
+    friend class BufferGuard;
+
+    template <CallTag tag>
+    friend class FunctionCall;
+
+    template <CallTag tag>
+    friend class NonvirtaulFunctionCall;
+
+    ~Object() {
+        if (_obj != nullptr) [[likely]] {
+            JNIEnv* env = nullptr;
+            if (Status st = RefHelper<Ref>::get_env(&env); !st.ok()) 
[[unlikely]] {
+                LOG(WARNING) << "Can't destroy Jni Ref : " << st.msg();
+                return;

Review Comment:
   Yes, because `destroy` requires an environment variable (env). However, this 
situation is unlikely to occur, as the environment variable is used when the 
object is created.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to