Coverage Report

Created: 2026-01-04 14:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/util/jni-util.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <butil/macros.h>
21
#include <jni.h>
22
#include <limits.h>
23
#include <stddef.h>
24
#include <stdint.h>
25
26
#include <string>
27
28
#include "common/status.h"
29
#include "jni_md.h"
30
#include "util/defer_op.h"
31
#include "util/thrift_util.h"
32
33
#ifdef USE_HADOOP_HDFS
34
// defined in hadoop/hadoop-hdfs-project/hadoop-hdfs/src/main/native/libhdfs/jni_helper.c
35
extern "C" JNIEnv* getJNIEnv(void);
36
#endif
37
38
namespace doris {
39
40
#define RETURN_ERROR_IF_EXC(env)                      \
41
    do {                                              \
42
4.88M
        if (env->ExceptionCheck()) [[unlikely]]       \
43
4.88M
            return Jni::Env::GetJniExceptionMsg(env); \
44
6
    } while (false)
45
4.88M
46
//In order to reduce the potential risks caused by not handling exceptions,
47
// you need to refer to  https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
48
2.72M
// to confirm whether the jni method will throw an exception.
49
2.72M
50
2.72M
namespace Jni {
51
class Env {
52
public:
53
48.8k
    static Status Get(JNIEnv** env) {
54
48.8k
        if (tls_env_) {
55
            *env = tls_env_;
56
        } else {
57
            Status status = GetJNIEnvSlowPath(env);
58
            if (!status.ok()) {
59
                return status;
60
            }
61
        }
62
        if (*env == nullptr) [[unlikely]] {
63
            return Status::JniError("Failed to get JNIEnv: it is nullptr.");
64
48
        }
65
        return Status::OK();
66
497k
    }
67
497k
68
491k
    static Status Init() { return init_throw_exception(); }
69
491k
70
6.12k
    static Status GetJniExceptionMsg(JNIEnv* env, bool log_stack = true,
71
6.12k
                                     const std::string& prefix = "") WARN_UNUSED_RESULT;
72
0
73
0
private:
74
6.12k
    static Status GetJNIEnvSlowPath(JNIEnv** env);
75
497k
    static Status init_throw_exception() {
76
0
        JNIEnv* env = nullptr;
77
0
        RETURN_IF_ERROR(Jni::Env::Get(&env));
78
497k
79
497k
        // Find JniUtil class and create a global ref.
80
        jclass local_jni_util_cl = env->FindClass("org/apache/doris/common/jni/utils/JniUtil");
81
        if (local_jni_util_cl == nullptr) {
82
            if (env->ExceptionOccurred()) {
83
                env->ExceptionDescribe();
84
            }
85
            return Status::JniError("Failed to find JniUtil class.");
86
        }
87
        jni_util_cl_ = reinterpret_cast<jclass>(env->NewGlobalRef(local_jni_util_cl));
88
        env->DeleteLocalRef(local_jni_util_cl);
89
        if (jni_util_cl_ == nullptr) {
90
            if (env->ExceptionOccurred()) {
91
                env->ExceptionDescribe();
92
            }
93
            return Status::JniError("Failed to create global reference to JniUtil class.");
94
48
        }
95
24
        if (env->ExceptionOccurred()) {
96
            return Status::JniError("Failed to delete local reference to JniUtil class.");
97
        }
98
99
0
        // Throwable toString()
100
0
        throwable_to_string_id_ = env->GetStaticMethodID(
101
0
                jni_util_cl_, "throwableToString", "(Ljava/lang/Throwable;)Ljava/lang/String;");
102
        if (throwable_to_string_id_ == nullptr) {
103
            if (env->ExceptionOccurred()) {
104
                env->ExceptionDescribe();
105
            }
106
            return Status::JniError("Failed to find JniUtil.throwableToString method.");
107
        }
108
109
        // throwableToStackTrace()
110
        throwable_to_stack_trace_id_ = env->GetStaticMethodID(
111
                jni_util_cl_, "throwableToStackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;");
112
        if (throwable_to_stack_trace_id_ == nullptr) {
113
            if (env->ExceptionOccurred()) {
114
                env->ExceptionDescribe();
115
            }
116
            return Status::JniError("Failed to find JniUtil.throwableToFullStackTrace method.");
117
        }
118
        return Status::OK();
119
    }
120
121
private:
122
    // Thread-local cache of the JNIEnv for this thread.
123
    static __thread JNIEnv* tls_env_;
124
125
    //for exception
126
    static jclass jni_util_cl_;
127
    static jmethodID throwable_to_string_id_;
128
    static jmethodID throwable_to_stack_trace_id_;
129
};
130
131
enum RefType { Local, Global };
132
133
enum BufferType { Chars, ByteArray };
134
135
template <RefType Ref>
136
struct RefHelper {};
137
138
48
template <>
139
struct RefHelper<Local> {
140
    static jobject create(JNIEnv* env, jobject obj) { return env->NewLocalRef(obj); }
141
48
142
48
    static void destroy(JNIEnv* env, jobject obj) { env->DeleteLocalRef(obj); }
143
48
144
    static Status get_env(JNIEnv** env) {
145
        // Get the JNIEnv* corresponding to current thread.
146
        return Jni::Env::Get(env);
147
    }
148
};
149
150
template <>
151
48
struct RefHelper<Global> {
152
    static jobject create(JNIEnv* env, jobject obj) { return env->NewGlobalRef(obj); }
153
154
    static void destroy(JNIEnv* env, jobject obj) { env->DeleteGlobalRef(obj); }
155
156
    static Status get_env(JNIEnv** env) { return Jni::Env::Get(env); }
157
};
158
159
template <RefType Ref>
160
class Object;
161
162
4.70k
template <RefType Ref>
163
4.71k
class Class;
164
4.71k
165
4.71k
class MethodId {
166
public:
167
0
    MethodId() = default;
168
    bool uninitialized() const { return _id == nullptr; }
169
170
    template <RefType U>
171
    friend class Object;
172
173
    template <RefType U>
174
    friend class Class;
175
176
private:
177
    jmethodID _id = nullptr;
178
};
179
180
class FieldId {
181
public:
182
4.70k
    FieldId() = default;
183
4.70k
    bool uninitialized() const { return _id == nullptr; }
184
4.70k
185
    template <RefType U>
186
4.70k
    friend class Object;
187
4.70k
188
4.70k
    template <RefType U>
189
    friend class Class;
190
191
private:
192
4.70k
    jfieldID _id = nullptr;
193
0
};
194
0
195
0
enum CallTag {
196
0
    ObjectMethod,
197
    IntMethod,
198
    LongMethod,
199
4.70k
    VoidMethod,
200
4.70k
    BooleanMethod,
201
4.70k
202
0
    StaticObjectMethod,
203
0
    StaticIntMethod,
204
4.70k
    StaticLongMethod,
205
4.70k
    StaticVoidMethod,
206
4.70k
207
4.70k
    NewObject,
_ZN5doris18SerializeThriftMsgINS_23TJdbcExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray
Line
Count
Source
182
2.63k
    FieldId() = default;
183
2.63k
    bool uninitialized() const { return _id == nullptr; }
184
2.63k
185
    template <RefType U>
186
2.63k
    friend class Object;
187
2.63k
188
2.63k
    template <RefType U>
189
    friend class Class;
190
191
private:
192
2.63k
    jfieldID _id = nullptr;
193
0
};
194
0
195
0
enum CallTag {
196
0
    ObjectMethod,
197
    IntMethod,
198
    LongMethod,
199
2.63k
    VoidMethod,
200
2.63k
    BooleanMethod,
201
2.63k
202
0
    StaticObjectMethod,
203
0
    StaticIntMethod,
204
2.63k
    StaticLongMethod,
205
2.63k
    StaticVoidMethod,
206
2.63k
207
2.63k
    NewObject,
_ZN5doris18SerializeThriftMsgINS_26TJavaUdfExecutorCtorParamsEEENS_6StatusEP7JNIEnv_PT_PP11_jbyteArray
Line
Count
Source
182
2.06k
    FieldId() = default;
183
2.06k
    bool uninitialized() const { return _id == nullptr; }
184
2.06k
185
    template <RefType U>
186
2.06k
    friend class Object;
187
2.06k
188
2.06k
    template <RefType U>
189
    friend class Class;
190
191
private:
192
2.06k
    jfieldID _id = nullptr;
193
0
};
194
0
195
0
enum CallTag {
196
0
    ObjectMethod,
197
    IntMethod,
198
    LongMethod,
199
2.06k
    VoidMethod,
200
2.06k
    BooleanMethod,
201
2.06k
202
0
    StaticObjectMethod,
203
0
    StaticIntMethod,
204
2.06k
    StaticLongMethod,
205
2.06k
    StaticVoidMethod,
206
2.06k
207
2.06k
    NewObject,
208
209
    NonvirtualVoidMethod,
210
    NonvirtualObjectMethod,
211
    NonvirtualIntMethod,
212
    NonvirtualBooleanMethod,
213
};
214
215
template <CallTag Tag>
216
struct CallHelper {};
217
218
template <>
219
struct CallHelper<ObjectMethod> {
220
    static jobject call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) {
221
        return env->CallObjectMethodA(obj, methodID, args);
222
    }
223
    using BASE_TYPE = jobject;
224
    using RETURN_TYPE = jobject;
225
};
226
227
template <>
228
struct CallHelper<IntMethod> {
229
    static jint call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) {
230
        return env->CallIntMethodA(obj, methodID, args);
231
    }
232
    using BASE_TYPE = jobject;
233
    using RETURN_TYPE = jint;
234
};
235
236
template <>
237
struct CallHelper<LongMethod> {
238
    static jlong call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) {
239
        return env->CallLongMethodA(obj, methodID, args);
240
    }
