blob: a59185c36dd046f63d8543e59cb1b5f7b0f50924 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use jni::Env;
use jni::errors::ErrorPolicy;
use jni::jni_sig;
use jni::jni_str;
use jni::objects::JThrowable;
use jni::objects::JValue;
use jni::strings::JNIString;
use opendal::ErrorKind;
pub(crate) struct Error {
inner: opendal::Error,
}
impl Error {
pub(crate) fn throw(&self, env: &mut Env) {
if let Err(err) = self.do_throw(env) {
match err {
jni::errors::Error::JavaException => {
// The exception has been thrown; it will propagate to the JVM.
}
_ => env.fatal_error(JNIString::new(err.to_string()).as_ref()),
}
}
}
pub(crate) fn to_exception<'local>(
&self,
env: &mut Env<'local>,
) -> jni::errors::Result<JThrowable<'local>> {
let code = env.new_string(match self.inner.kind() {
ErrorKind::Unexpected => "Unexpected",
ErrorKind::Unsupported => "Unsupported",
ErrorKind::ConfigInvalid => "ConfigInvalid",
ErrorKind::NotFound => "NotFound",
ErrorKind::PermissionDenied => "PermissionDenied",
ErrorKind::IsADirectory => "IsADirectory",
ErrorKind::NotADirectory => "NotADirectory",
ErrorKind::AlreadyExists => "AlreadyExists",
ErrorKind::RateLimited => "RateLimited",
ErrorKind::IsSameFile => "IsSameFile",
ErrorKind::ConditionNotMatch => "ConditionNotMatch",
ErrorKind::RangeNotSatisfied => "RangeNotSatisfied",
_ => "Unexpected",
})?;
let message = env.new_string(format!("{:?}", self.inner))?;
let exception = env.new_object(
jni_str!("org/apache/opendal/OpenDALException"),
jni_sig!("(Ljava/lang/String;Ljava/lang/String;)V"),
&[JValue::Object(&code), JValue::Object(&message)],
)?;
// SAFETY: `exception` was just constructed as an `OpenDALException`, a
// subclass of `java.lang.Throwable`.
Ok(unsafe { JThrowable::from_raw(env, exception.into_raw()) })
}
fn do_throw(&self, env: &mut Env) -> jni::errors::Result<()> {
let exception = self.to_exception(env)?;
env.throw(exception)
}
}
/// A native-method [`ErrorPolicy`] that converts a Rust [`Error`] into an
/// `OpenDALException` and a panic into a `RuntimeException`, returning the
/// default value to the JVM in both cases.
pub(crate) enum ThrowException {}
impl<T: Default> ErrorPolicy<T, Error> for ThrowException {
type Captures<'unowned_env_local: 'native_method, 'native_method> = ();
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: Error,
) -> jni::errors::Result<T> {
if !env.exception_check() {
err.throw(env);
}
Ok(T::default())
}
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> jni::errors::Result<T> {
if !env.exception_check() {
fn downcast_payload(payload: Box<dyn std::any::Any + Send + 'static>) -> String {
let payload = match payload.downcast::<&'static str>() {
Ok(payload) => return payload.to_string(),
Err(payload) => payload,
};
let payload = match payload.downcast::<String>() {
Ok(payload) => return *payload,
Err(payload) => payload,
};
let _ = payload;
"native method panicked".to_string()
}
let payload = downcast_payload(payload);
env.fatal_error(JNIString::new(payload).as_ref());
}
Ok(T::default())
}
}
impl From<opendal::Error> for Error {
fn from(err: opendal::Error) -> Self {
Self { inner: err }
}
}
impl From<jni::errors::Error> for Error {
fn from(err: jni::errors::Error) -> Self {
opendal::Error::new(ErrorKind::Unexpected, err.to_string()).into()
}
}
impl Debug for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.inner, f)
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.source()
}
}