-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JNI bindings for FileStorage #61
Changes from 14 commits
dba3768
c7f09fb
504060a
f9b0b1b
a634e8f
c73a579
286a5f0
d14d3f6
40911c5
4bc017a
b119612
edefa49
c547043
f7dc07d
9131940
17fc257
e3e8242
f69de7f
5a49390
f922bf3
10f237a
f4e3df3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,17 @@ jobs: | |
- name: Build Release | ||
run: cargo build --verbose --release | ||
|
||
- name: Install JDK | ||
uses: actions/[email protected] | ||
with: | ||
distribution: 'temurin' | ||
java-version: '22' | ||
|
||
- name: Java tests | ||
run: | | ||
cd fs-storage | ||
make test-linux | ||
|
||
windows: | ||
name: Test on Windows | ||
runs-on: windows-latest | ||
|
@@ -54,6 +65,17 @@ jobs: | |
- name: Run tests | ||
run: cargo test --workspace --verbose | ||
|
||
- name: Install JDK | ||
uses: actions/[email protected] | ||
with: | ||
distribution: 'temurin' | ||
java-version: '22' | ||
|
||
- name: Java tests | ||
run: | | ||
cd fs-storage | ||
make test-windows | ||
|
||
mac-intel: | ||
name: Test on macOS Intel | ||
runs-on: macos-14 | ||
|
@@ -66,3 +88,14 @@ jobs: | |
|
||
- name: Run tests | ||
run: cargo test --workspace --verbose | ||
|
||
- name: Install JDK | ||
uses: actions/[email protected] | ||
with: | ||
distribution: 'temurin' | ||
java-version: '22' | ||
|
||
- name: Java tests | ||
run: | | ||
cd fs-storage | ||
make test-mac |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
target | ||
Cargo.lock | ||
**/app_id | ||
*.class |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,52 @@ | ||||||
CARGO_CMD = cargo | ||||||
JAR_FILE := junit.jar | ||||||
URL := https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.9.3/junit-platform-console-standalone-1.9.3.jar | ||||||
|
||||||
.PHONY: test-linux | ||||||
test-linux: build-rust download-test-lib java-test-linux | ||||||
|
||||||
.PHONY: test-mac | ||||||
test-mac: build-rust download-test-lib java-test-mac | ||||||
|
||||||
.PHONY: test-windows | ||||||
test-windows: build-rust download-test-lib java-test-windows | ||||||
Comment on lines
+6
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can detect the OS in the For reference: https://gist.github.com/sighingnow/deee806603ec9274fd47 |
||||||
|
||||||
.PHONY: build-rust | ||||||
build-rust: | ||||||
$(CARGO_CMD) build | ||||||
|
||||||
.PHONY: download-test-lib | ||||||
download-test-lib: | ||||||
cd tests && \ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
So it doesn't print the command to the terminal each time |
||||||
if [ ! -f "$(JAR_FILE)" ]; then \ | ||||||
curl -o "$(JAR_FILE)" "$(URL)"; \ | ||||||
else \ | ||||||
echo "$(JAR_FILE) already exists."; \ | ||||||
fi | ||||||
|
||||||
.PHONY: java-test-linux | ||||||
java-test-linux: | ||||||
(cd ../target/debug && \ | ||||||
export LD_LIBRARY_PATH=$$PWD && \ | ||||||
cd ../../fs-storage/tests && \ | ||||||
javac -d out FileStorage.java && \ | ||||||
javac -d out -cp out:$(JAR_FILE) FileStorageTest.java && \ | ||||||
RUST_BACKTRACE=1 java -jar $(JAR_FILE) --class-path out --scan-class-path) | ||||||
|
||||||
.PHONY: java-test-mac | ||||||
java-test-mac: | ||||||
(cd ../target/debug && \ | ||||||
export DYLD_LIBRARY_PATH=$$PWD && \ | ||||||
cd ../../fs-storage/tests && \ | ||||||
javac -d out FileStorage.java && \ | ||||||
javac -d out -cp out:$(JAR_FILE) FileStorageTest.java && \ | ||||||
RUST_BACKTRACE=1 java -jar $(JAR_FILE) --class-path out --scan-class-path) | ||||||
|
||||||
.PHONY: java-test-windows | ||||||
java-test-windows: | ||||||
(cd ../target/debug && \ | ||||||
export LIBRARY_PATH="$$PWD" && \ | ||||||
cd ../../fs-storage/tests && \ | ||||||
javac -d out FileStorage.java && \ | ||||||
javac -d out -cp "out;$(JAR_FILE)" FileStorageTest.java && \ | ||||||
RUST_BACKTRACE=1 java -Djava.library.path=$$LIBRARY_PATH -jar $(JAR_FILE) --class-path out --scan-class-path) |
kirillt marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,3 +44,15 @@ key2: value2 | |
```bash | ||
cargo run --example cli read /tmp/z | ||
``` | ||
|
||
## Java Wrapper | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These commands should be run inside |
||
```java | ||
javac -h . FileStorage.java | ||
javac FileStorage.java | ||
LD_LIBRARY_PATH=<project-root-path>/target/debug && java FileStorage.java | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The project root path is always |
||
``` | ||
|
||
## Steps to test Java Wrapper | ||
```bash | ||
cd tests && make test | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no |
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
#[cfg(feature = "jni-bindings")] | ||
pub mod jni_bindings { | ||
use std::collections::BTreeMap; | ||
use std::path::Path; | ||
|
||
use jni::signature::ReturnType; | ||
// This is the interface to the JVM that we'll call the majority of our | ||
// methods on. | ||
use jni::JNIEnv; | ||
|
||
// These objects are what you should use as arguments to your native | ||
// function. They carry extra lifetime information to prevent them escaping | ||
// this context and getting used after being GC'd. | ||
use jni::objects::{JClass, JString, JValue}; | ||
|
||
// This is just a pointer. We'll be returning it from our function. We | ||
// can't return one of the objects with lifetime information because the | ||
// lifetime checker won't let us. | ||
use jni::sys::{jlong, jobject, jstring}; | ||
|
||
use crate::base_storage::BaseStorage; | ||
|
||
use crate::file_storage::FileStorage; | ||
|
||
impl FileStorage<String, String> { | ||
fn from_jlong<'a>(value: jlong) -> &'a mut Self { | ||
unsafe { &mut *(value as *mut FileStorage<String, String>) } | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_create<'local>( | ||
mut env: JNIEnv<'local>, | ||
_class: JClass, | ||
label: JString<'local>, | ||
path: JString<'local>, | ||
) -> jlong { | ||
let label: String = env | ||
.get_string(&label) | ||
.expect("Couldn't get label!") | ||
.into(); | ||
let path: String = env | ||
.get_string(&path) | ||
.expect("Couldn't get path!") | ||
.into(); | ||
|
||
let file_storage: FileStorage<String, String> = | ||
FileStorage::new(label, Path::new(&path)); | ||
Box::into_raw(Box::new(file_storage)) as jlong | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_set<'local>( | ||
mut env: JNIEnv<'local>, | ||
_class: JClass, | ||
id: JString<'local>, | ||
value: JString<'local>, | ||
file_storage_ptr: jlong, | ||
) { | ||
let id: String = env.get_string(&id).expect("msg").into(); | ||
let value: String = env.get_string(&value).expect("msg").into(); | ||
|
||
FileStorage::from_jlong(file_storage_ptr).set(id, value); | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_remove<'local>( | ||
mut env: JNIEnv<'local>, | ||
_class: JClass, | ||
id: JString<'local>, | ||
file_storage_ptr: jlong, | ||
) -> jstring { | ||
let id: String = env.get_string(&id).unwrap().into(); | ||
match FileStorage::from_jlong(file_storage_ptr).remove(&id) { | ||
Ok(()) => env.new_string("").unwrap().into_raw(), | ||
Err(err) => env | ||
.new_string(err.to_string()) | ||
.unwrap() | ||
.into_raw(), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_needsSyncing( | ||
env: JNIEnv<'_>, | ||
_class: JClass, | ||
file_storage_ptr: jlong, | ||
) -> jstring { | ||
match FileStorage::from_jlong(file_storage_ptr).needs_syncing() { | ||
Ok(updated) => { | ||
let result = if updated { | ||
"true" | ||
} else { | ||
"false" | ||
}; | ||
env.new_string(result).unwrap().into_raw() | ||
} | ||
Err(err) => env | ||
.new_string(err.to_string()) | ||
.unwrap() | ||
.into_raw(), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_readFS( | ||
kirillt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
mut env: JNIEnv<'_>, | ||
_class: JClass, | ||
file_storage_ptr: jlong, | ||
) -> jobject { | ||
let data: BTreeMap<String, String> = | ||
FileStorage::from_jlong(file_storage_ptr) | ||
.read_fs() | ||
.expect("not able to read data"); | ||
|
||
// Create a new LinkedHashMap object | ||
let linked_hash_map_class = | ||
env.find_class("java/util/LinkedHashMap").unwrap(); | ||
let linked_hash_map = env | ||
.new_object(linked_hash_map_class, "()V", &[]) | ||
.unwrap(); | ||
|
||
// Get the put method ID | ||
let put_method_id = env | ||
.get_method_id( | ||
"java/util/LinkedHashMap", | ||
"put", | ||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", | ||
) | ||
.unwrap(); | ||
|
||
// Insert each key-value pair from the BTreeMap into the LinkedHashMap | ||
for (key, value) in data { | ||
let j_key = env.new_string(key).unwrap(); | ||
let j_value = env.new_string(value).unwrap(); | ||
let j_key = JValue::from(&j_key).as_jni(); | ||
let j_value = JValue::from(&j_value).as_jni(); | ||
unsafe { | ||
env.call_method_unchecked( | ||
&linked_hash_map, | ||
put_method_id, | ||
ReturnType::Object, | ||
&[j_key, j_value], | ||
) | ||
.unwrap() | ||
}; | ||
} | ||
|
||
// Return the LinkedHashMap as a raw pointer | ||
linked_hash_map.as_raw() | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_writeFS( | ||
env: JNIEnv, | ||
_class: JClass, | ||
file_storage_ptr: jlong, | ||
) -> jstring { | ||
match FileStorage::from_jlong(file_storage_ptr).write_fs() { | ||
Ok(()) => env.new_string("").unwrap().into_raw(), | ||
Err(err) => env | ||
.new_string(err.to_string()) | ||
.unwrap() | ||
.into_raw(), | ||
} | ||
} | ||
|
||
#[allow(clippy::suspicious_doc_comments)] | ||
///! Safety: The FileStorage instance is dropped after this call | ||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_erase( | ||
env: JNIEnv, | ||
_class: JClass, | ||
file_storage_ptr: jlong, | ||
) -> jstring { | ||
let file_storage = unsafe { | ||
Box::from_raw(file_storage_ptr as *mut FileStorage<String, String>) | ||
}; | ||
match file_storage.erase() { | ||
Ok(()) => env.new_string("").unwrap().into_raw(), | ||
Err(err) => env | ||
.new_string(err.to_string()) | ||
.unwrap() | ||
.into_raw(), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "system" fn Java_FileStorage_merge( | ||
env: JNIEnv, | ||
_class: JClass, | ||
file_storage_ptr: jlong, | ||
other_file_storage_ptr: jlong, | ||
) -> jstring { | ||
match FileStorage::from_jlong(file_storage_ptr) | ||
.merge_from(FileStorage::from_jlong(other_file_storage_ptr)) | ||
{ | ||
Ok(()) => env.new_string("").unwrap().into_raw(), | ||
Err(err) => env | ||
.new_string(err.to_string()) | ||
.unwrap() | ||
.into_raw(), | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.jar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also git ignore
*.h
files