241
    using BASE_TYPE = jobject;
242
    using RETURN_TYPE = jlong;
243
};
244
245
template <>
246
struct CallHelper<VoidMethod> {
247
    static void call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) {
248
        env->CallVoidMethodA(obj, methodID, args);
249
    }
250
    using BASE_TYPE = jobject;
251
    using RETURN_TYPE = void;
252
};
253
254
template <>
255
struct CallHelper<BooleanMethod> {
256
    static jboolean call_impl(JNIEnv* env, jobject obj, jmethodID methodID, const jvalue* args) {
257
        return env->CallBooleanMethodA(obj, methodID, args);
258
    }
259
    using BASE_TYPE = jobject;
260
    using RETURN_TYPE = jboolean;
261
};
262
263
template <>
264
struct CallHelper<StaticObjectMethod> {
265
    static jobject call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const jvalue* args) {
266
        return env->CallStaticObjectMethodA(cls, methodID, args);
267
    }
268
    using BASE_TYPE = jclass;
269
    using RETURN_TYPE = jobject;
270
};
271
272
template <>
273
struct CallHelper<StaticIntMethod> {
274
    static jint call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const jvalue* args) {
275
        return env->CallStaticIntMethodA(cls, methodID, args);
276
    }
277
    using BASE_TYPE = jclass;
278
    using RETURN_TYPE = jint;
279
};
280
281
template <>
282
struct CallHelper<StaticLongMethod> {
283
    static jlong call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const jvalue* args) {
284
        return env->CallStaticLongMethodA(cls, methodID, args);
285
    }
286
    using BASE_TYPE = jclass;
287
    using RETURN_TYPE = jlong;
288
};
289
290
template <>
291
struct CallHelper<StaticVoidMethod> {
292
    static void call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const jvalue* args) {
293
        return env->CallStaticVoidMethodA(cls, methodID, args);
294
    }
295
296
    using BASE_TYPE = jclass;
297
    using RETURN_TYPE = void;
298
};
299
300
template <>
301
struct CallHelper<NewObject> {
302
    static jobject call_impl(JNIEnv* env, jclass cls, jmethodID methodID, const jvalue* args) {
303
        return env->NewObjectA(cls, methodID, args);
304
    }
305
306
    using BASE_TYPE = jclass;
307
    using RETURN_TYPE = jobject;
308
};
309
310
template <>
311
struct CallHelper<NonvirtualVoidMethod> {
312
    static void call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID,
313
                          const jvalue* args) {
314
        return env->CallNonvirtualVoidMethodA(obj, clazz, methodID, args);
315
    }
316
317
    using BASE_TYPE = jobject;
318
    using RETURN_TYPE = void;
319
};
320
321
template <>
322
struct CallHelper<NonvirtualObjectMethod> {
323
    static jobject call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID,
324
                             const jvalue* args) {
325
        return env->CallNonvirtualObjectMethodA(obj, clazz, methodID, args);
326
    }
327
328
    using BASE_TYPE = jobject;
329
    using RETURN_TYPE = jobject;
