@@ -205,6 +205,8 @@ pub struct JVMClasses<'a> {
205
205
pub class_get_name_method : JMethodID ,
206
206
/// Cached method ID for "java.lang.Throwable#getMessage"
207
207
pub throwable_get_message_method : JMethodID ,
208
+ /// Cached method ID for "java.lang.Throwable#getCause"
209
+ pub throwable_get_cause_method : JMethodID ,
208
210
209
211
/// The CometMetricNode class. Used for updating the metrics.
210
212
pub comet_metric_node : CometMetricNode < ' a > ,
@@ -213,6 +215,7 @@ pub struct JVMClasses<'a> {
213
215
}
214
216
215
217
unsafe impl < ' a > Send for JVMClasses < ' a > { }
218
+
216
219
unsafe impl < ' a > Sync for JVMClasses < ' a > { }
217
220
218
221
/// Keeps global references to JVM classes. Used for JNI calls to JVM.
@@ -241,10 +244,16 @@ impl JVMClasses<'_> {
241
244
. get_method_id ( clazz, "getMessage" , "()Ljava/lang/String;" )
242
245
. unwrap ( ) ;
243
246
247
+ let clazz = env. find_class ( "java/lang/Throwable" ) . unwrap ( ) ;
248
+ let throwable_get_cause_method = env
249
+ . get_method_id ( clazz, "getCause" , "()Ljava/lang/Throwable;" )
250
+ . unwrap ( ) ;
251
+
244
252
JVMClasses {
245
253
object_get_class_method,
246
254
class_get_name_method,
247
255
throwable_get_message_method,
256
+ throwable_get_cause_method,
248
257
comet_metric_node : CometMetricNode :: new ( env) . unwrap ( ) ,
249
258
comet_exec : CometExec :: new ( env) . unwrap ( ) ,
250
259
}
@@ -277,54 +286,93 @@ pub(crate) fn check_exception(env: &mut JNIEnv) -> CometResult<Option<CometError
277
286
Ok ( result)
278
287
}
279
288
280
- /// Given a `JThrowable` which is thrown from calling a Java method on the native side,
281
- /// this converts it into a `CometError::JavaException` with the exception class name
282
- /// and exception message. This error can then be populated to the JVM side to let
283
- /// users know the cause of the native side error.
284
- pub ( crate ) fn convert_exception (
289
+ /// get the class name of the exception by:
290
+ /// 1. get the `Class` object of the input `throwable` via `Object#getClass` method
291
+ /// 2. get the exception class name via calling `Class#getName` on the above object
292
+ fn get_throwable_class_name (
285
293
env : & mut JNIEnv ,
294
+ jvm_classes : & JVMClasses ,
286
295
throwable : & JThrowable ,
287
- ) -> CometResult < CometError > {
296
+ ) -> CometResult < String > {
288
297
unsafe {
289
- let cache = JVMClasses :: get ( ) ;
290
-
291
- // get the class name of the exception by:
292
- // 1. get the `Class` object of the input `throwable` via `Object#getClass` method
293
- // 2. get the exception class name via calling `Class#getName` on the above object
294
298
let class_obj = env
295
299
. call_method_unchecked (
296
300
throwable,
297
- cache . object_get_class_method ,
301
+ jvm_classes . object_get_class_method ,
298
302
ReturnType :: Object ,
299
303
& [ ] ,
300
304
) ?
301
305
. l ( ) ?;
302
- let exception_class_name = env
306
+ let class_name = env
303
307
. call_method_unchecked (
304
308
class_obj,
305
- cache . class_get_name_method ,
309
+ jvm_classes . class_get_name_method ,
306
310
ReturnType :: Object ,
307
311
& [ ] ,
308
312
) ?
309
313
. l ( ) ?
310
314
. into ( ) ;
311
- let exception_class_name_str = env. get_string ( & exception_class_name ) ?. into ( ) ;
315
+ let class_name_str = env. get_string ( & class_name ) ?. into ( ) ;
312
316
313
- // get the exception message via calling `Throwable#getMessage` on the throwable object
317
+ Ok ( class_name_str)
318
+ }
319
+ }
320
+
321
+ /// Get the exception message via calling `Throwable#getMessage` on the throwable object
322
+ fn get_throwable_message (
323
+ env : & mut JNIEnv ,
324
+ jvm_classes : & JVMClasses ,
325
+ throwable : & JThrowable ,
326
+ ) -> CometResult < String > {
327
+ unsafe {
314
328
let message = env
315
329
. call_method_unchecked (
316
330
throwable,
317
- cache . throwable_get_message_method ,
331
+ jvm_classes . throwable_get_message_method ,
318
332
ReturnType :: Object ,
319
333
& [ ] ,
320
334
) ?
321
335
. l ( ) ?
322
336
. into ( ) ;
323
337
let message_str = env. get_string ( & message) ?. into ( ) ;
324
338
325
- Ok ( CometError :: JavaException {
326
- class : exception_class_name_str,
327
- msg : message_str,
328
- } )
339
+ let cause: JThrowable = env
340
+ . call_method_unchecked (
341
+ throwable,
342
+ jvm_classes. throwable_get_cause_method ,
343
+ ReturnType :: Object ,
344
+ & [ ] ,
345
+ ) ?
346
+ . l ( ) ?
347
+ . into ( ) ;
348
+
349
+ if !cause. is_null ( ) {
350
+ let cause_class_name = get_throwable_class_name ( env, jvm_classes, & cause) ?;
351
+ let cause_message = get_throwable_message ( env, jvm_classes, & cause) ?;
352
+ Ok ( format ! (
353
+ "{}\n Caused by: {}: {}" ,
354
+ message_str, cause_class_name, cause_message
355
+ ) )
356
+ } else {
357
+ Ok ( message_str)
358
+ }
329
359
}
330
360
}
361
+
362
+ /// Given a `JThrowable` which is thrown from calling a Java method on the native side,
363
+ /// this converts it into a `CometError::JavaException` with the exception class name
364
+ /// and exception message. This error can then be populated to the JVM side to let
365
+ /// users know the cause of the native side error.
366
+ pub ( crate ) fn convert_exception (
367
+ env : & mut JNIEnv ,
368
+ throwable : & JThrowable ,
369
+ ) -> CometResult < CometError > {
370
+ let cache = JVMClasses :: get ( ) ;
371
+ let exception_class_name_str = get_throwable_class_name ( env, cache, throwable) ?;
372
+ let message_str = get_throwable_message ( env, cache, throwable) ?;
373
+
374
+ Ok ( CometError :: JavaException {
375
+ class : exception_class_name_str,
376
+ msg : message_str,
377
+ } )
378
+ }
0 commit comments