mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-28 05:24:22 +01:00
Merge 'bindings/java: Enhance exception handling logic' from Kim Seon Woo
### Purpose of this PR - Enhance exception handling logic - When exceptions has to be thrown from Rust to Java, let's just return the error message directly. - Removes JNI call to get error message using `Java_org_github_tursodatabase_core_LimboDB_getErrorMessageUtf8` - Add `throwJavaException` to assure that the exception throwing logic works corretly Closes #642
This commit is contained in:
@@ -50,18 +50,34 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB__1open_1utf8<'
|
||||
Box::into_raw(Box::new(db)) as jlong
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB_throwJavaException<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
obj: JObject<'local>,
|
||||
error_code: jint,
|
||||
) {
|
||||
set_err_msg_and_throw_exception(
|
||||
&mut env,
|
||||
obj,
|
||||
error_code,
|
||||
"throw java exception".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
fn set_err_msg_and_throw_exception<'local>(
|
||||
env: &mut JNIEnv<'local>,
|
||||
obj: JObject<'local>,
|
||||
err_code: i32,
|
||||
err_msg: String,
|
||||
) {
|
||||
let error_message_pointer = Box::into_raw(Box::new(err_msg)) as i64;
|
||||
let error_message_bytes = env
|
||||
.byte_array_from_slice(err_msg.as_bytes())
|
||||
.expect("Failed to convert to byte array");
|
||||
match env.call_method(
|
||||
obj,
|
||||
"newSQLException",
|
||||
"(IJ)Lorg/github/tursodatabase/exceptions/LimboException;",
|
||||
&[err_code.into(), error_message_pointer.into()],
|
||||
"throwLimboException",
|
||||
"(I[B)V",
|
||||
&[err_code.into(), (&error_message_bytes).into()],
|
||||
) {
|
||||
Ok(_) => {
|
||||
// do nothing because above method will always return Err
|
||||
@@ -71,16 +87,3 @@ fn set_err_msg_and_throw_exception<'local>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn Java_org_github_tursodatabase_core_LimboDB_getErrorMessageUtf8<
|
||||
'local,
|
||||
>(
|
||||
env: JNIEnv<'local>,
|
||||
_obj: JObject<'local>,
|
||||
error_message_ptr: jlong,
|
||||
) -> JByteArray<'local> {
|
||||
let error_message = Box::from_raw(error_message_ptr as *mut String);
|
||||
let error_message_bytes = error_message.as_bytes();
|
||||
env.byte_array_from_slice(error_message_bytes).unwrap()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Annotation to mark methods that are called by native functions.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface NativeInvocation {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.github.tursodatabase;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark methods that use larger visibility for testing purposes.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface VisibleForTesting {
|
||||
}
|
||||
@@ -172,35 +172,4 @@ public abstract class AbstractDB {
|
||||
// TODO: add implementation
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws SQL Exception with error code.
|
||||
*
|
||||
* @param errorCode Error code to be passed.
|
||||
* @throws SQLException Formatted SQLException with error code
|
||||
*/
|
||||
@NativeInvocation
|
||||
private LimboException newSQLException(int errorCode, long errorMessagePointer) throws SQLException {
|
||||
throw newSQLException(errorCode, getErrorMessage(errorMessagePointer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code to be passed.
|
||||
* @param errorMessage throw newSQLException(errorCode);Error message to be passed.
|
||||
* @return Formatted SQLException with error code and message.
|
||||
*/
|
||||
public static LimboException newSQLException(int errorCode, String errorMessage) {
|
||||
LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode);
|
||||
String msg;
|
||||
if (code == LimboErrorCode.UNKNOWN_ERROR) {
|
||||
msg = String.format("%s:%s (%s)", code, errorCode, errorMessage);
|
||||
} else {
|
||||
msg = String.format("%s (%s)", code, errorMessage);
|
||||
}
|
||||
return new LimboException(msg, code);
|
||||
}
|
||||
|
||||
protected abstract String getErrorMessage(long errorMessagePointer);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package org.github.tursodatabase.core;
|
||||
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
import org.github.tursodatabase.NativeInvocation;
|
||||
import org.github.tursodatabase.VisibleForTesting;
|
||||
import org.github.tursodatabase.exceptions.LimboException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
@@ -30,8 +33,7 @@ public final class LimboDB extends AbstractDB {
|
||||
// url example: "jdbc:sqlite:{fileName}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url e.g. "jdbc:sqlite:fileName
|
||||
* @param url e.g. "jdbc:sqlite:fileName
|
||||
* @param fileName e.g. path to file
|
||||
*/
|
||||
public static LimboDB create(String url, String fileName) throws SQLException {
|
||||
@@ -83,7 +85,7 @@ public final class LimboDB extends AbstractDB {
|
||||
@Override
|
||||
protected void _open(String fileName, int openFlags) throws SQLException {
|
||||
if (isOpen) {
|
||||
throw newSQLException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened");
|
||||
throwLimboException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened");
|
||||
}
|
||||
dbPtr = _open_utf8(stringToUtf8ByteArray(fileName), openFlags);
|
||||
isOpen = true;
|
||||
@@ -103,12 +105,38 @@ public final class LimboDB extends AbstractDB {
|
||||
@Override
|
||||
public synchronized native int step(long stmt);
|
||||
|
||||
@Override
|
||||
protected String getErrorMessage(long errorMessagePointer) {
|
||||
return utf8ByteBufferToString(getErrorMessageUtf8(errorMessagePointer));
|
||||
@VisibleForTesting
|
||||
native void throwJavaException(int errorCode) throws SQLException;
|
||||
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
@NativeInvocation
|
||||
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
|
||||
String errorMessage = utf8ByteBufferToString(errorMessageBytes);
|
||||
throwLimboException(errorCode, errorMessage);
|
||||
}
|
||||
|
||||
private native byte[] getErrorMessageUtf8(long errorMessagePointer);
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessage Error message.
|
||||
*/
|
||||
public void throwLimboException(int errorCode, String errorMessage) throws SQLException {
|
||||
LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode);
|
||||
String msg;
|
||||
if (code == LimboErrorCode.UNKNOWN_ERROR) {
|
||||
msg = String.format("%s:%s (%s)", code, errorCode, errorMessage);
|
||||
} else {
|
||||
msg = String.format("%s (%s)", code, errorMessage);
|
||||
}
|
||||
|
||||
throw new LimboException(msg, code);
|
||||
}
|
||||
|
||||
private static String utf8ByteBufferToString(byte[] buffer) {
|
||||
if (buffer == null) {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.github.tursodatabase.exceptions.LimboException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class LimboDBTest {
|
||||
@@ -26,4 +29,20 @@ public class LimboDBTest {
|
||||
|
||||
assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwJavaException_should_throw_appropriate_java_exception() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath);
|
||||
db.load();
|
||||
|
||||
final int limboExceptionCode = LimboErrorCode.ETC.code;
|
||||
try {
|
||||
db.throwJavaException(limboExceptionCode);
|
||||
} catch (Exception e) {
|
||||
assertThat(e).isInstanceOf(LimboException.class);
|
||||
LimboException limboException = (LimboException) e;
|
||||
assertThat(limboException.getResultCode().code).isEqualTo(limboExceptionCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user