330
};
331
332
template <>
333
struct CallHelper<NonvirtualIntMethod> {
334
    static jint call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID,
335
                          const jvalue* args) {
336
        return env->CallNonvirtualIntMethodA(obj, clazz, methodID, args);
337
    }
338
339
    using BASE_TYPE = jobject;
340
    using RETURN_TYPE = jint;
341
};
342
343
template <>
344
struct CallHelper<NonvirtualBooleanMethod> {
345
    static jboolean call_impl(JNIEnv* env, jobject obj, jclass clazz, jmethodID methodID,
346
                              const jvalue* args) {
347
        return env->CallNonvirtualBooleanMethodA(obj, clazz, methodID, args);
348
    }
349
350
    using BASE_TYPE = jobject;
351
    using RETURN_TYPE = jboolean;
352
};
353
354
template <CallTag tag>
355
class FunctionCall {
356
public:
357
    FunctionCall(FunctionCall&& other) noexcept = default;
358
359
    static FunctionCall instance(JNIEnv* env, typename CallHelper<tag>::BASE_TYPE base,
360
                                 jmethodID method_id) {
361
        return FunctionCall(env, base, method_id);
362
    }
363
364
    /// Pass a primitive arg (eg an integer).
365
    /// Multiple arguments may be passed by repeated calls.
366
    template <class T>
367
        requires std::disjunction_v<std::is_same<T, jboolean>, std::is_same<T, jbyte>,
368
                                    std::is_same<T, jchar>, std::is_same<T, jshort>,
369
                                    std::is_same<T, jint>, std::is_same<T, jlong>,
370
                                    std::is_same<T, jfloat>, std::is_same<T, jdouble>>
371
    FunctionCall& with_arg(T arg) {
372
        jvalue v;
373
        std::memset(&v, 0, sizeof(v));
374
        if constexpr (std::is_same_v<T, jboolean>) {
375
            v.z = arg;
376
        } else if constexpr (std::is_same_v<T, jbyte>) {
377
            v.b = arg;
378
        } else if constexpr (std::is_same_v<T, jchar>) {
379
            v.c = arg;
380
        } else if constexpr (std::is_same_v<T, jshort>) {
381
            v.s = arg;
382
        } else if constexpr (std::is_same_v<T, jint>) {
383
            v.i = arg;
384
        } else if constexpr (std::is_same_v<T, jlong>) {
385
            v.j = arg;
386
        } else if constexpr (std::is_same_v<T, jfloat>) {
387
            v.f = arg;
388
        } else if constexpr (std::is_same_v<T, jdouble>) {
389
            v.d = arg;
390
        } else {
391
            static_assert(false);
392
        }
393
        _args.push_back(v);
394
        return *this;
395
    }
396
397
    template <RefType Ref>
398
    FunctionCall& with_arg(const Object<Ref>& obj) WARN_UNUSED_RESULT;
399
400
    template <typename ReturnType>
401
        requires(std::is_same_v<typename CallHelper<tag>::RETURN_TYPE, ReturnType>)
402
    Status call(ReturnType* result) {
403
        *result = CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
404
        RETURN_ERROR_IF_EXC(_env);
405
        return Status::OK();
406
    }
407
408
    Status call(Object<Local>* result);
409
410
    Status call(Object<Global>* result);
411
412
    Status call() {
413
        using return_type = typename CallHelper<tag>::RETURN_TYPE;
414
        if constexpr (std::disjunction_v<
415
                              std::is_same<return_type, jboolean>, std::is_same<return_type, jbyte>,
416
                              std::is_same<return_type, jchar>, std::is_same<return_type, jshort>,
417
                              std::is_same<return_type, jint>, std::is_same<return_type, jlong>,
418
                              std::is_same<return_type, jfloat>, std::is_same<return_type, jdouble>,
419
                              std::is_same<return_type, void>>) {
420
            CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
421
            RETURN_ERROR_IF_EXC(_env);
422
        } else if constexpr (std::is_same_v<return_type, jobject>) {
423
            jobject tmp = CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
424
            _env->DeleteLocalRef(tmp);
425
            RETURN_ERROR_IF_EXC(_env);
426
        } else {
427
            static_assert(false);
428
        }
429
        return Status::OK();
430
    }
431
432
protected:
433
    explicit FunctionCall(JNIEnv* env, typename CallHelper<tag>::BASE_TYPE base,
434
                          jmethodID method_id)
435
            : _env(env), _base(base), _method(method_id) {}
436
437
    JNIEnv* _env = nullptr;
438
    CallHelper<tag>::BASE_TYPE _base; // is jobject/jclass  not need new/delete local/global ref.
439
    const jmethodID _method = nullptr;
440
    std::vector<jvalue> _args;
441
    Status _st = Status::OK();
442
    DISALLOW_COPY_AND_ASSIGN(FunctionCall);
443
};
444
445
template <CallTag tag>
446
class NonvirtualFunctionCall : public FunctionCall<tag> {
447
public:
448
    NonvirtualFunctionCall(NonvirtualFunctionCall&& other) noexcept = default;
449
450
    static NonvirtualFunctionCall instance(JNIEnv* env, typename CallHelper<tag>::BASE_TYPE base,
451
                                           jclass cls, jmethodID method_id) {
452
        return NonvirtualFunctionCall(env, base, cls, method_id);
453
    }
454
455
    // no override
456
    template <class T>
457
        requires std::disjunction_v<std::is_same<T, jboolean>, std::is_same<T, jbyte>,
458
                                    std::is_same<T, jchar>, std::is_same<T, jshort>,
459
                                    std::is_same<T, jint>, std::is_same<T, jlong>,
460
                                    std::is_same<T, jfloat>, std::is_same<T, jdouble>>
461
    NonvirtualFunctionCall& with_arg(T arg) {
462
        jvalue v;
463
        std::memset(&v, 0, sizeof(v));
464
        if constexpr (std::is_same_v<T, jboolean>) {
465
            v.z = arg;
466
        } else if constexpr (std::is_same_v<T, jbyte>) {
467
            v.b = arg;
468
        } else if constexpr (std::is_same_v<T, jchar>) {
469
            v.c = arg;
470
        } else if constexpr (std::is_same_v<T, jshort>) {
471
            v.s = arg;
472
        } else if constexpr (std::is_same_v<T, jint>) {
473
            v.i = arg;
474
        } else if constexpr (std::is_same_v<T, jlong>) {
475
            v.j = arg;
476
        } else if constexpr (std::is_same_v<T, jfloat>) {
477
            v.f = arg;
478
        } else if constexpr (std::is_same_v<T, jdouble>) {
479
            v.d = arg;
480
        } else {
481
            static_assert(false);
482
        }
483
        this->_args.push_back(v);
484
        return *this;
485
    }
486
487
    template <RefType Ref>
488
    NonvirtualFunctionCall& with_arg(const Object<Ref>& obj) WARN_UNUSED_RESULT;
489
490
    // no override
491
    Status call() {
492
        using return_type = typename CallHelper<tag>::RETURN_TYPE;
493
        if constexpr (std::disjunction_v<
494
                              std::is_same<return_type, jboolean>, std::is_same<return_type, jbyte>,
495
                              std::is_same<return_type, jchar>, std::is_same<return_type, jshort>,
496
                              std::is_same<return_type, jint>, std::is_same<return_type, jlong>,
497
                              std::is_same<return_type, jfloat>, std::is_same<return_type, jdouble>,
498
                              std::is_same<return_type, void>>) {
499
            CallHelper<tag>::call_impl(this->_env, this->_base, _cls, this->_method,
500
                                       this->_args.data());
501
            RETURN_ERROR_IF_EXC(this->_env);
502
        } else if constexpr (std::is_same_v<return_type, jobject>) {
503
            jobject tmp = CallHelper<tag>::call_impl(this->_env, this->_base, _cls, this->_method,
504
                                                     this->_args.data());
505
            RETURN_ERROR_IF_EXC(this->_env);
506
            this->_env->DeleteLocalRef(tmp);
507
        } else {
508
            static_assert(false);
509
        }
510
        return Status::OK();
511
    }
512
513
    Status call(Object<Local>* result);
514
515
    template <typename ReturnType>
516
        requires(std::is_same_v<typename CallHelper<tag>::RETURN_TYPE, ReturnType>)
517
    Status call(ReturnType* result) {
518
        *result = CallHelper<tag>::call_impl(this->_env, this->_base, _cls, this->_method,
519
                                             this->_args.data());
520
        RETURN_ERROR_IF_EXC(this->_env);
521
        return Status::OK();
522
    }
523
524
private:
525
    explicit NonvirtualFunctionCall(JNIEnv* env, typename CallHelper<tag>::BASE_TYPE base,
526
                                    jclass cls, jmethodID method_id)
527
            : FunctionCall<tag>(env, base, method_id), _cls(cls) {}
528
    jclass _cls;
529
    DISALLOW_COPY_AND_ASSIGN(NonvirtualFunctionCall);
530
};
531
532
/**
533
 * When writing JNI code, developers usually need to pay extra attention to several error-prone aspects, including:
534
 * 1. The reference type of jobject (local vs. global)
535
 * 2. The lifetime and scope of JNI references
536
 * 3. Proper release of references after use
537
 * 4. Explicit exception checking after JNI calls
538
 * Because these concerns are verbose and easy to overlook, they often lead to bugs or inconsistent code. To simplify this, 
539
 * we provide a wrapper framework around raw JNI APIs. The following describes how to use it (assuming the user already 
540
 * understands the basic JNI programming model).
541
 *
542
 * 0. Get JNIEnv* env: `Status st = Jni::Env::Get(&env)`
543
 * 1. Choose the reference type
544
 *   First, determine whether the JNI object should be a local or global reference. Based on this, create the corresponding C++ wrapper object:
545
 *   LocalObject / GlobalObject. If the exact JNI type is known, use specialized wrappers such as <Local/Global><Array/String/Class>. 
546
 * 2. Initialize the object
547
 *   For `jclass`, typically use: `Status st = Jni::Util::find_class(xxx);`
548
 *   For other object types, they are usually initialized via: `Status st = clazz.new_object(xxx).with_arg(xxx).call(&object) or by calling methods on existing objects.
549
 * 3. Call methods and retrieve results
550
 *   To invoke a method and obtain a return value, use: `Status st = object.call_<return_type>_method(xxx).call(&result);` 
551
 * 
552
 * Notes
553
 * 1. All JNI references are automatically released in the wrapper’s destructor, ensuring safe and deterministic cleanup.
554
 * 2. All framework method invocations return a Status.
555
 * The actual JNI return value is written to the address passed to call().
556
 * 
557
 * Example: be/test/util/jni_util_test.cpp
558
*/
559
template <RefType Ref>
560
class Object {
561
    // env->GetObjectRefType
562
public:
563
    Object() = default;
564
565
    template <RefType U>
566
    friend class Object;
567
568
    template <RefType U>
569
    friend class Class;
570
571
    template <RefType U>
572
    friend class String;
573
574
    template <RefType U>
575
    friend class Array;
576
577
    template <BufferType bufferfType, RefType U>
578
    friend class BufferGuard;
579
580
    template <CallTag tag>
581
    friend class FunctionCall;
582
583
    template <CallTag tag>
584
    friend class NonvirtualFunctionCall;
585
586
    virtual ~Object() {
587
        if (_obj != nullptr) [[likely]] {
588
            JNIEnv* env = nullptr;
589
            if (Status st = RefHelper<Ref>::get_env(&env); !st.ok()) [[unlikely]] {
590
                LOG(WARNING) << "Can't destroy Jni Ref : " << st.msg();
591
                return;
592
            }
593
            RefHelper<Ref>::destroy(env, _obj);
594
        }
595
    }
596
597
    template <RefType R>
598
    static Status create(JNIEnv* env, const Object<R>& other, Object<Ref>* result) {
599
        DCHECK(!other.uninitialized());
600
        DCHECK(result->uninitialized());
601
602
        result->_obj = RefHelper<Ref>::create(env, other._obj);
603
        RETURN_ERROR_IF_EXC(env);
604
        return Status::OK();
605
    }
606
607
    bool uninitialized() const { return _obj == nullptr; }
608
609
    template <RefType T>
610
    bool equal(JNIEnv* env, const Object<T>& other) {
611
        DCHECK(!uninitialized());
612
        DCHECK(!other.uninitialized());
613
        return env->IsSameObject(this->_obj, other._obj); //assume not throw exception.
614
    }
615
616
    FunctionCall<ObjectMethod> call_object_method(JNIEnv* env, MethodId method_id) const {
617
        DCHECK(!this->uninitialized());
618
        DCHECK(!method_id.uninitialized());
619
        return FunctionCall<ObjectMethod>::instance(env, _obj, method_id._id);
620
    }
621
622
    FunctionCall<IntMethod> call_int_method(JNIEnv* env, MethodId method_id) const {
623
        DCHECK(!this->uninitialized());
624
        DCHECK(!method_id.uninitialized());
625
        return FunctionCall<IntMethod>::instance(env, _obj, method_id._id);
626
    }
627
628
    FunctionCall<LongMethod> call_long_method(JNIEnv* env, MethodId method_id) const {
629
        DCHECK(!this->uninitialized());
630
        DCHECK(!method_id.uninitialized());
631
        return FunctionCall<LongMethod>::instance(env, _obj, method_id._id);
632
    }
633
634
    FunctionCall<VoidMethod> call_void_method(JNIEnv* env, MethodId method_id) const {
635
        DCHECK(!this->uninitialized());
636
        DCHECK(!method_id.uninitialized());
637
        return FunctionCall<VoidMethod>::instance(env, _obj, method_id._id);
638
    }
639
640
    FunctionCall<BooleanMethod> call_boolean_method(JNIEnv* env, MethodId method_id) const {
641
        DCHECK(!this->uninitialized());
642
        DCHECK(!method_id.uninitialized());
643
        return FunctionCall<BooleanMethod>::instance(env, _obj, method_id._id);
644
    }
645
646
    template <RefType R>
647
    NonvirtualFunctionCall<NonvirtualVoidMethod> call_nonvirtual_void_method(
648
            JNIEnv* env, const Class<R>& clazz, MethodId method_id) const;
649
650
    template <RefType R>
651
    NonvirtualFunctionCall<NonvirtualObjectMethod> call_nonvirtual_object_method(
652
            JNIEnv* env, const Class<R>& clazz, MethodId method_id) const;
653
654
    template <RefType R>
655
    NonvirtualFunctionCall<NonvirtualIntMethod> call_nonvirtual_int_method(
656
            JNIEnv* env, const Class<R>& clazz, MethodId method_id) const;
657
658
    template <RefType R>
659
    NonvirtualFunctionCall<NonvirtualBooleanMethod> call_nonvirtual_boolean_method(
660
            JNIEnv* env, const Class<R>& clazz, MethodId method_id) const;
661
662
protected:
663
    jobject _obj = nullptr;
664
    DISALLOW_COPY_AND_ASSIGN(Object);
665
};
666
667
using LocalObject = Object<Local>;
668
using GlobalObject = Object<Global>;
669
670
static inline Status local_to_global_ref(JNIEnv* env, const LocalObject& local_ref,
671
                                         GlobalObject* global_ref) {
672
    return Object<Global>::create(env, local_ref, global_ref);
673
}
674
675
// auto ReleaseStringUTFChars ReleaseByteArrayElements ...
676
template <BufferType bufferfType, RefType Ref>
677
class BufferGuard {
678
public:
679
    BufferGuard() = default;
680
681
    template <RefType R>
682
    static Status create(JNIEnv* env, const Object<R>& object,
683
                         BufferGuard<bufferfType, Ref>* result, jboolean* isCopy) {
684
        DCHECK(result->_buffer == nullptr && result->_object.uninitialized());
685
686
        RETURN_IF_ERROR(Object<Ref>::create(env, object, &result->_object));
687
688
        if constexpr (bufferfType == BufferType::Chars) {
689
            result->_buffer = env->GetStringUTFChars((jstring)result->_object._obj, isCopy);
690
        } else if constexpr (bufferfType == BufferType::ByteArray) {
691
            result->_buffer =
692
                    (char*)env->GetByteArrayElements((jbyteArray)result->_object._obj, isCopy);
693
        } else {
694
            static_assert(false);
695
        }
696
697
        RETURN_ERROR_IF_EXC(env);
698
        if (result->_buffer == nullptr) [[unlikely]] {
699
            return Status::JniError("GetStringUTFChars/GetByteArrayElements fail.");
700
        }
701
702
        return Status::OK();
703
    }
704
705
    ~BufferGuard() {
706
        if (_object.uninitialized() || _buffer == nullptr) [[unlikely]] {
707
            return;
708
        }
709
        JNIEnv* env = nullptr;
710
711
        if (auto st = RefHelper<Ref>::get_env(&env); !st.ok()) [[unlikely]] {
712
            LOG(WARNING) << "BufferGuard release fail: " << st;
713
            return;
714
        }
715
716
        if constexpr (bufferfType == BufferType::Chars) {
717
            env->ReleaseStringUTFChars((jstring)_object._obj, _buffer);
718
        } else if constexpr (bufferfType == BufferType::ByteArray) {
719
            env->ReleaseByteArrayElements((jbyteArray)_object._obj, (jbyte*)_buffer, JNI_ABORT);
720
        }
721
    }
722
723
    const char* get() const { return _buffer; }
724
725
private:
726
    Object<Ref> _object;
727
    const char* _buffer = nullptr;
728
729
    DISALLOW_COPY_AND_ASSIGN(BufferGuard);
730
};
731
732
template <RefType Ref>
733
using StringBufferGuard = BufferGuard<Chars, Ref>;
734
using LocalStringBufferGuard = BufferGuard<Chars, Local>;
735
using GlobalStringBufferGuard = BufferGuard<Chars, Global>;
736
737
template <RefType Ref>
738
using ByteArrayBufferGuard = BufferGuard<ByteArray, Ref>;
739
using LocalByteArrayBufferGuard = BufferGuard<ByteArray, Local>;
740
using GlobalByteArrayBufferGuard = BufferGuard<ByteArray, Global>;
741
742
template <RefType Ref>
743
class String : public Object<Ref> {
744
public:
745
    String() = default;
746
747
    static Status new_string(JNIEnv* env, const char* utf_chars, String<Ref>* result) {
748
        DCHECK(result->uninitialized());
749
750
        if constexpr (Ref == Local) {
751
            result->_obj = env->NewStringUTF(utf_chars);
752
            RETURN_ERROR_IF_EXC(env);
753
        } else if constexpr (Ref == Global) {
754
            String local_result;
755
            local_result->_obj = env->NewStringUTF(utf_chars);
756
            RETURN_ERROR_IF_EXC(env);
757
            RETURN_IF_ERROR(local_to_global_ref(env, local_result, result));
758
        } else {
759
            static_assert(false);
760
        }
761
        return Status::OK();
762
    }
763
764
    Status get_string_chars(JNIEnv* env, StringBufferGuard<Ref>* jni_chars) const {
765
        return StringBufferGuard<Ref>::create(env, *this, jni_chars, nullptr);
766
    }
767
768
private:
769
    DISALLOW_COPY_AND_ASSIGN(String);
770
};
771
772
template <RefType Ref>
773
class Array : public Object<Ref> {
774
public:
775
    Array() = default;
776
777
    Status get_length(JNIEnv* env, jsize* result) const {
778
        DCHECK(!this->uninitialized());
779
780
        *result = env->GetArrayLength((jarray)this->_obj);
781
        RETURN_ERROR_IF_EXC(env);
782
        return Status::OK();
783
    }
784
785
    Status get_object_array_element(JNIEnv* env, jsize index, Jni::LocalObject* result) {
786
        DCHECK(!this->uninitialized());
787
        DCHECK(result->uninitialized());
788
        result->_obj = env->GetObjectArrayElement((jobjectArray)this->_obj, index);
789
        RETURN_ERROR_IF_EXC(env);
790
        return Status::OK();
791
    }
792
793
    Status get_byte_elements(JNIEnv* env, ByteArrayBufferGuard<Ref>* jni_bytes) const {
794
        DCHECK(!this->uninitialized());
795
        return ByteArrayBufferGuard<Ref>::create(env, *this, jni_bytes, nullptr);
796
    }
797
798
    Status get_byte_elements(JNIEnv* env, jsize start, jsize len, jbyte* buffer) {
799
        DCHECK(!this->uninitialized());
800
        env->GetByteArrayRegion((jbyteArray)this->_obj, start, len,
801
                                reinterpret_cast<jbyte*>(buffer));
802
        RETURN_ERROR_IF_EXC(env);
803
        return Status::OK();
804
    }
805
806
    static Status WriteBufferToByteArray(JNIEnv* env, const jbyte* buffer, jint size,
807
                                         Array<Local>* serialized_msg) {
808
        DCHECK(serialized_msg->uninitialized());
809
        /// create jbyteArray given buffer
810
        serialized_msg->_obj = env->NewByteArray(size);
811
        RETURN_ERROR_IF_EXC(env);
812
        if (serialized_msg->_obj == nullptr) [[unlikely]] {
813
            return Status::JniError("couldn't construct jbyteArray");
814
        }
815
        env->SetByteArrayRegion((jbyteArray)serialized_msg->_obj, 0, size, buffer);
816
        RETURN_ERROR_IF_EXC(env);
817
        return Status::OK();
818
    }
819
820
    template <class T>
821
    static Status SerializeThriftMsg(JNIEnv* env, T* msg, Array<Local>* serialized_msg) {
822
        int buffer_size = 100 * 1024; // start out with 100KB
823
        ThriftSerializer serializer(false, buffer_size);
824
825
        uint8_t* buffer = nullptr;
826
        uint32_t size = 0;
827
        RETURN_IF_ERROR(serializer.serialize(msg, &size, &buffer));
828
829
        // Make sure that 'size' is within the limit of INT_MAX as the use of
830
        // 'size' below takes int.
831
        if (size > INT_MAX) [[unlikely]] {
832
            return Status::JniError(
833
                    "The length of the serialization buffer ({} bytes) exceeds the limit of {} "
834
                    "bytes",
835
                    size, INT_MAX);
836
        }
837
        RETURN_IF_ERROR(WriteBufferToByteArray(env, (jbyte*)buffer, size, serialized_msg));
838
        return Status::OK();
839
    }
840
841
private:
842
    DISALLOW_COPY_AND_ASSIGN(Array);
843
};
844
845
template <RefType Ref>
846
class Class : public Object<Ref> {
847
public:
848
    Class() = default;
849
850
    static Status find_class(JNIEnv* env, const char* class_str, Class<Ref>* result) {
851
        DCHECK(result->uninitialized());
852
        if constexpr (Ref == Local) {
853
            result->_obj = env->FindClass(class_str);
854
            RETURN_ERROR_IF_EXC(env);
855
            return Status::OK();
856
        } else if constexpr (Ref == Global) {
857
            Class<Local> local_class;
858
            local_class._obj = env->FindClass(class_str);
859
            RETURN_ERROR_IF_EXC(env);
860
            return local_to_global_ref(env, local_class, result);
861
        } else {
862
            static_assert(false);
863
        }
864
    }
865
866
    Status get_static_method(JNIEnv* env, const char* method_str, const char* method_signature,
867
                             MethodId* method_id) const {
868
        DCHECK(!this->uninitialized());
869
        DCHECK(method_id->uninitialized());
870
        method_id->_id = env->GetStaticMethodID((jclass)this->_obj, method_str, method_signature);
871
        RETURN_ERROR_IF_EXC(env);
872
        return Status::OK();
873
    }
874
875
    Status get_method(JNIEnv* env, const char* method_str, const char* method_signature,
876
                      MethodId* method_id) const {
877
        DCHECK(!this->uninitialized());
878
        DCHECK(method_id->uninitialized());
879
        method_id->_id = env->GetMethodID((jclass)this->_obj, method_str, method_signature);
880
        RETURN_ERROR_IF_EXC(env);
881
        return Status::OK();
882
    }
883
884
    Status get_static_fieldId(JNIEnv* env, const char* name, const char* signature,
885
                              FieldId* field_id) const {
886
        DCHECK(!this->uninitialized());
887
        DCHECK(field_id->uninitialized());
888
        field_id->_id = env->GetStaticFieldID((jclass)this->_obj, name, signature);
889
        RETURN_ERROR_IF_EXC(env);
890
        return Status::OK();
891
    }
892
893
    Status get_static_object_field(JNIEnv* env, const FieldId& field_id,
894
                                   Object<Local>* result) const {
895
        DCHECK(!this->uninitialized());
896
        DCHECK(!field_id.uninitialized());
897
        result->_obj = env->GetStaticObjectField((jclass)this->_obj, field_id._id);
898
        RETURN_ERROR_IF_EXC(env);
899
        return Status::OK();
900
    }
901
902
    Status get_static_object_field(JNIEnv* env, const FieldId& field_id,
903
                                   Object<Global>* global_result) const {
904
        DCHECK(!this->uninitialized());
905
        DCHECK(!field_id.uninitialized());
906
        Object<Local> local_result;
907
        local_result._obj = env->GetStaticObjectField((jclass)this->_obj, field_id._id);
908
        RETURN_ERROR_IF_EXC(env);
909
        return local_to_global_ref(env, local_result, global_result);
910
    }
911
912
    Status get_static_object_field(JNIEnv* env, const char* name, const char* signature,
913
                                   Object<Global>* global_result) const {
914
        Jni::FieldId tmpFieldID;
915
        RETURN_IF_ERROR(get_static_fieldId(env, name, signature, &tmpFieldID));
916
        RETURN_IF_ERROR(get_static_object_field(env, tmpFieldID, global_result));
917
        return Status::OK();
918
    }
919
920
    FunctionCall<NewObject> new_object(JNIEnv* env, MethodId method_id) const {
921
        DCHECK(!this->uninitialized());
922
        DCHECK(!method_id.uninitialized());
923
        return FunctionCall<NewObject>::instance(env, (jclass)this->_obj, method_id._id);
924
    }
925
926
    FunctionCall<StaticObjectMethod> call_static_object_method(JNIEnv* env,
927
                                                               MethodId method_id) const {
928
        DCHECK(!this->uninitialized());
929
        DCHECK(!method_id.uninitialized());
930
        return FunctionCall<StaticObjectMethod>::instance(env, (jclass)this->_obj, method_id._id);
931
    }
932
933
    FunctionCall<StaticVoidMethod> call_static_void_method(JNIEnv* env, MethodId method_id) const {
934
        DCHECK(!this->uninitialized());
935
        DCHECK(!method_id.uninitialized());
936
        return FunctionCall<StaticVoidMethod>::instance(env, (jclass)this->_obj, method_id._id);
937
    }
938
939
private:
940
    DISALLOW_COPY_AND_ASSIGN(Class);
941
};
942
943
using LocalClass = Class<Local>;
944
using GlobalClass = Class<Global>;
945
946
using LocalArray = Array<Local>;
947
using GlobalArray = Array<Global>;
948
949
using LocalString = String<Local>;
950
using GlobalString = String<Global>;
951
952
template <CallTag tag>
953
template <RefType Ref>
954
FunctionCall<tag>& FunctionCall<tag>::with_arg(const Object<Ref>& obj) {
955
    jvalue v;
956
    std::memset(&v, 0, sizeof(v));
957
    v.l = obj._obj;
958
    _args.push_back(v);
959
    return *this;
960
}
961
962
template <CallTag tag>
963
template <RefType Ref>
964
NonvirtualFunctionCall<tag>& NonvirtualFunctionCall<tag>::with_arg(const Object<Ref>& obj) {
965
    jvalue v;
966
    std::memset(&v, 0, sizeof(v));
967
    v.l = obj._obj;
968
    this->_args.push_back(v);
969
    return *this;
970
}
971
972
template <CallTag tag>
973
Status FunctionCall<tag>::call(Object<Local>* result) {
974
    DCHECK(result->uninitialized());
975
    result->_obj = CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
976
    RETURN_ERROR_IF_EXC(this->_env);
977
    return Status::OK();
978
}
979
980
template <CallTag tag>
981
Status FunctionCall<tag>::call(Object<Global>* result) {
982
    DCHECK(result->uninitialized());
983
    Object<Local> local_result;
984
    local_result._obj = CallHelper<tag>::call_impl(_env, _base, _method, _args.data());
985
    RETURN_ERROR_IF_EXC(this->_env);
986
    return local_to_global_ref(_env, local_result, result);
987
}
988
989
template <CallTag tag>
990
Status NonvirtualFunctionCall<tag>::call(Object<Local>* result) {
991
    DCHECK(result->uninitialized());
992
    result->_obj = CallHelper<tag>::call_impl(this->_env, this->_base, _cls, this->_method,
993
                                              this->_args.data());
994
    RETURN_ERROR_IF_EXC(this->_env);
995
    return Status::OK();
996
}
997
998
template <RefType Ref>
999
template <RefType R>
1000
NonvirtualFunctionCall<NonvirtualObjectMethod> Object<Ref>::call_nonvirtual_object_method(
1001
        JNIEnv* env, const Class<R>& clazz, MethodId method_id) const {
1002
    DCHECK(!this->uninitialized());
1003
    DCHECK(!method_id.uninitialized());
1004
1005
    return NonvirtualFunctionCall<NonvirtualObjectMethod>::instance(env, _obj, (jclass)clazz._obj,
1006
                                                                    method_id._id);
1007
}
1008
1009
template <RefType Ref>
1010
template <RefType R>
1011
NonvirtualFunctionCall<NonvirtualVoidMethod> Object<Ref>::call_nonvirtual_void_method(
1012
        JNIEnv* env, const Class<R>& clazz, MethodId method_id) const {
1013
    DCHECK(!this->uninitialized());
1014
    DCHECK(!method_id.uninitialized());
1015
    return NonvirtualFunctionCall<NonvirtualVoidMethod>::instance(env, _obj, (jclass)clazz._obj,
1016
                                                                  method_id._id);
1017
}
1018
1019
template <RefType Ref>
1020
template <RefType R>
1021
NonvirtualFunctionCall<NonvirtualIntMethod> Object<Ref>::call_nonvirtual_int_method(
1022
        JNIEnv* env, const Class<R>& clazz, MethodId method_id) const {
1023
    DCHECK(!this->uninitialized());
1024
    DCHECK(!method_id.uninitialized());
1025
    return NonvirtualFunctionCall<NonvirtualIntMethod>::instance(env, _obj, (jclass)clazz._obj,
1026
                                                                 method_id._id);
1027
}
1028
1029
template <RefType Ref>
1030
template <RefType R>
1031
NonvirtualFunctionCall<NonvirtualBooleanMethod> Object<Ref>::call_nonvirtual_boolean_method(
1032
        JNIEnv* env, const Class<R>& clazz, MethodId method_id) const {
1033
    DCHECK(!this->uninitialized());
1034
    DCHECK(!method_id.uninitialized());
1035
    return NonvirtualFunctionCall<NonvirtualBooleanMethod>::instance(env, _obj, (jclass)clazz._obj,
1036
                                                                     method_id._id);
1037
}
1038
1039
class Util {
1040
public:
1041
    static size_t get_max_jni_heap_memory_size();
1042
1043
    template <RefType Ref>
1044
    static Status find_class(JNIEnv* env, const char* class_str, Class<Ref>* result) {
1045
        return Class<Ref>::find_class(env, class_str, result);
1046
    }
1047
1048
    template <RefType Ref>
1049
    static Status WriteBufferToByteArray(JNIEnv* env, const jbyte* buffer, jint size,
1050
                                         Array<Ref>* serialized_msg) {
1051
        if constexpr (Ref == Local) {
1052
            return Array<Local>::WriteBufferToByteArray(env, buffer, size, serialized_msg);
1053
        } else if constexpr (Ref == Global) {
1054
            Array<Local> local_obj;
1055
            RETURN_IF_ERROR(Array<Local>::WriteBufferToByteArray(env, buffer, size, &local_obj));
1056
            return local_to_global_ref(env, local_obj, serialized_msg);
1057
        } else {
1058
            static_assert(false);
1059
        }
1060
    }
1061
1062
    template <class T, RefType Ref>
1063
    static Status SerializeThriftMsg(JNIEnv* env, T* msg, Array<Ref>* serialized_msg) {
1064
        if constexpr (Ref == Local) {
1065
            return Array<Local>::SerializeThriftMsg(env, msg, serialized_msg);
1066
        } else if (Ref == Global) {
1067
            Array<Local> local_obj;
1068
            RETURN_IF_ERROR(Array<Local>::SerializeThriftMsg(env, msg, local_obj));
1069
            return local_to_global_ref(env, local_obj, serialized_msg);
1070
        } else {
1071
            static_assert(false);
1072
        }
1073
    }
1074
1075
    template <RefType Ref>
1076
    static Status get_jni_scanner_class(JNIEnv* env, const char* classname,
1077
                                        Object<Ref>* jni_scanner_class) {
1078
        // Get JNI scanner class by class name;
1079
        LocalString class_name_str;
1080
        RETURN_IF_ERROR(LocalString::new_string(env, classname, &class_name_str));
1081
        return jni_scanner_loader_obj_.call_object_method(env, jni_scanner_loader_method_)
1082
                .with_arg(class_name_str)
1083
                .call(jni_scanner_class);
1084
    }
1085
1086
    template <RefType Ref>
1087
    static Status convert_to_java_map(JNIEnv* env, const std::map<std::string, std::string>& map,
1088
                                      Object<Ref>* hashmap_object) {
1089
        RETURN_IF_ERROR(hashmap_class.new_object(env, hashmap_constructor)
1090
                                .with_arg((jint)map.size())
1091
                                .call(hashmap_object));
1092
1093
        for (const auto& it : map) {
1094
            LocalString key;
1095
            RETURN_IF_ERROR(String<Local>::new_string(env, it.first.c_str(), &key));
1096
1097
            LocalString value;
1098
            RETURN_IF_ERROR(String<Local>::new_string(env, it.second.c_str(), &value));
1099
1100
            LocalObject result;
1101
            RETURN_IF_ERROR(hashmap_object->call_object_method(env, hashmap_put)
1102
                                    .with_arg(key)
1103
                                    .with_arg(value)
1104
                                    .call());
1105
        }
1106
        return Status::OK();
1107
    }
1108
1109
    template <RefType Ref>
1110
    static Status convert_to_cpp_map(JNIEnv* env, const Object<Ref>& map,
1111
                                     std::map<std::string, std::string>* resultMap) {
1112
        LocalObject entrySet;
1113
        RETURN_IF_ERROR(map.call_object_method(env, mapEntrySetMethod).call(&entrySet));
1114
1115
        // Call the iterator method on the set to iterate over the key-value pairs
1116
        LocalObject iteratorSet;
1117
        RETURN_IF_ERROR(entrySet.call_object_method(env, iteratorSetMethod).call(&iteratorSet));
1118
1119
        while (true) {
1120
            jboolean hasNext = false;
1121
            RETURN_IF_ERROR(
1122
                    iteratorSet.call_boolean_method(env, iteratorHasNextMethod).call(&hasNext));
1123
            if (!hasNext) {
1124
                break;
1125
            }
1126
1127
            LocalObject entry;
1128
            RETURN_IF_ERROR(iteratorSet.call_object_method(env, iteratorNextMethod).call(&entry));
1129
1130
            LocalString javaKey;
1131
            RETURN_IF_ERROR(entry.call_object_method(env, getEntryKeyMethod).call(&javaKey));
1132
1133
            LocalString javaValue;
1134
            RETURN_IF_ERROR(entry.call_object_method(env, getEntryValueMethod).call(&javaValue));
1135
1136
            LocalStringBufferGuard key;
1137
            RETURN_IF_ERROR(javaKey.get_string_chars(env, &key));
1138
1139
            LocalStringBufferGuard value;
1140
            RETURN_IF_ERROR(javaValue.get_string_chars(env, &value));
1141
1142
            // Store the key-value pair in the map
1143
            (*resultMap)[key.get()] = value.get();
1144
        }
1145
        return Status::OK();
1146
    }
1147
1148
    static Status clean_udf_class_load_cache(const std::string& function_signature);
1149
1150
    static Status Init();
1151
1152
private:
1153
    static void _parse_max_heap_memory_size_from_jvm();
1154
1155
    static Status _init_collect_class() WARN_UNUSED_RESULT;
1156
    static Status _init_register_natives() WARN_UNUSED_RESULT;
1157
    static Status _init_jni_scanner_loader() WARN_UNUSED_RESULT;
1158
1159
    static bool jvm_inited_;
1160
1161
    // for jvm heap
1162
    static jlong max_jvm_heap_memory_size_;
1163
1164
    // for JNI scanner loader
1165
    static GlobalObject jni_scanner_loader_obj_;
1166
    static MethodId jni_scanner_loader_method_;
1167
1168
    // for clean udf cache
1169
    static MethodId _clean_udf_cache_method_id;
1170
1171
    //for hashmap
1172
    static GlobalClass hashmap_class;
1173
    static MethodId hashmap_constructor;
1174
    static MethodId hashmap_put;
1175
1176
    //for map
1177
    static GlobalClass mapClass;
1178
    static MethodId mapEntrySetMethod;
1179
1180
    // for map entry
1181
    static GlobalClass mapEntryClass;
1182
    static MethodId getEntryKeyMethod;
1183
    static MethodId getEntryValueMethod;
1184
1185
    //for set
1186
    static GlobalClass setClass;
1187
    static MethodId iteratorSetMethod;
1188
1189
    // for iterator
1190
    static GlobalClass iteratorClass;
1191
    static MethodId iteratorHasNextMethod;
1192
    static MethodId iteratorNextMethod;
1193
};
1194
}; // namespace Jni
1195
1196
} // namespace doris