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
9 // http://www.apache.org/licenses/LICENSE-2.0
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
26 #include "org_apache_arrow_c_jni_JniWrapper.h"
30 jclass
CreateGlobalClassReference(JNIEnv
* env
, const char* class_name
) {
31 jclass local_class
= env
->FindClass(class_name
);
32 jclass global_class
= (jclass
)env
->NewGlobalRef(local_class
);
33 env
->DeleteLocalRef(local_class
);
37 jclass illegal_access_exception_class
;
38 jclass illegal_argument_exception_class
;
39 jclass runtime_exception_class
;
40 jclass private_data_class
;
42 jmethodID private_data_close_method
;
44 jint JNI_VERSION
= JNI_VERSION_1_6
;
46 class JniPendingException
: public std::runtime_error
{
48 explicit JniPendingException(const std::string
& arg
) : std::runtime_error(arg
) {}
51 void ThrowPendingException(const std::string
& message
) {
52 throw JniPendingException(message
);
55 void JniThrow(std::string message
) { ThrowPendingException(message
); }
57 jmethodID
GetMethodID(JNIEnv
* env
, jclass this_class
, const char* name
, const char* sig
) {
58 jmethodID ret
= env
->GetMethodID(this_class
, name
, sig
);
60 std::string error_message
= "Unable to find method " + std::string(name
) +
61 " within signature " + std::string(sig
);
62 ThrowPendingException(error_message
);
67 class InnerPrivateData
{
69 InnerPrivateData(JavaVM
* vm
, jobject private_data
)
70 : vm_(vm
), j_private_data_(private_data
) {}
73 jobject j_private_data_
;
78 explicit JNIEnvGuard(JavaVM
* vm
) : vm_(vm
), should_detach_(false) {
80 jint code
= vm
->GetEnv(reinterpret_cast<void**>(&env
), JNI_VERSION
);
81 if (code
== JNI_EDETACHED
) {
82 JavaVMAttachArgs args
;
83 args
.version
= JNI_VERSION
;
86 code
= vm
->AttachCurrentThread(reinterpret_cast<void**>(&env
), &args
);
87 should_detach_
= (code
== JNI_OK
);
90 ThrowPendingException("Failed to attach the current thread to a Java VM");
95 JNIEnv
* env() { return env_
; }
99 vm_
->DetachCurrentThread();
100 should_detach_
= false;
110 template <typename T
>
111 void release_exported(T
* base
) {
112 // This should not be called on already released structure
113 assert(base
->release
!= nullptr);
116 for (int64_t i
= 0; i
< base
->n_children
; ++i
) {
117 T
* child
= base
->children
[i
];
118 if (child
->release
!= nullptr) {
119 child
->release(child
);
120 assert(child
->release
== nullptr);
124 // Release dictionary
125 T
* dict
= base
->dictionary
;
126 if (dict
!= nullptr && dict
->release
!= nullptr) {
128 assert(dict
->release
== nullptr);
131 // Release all data directly owned by the struct
132 InnerPrivateData
* private_data
=
133 reinterpret_cast<InnerPrivateData
*>(base
->private_data
);
135 JNIEnvGuard
guard(private_data
->vm_
);
136 JNIEnv
* env
= guard
.env();
138 env
->CallObjectMethod(private_data
->j_private_data_
, private_data_close_method
);
139 if (env
->ExceptionCheck()) {
140 env
->ExceptionDescribe();
141 env
->ExceptionClear();
142 ThrowPendingException("Error calling close of private data");
144 env
->DeleteGlobalRef(private_data
->j_private_data_
);
146 base
->private_data
= nullptr;
149 base
->release
= nullptr;
153 #define JNI_METHOD_START try {
156 #define JNI_METHOD_END(fallback_expr) \
158 catch (JniPendingException & e) { \
159 env->ThrowNew(runtime_exception_class, e.what()); \
160 return fallback_expr; \
164 jint
JNI_OnLoad(JavaVM
* vm
, void* reserved
) {
166 if (vm
->GetEnv(reinterpret_cast<void**>(&env
), JNI_VERSION
) != JNI_OK
) {
170 illegal_access_exception_class
=
171 CreateGlobalClassReference(env
, "Ljava/lang/IllegalAccessException;");
172 illegal_argument_exception_class
=
173 CreateGlobalClassReference(env
, "Ljava/lang/IllegalArgumentException;");
174 runtime_exception_class
=
175 CreateGlobalClassReference(env
, "Ljava/lang/RuntimeException;");
177 CreateGlobalClassReference(env
, "Lorg/apache/arrow/c/jni/PrivateData;");
179 private_data_close_method
= GetMethodID(env
, private_data_class
, "close", "()V");
182 JNI_METHOD_END(JNI_ERR
)
185 void JNI_OnUnload(JavaVM
* vm
, void* reserved
) {
187 vm
->GetEnv(reinterpret_cast<void**>(&env
), JNI_VERSION
);
188 env
->DeleteGlobalRef(illegal_access_exception_class
);
189 env
->DeleteGlobalRef(illegal_argument_exception_class
);
190 env
->DeleteGlobalRef(runtime_exception_class
);
194 * Class: org_apache_arrow_c_jni_JniWrapper
195 * Method: releaseSchema
198 JNIEXPORT
void JNICALL
Java_org_apache_arrow_c_jni_JniWrapper_releaseSchema(
199 JNIEnv
* env
, jobject
, jlong address
) {
201 ArrowSchema
* schema
= reinterpret_cast<ArrowSchema
*>(address
);
202 if (schema
->release
!= nullptr) {
203 schema
->release(schema
);
209 * Class: org_apache_arrow_c_jni_JniWrapper
210 * Method: releaseArray
213 JNIEXPORT
void JNICALL
214 Java_org_apache_arrow_c_jni_JniWrapper_releaseArray(JNIEnv
* env
, jobject
, jlong address
) {
216 ArrowArray
* array
= reinterpret_cast<ArrowArray
*>(address
);
217 if (array
->release
!= nullptr) {
218 array
->release(array
);
224 * Class: org_apache_arrow_c_jni_JniWrapper
225 * Method: exportSchema
226 * Signature: (JLorg/apache/arrow/c/jni/PrivateData;)V
228 JNIEXPORT
void JNICALL
Java_org_apache_arrow_c_jni_JniWrapper_exportSchema(
229 JNIEnv
* env
, jobject
, jlong address
, jobject private_data
) {
231 ArrowSchema
* schema
= reinterpret_cast<ArrowSchema
*>(address
);
234 if (env
->GetJavaVM(&vm
) != JNI_OK
) {
235 JniThrow("Unable to get JavaVM instance");
237 jobject private_data_ref
= env
->NewGlobalRef(private_data
);
239 schema
->private_data
= new InnerPrivateData(vm
, private_data_ref
);
240 schema
->release
= &release_exported
<ArrowSchema
>;
245 * Class: org_apache_arrow_c_jni_JniWrapper
246 * Method: exportArray
247 * Signature: (JLorg/apache/arrow/c/jni/PrivateData;)V
249 JNIEXPORT
void JNICALL
Java_org_apache_arrow_c_jni_JniWrapper_exportArray(
250 JNIEnv
* env
, jobject
, jlong address
, jobject private_data
) {
252 ArrowArray
* array
= reinterpret_cast<ArrowArray
*>(address
);
255 if (env
->GetJavaVM(&vm
) != JNI_OK
) {
256 JniThrow("Unable to get JavaVM instance");
258 jobject private_data_ref
= env
->NewGlobalRef(private_data
);
260 array
->private_data
= new InnerPrivateData(vm
, private_data_ref
);
261 array
->release
= &release_exported
<ArrowArray
>;