@@ -6,187 +6,146 @@ use crate::server;
66use crate :: server:: start;
77use crate :: { log_debug, log_error, log_info} ;
88use jni:: errors:: Result as JniResult ;
9+ use jni:: errors:: ThrowRuntimeExAndDefault ;
10+ use jni:: jni_sig;
11+ use jni:: jni_str;
12+ use jni:: objects:: { JClass , JObject , JString , JValue } ;
913use jni:: sys:: { jint, jstring} ;
10- use jni:: {
11- objects:: GlobalRef , objects:: JClass , objects:: JMethodID , objects:: JObject , objects:: JString ,
12- objects:: JValue , objects:: JValueGen , JNIEnv , JavaVM ,
13- } ;
14- use lazy_static:: lazy_static;
15- use std:: error:: Error ;
16- use std:: sync:: { Arc , Mutex , Once } ;
17- use std:: thread;
14+ use jni:: { Env , EnvUnowned } ;
1815use std:: time:: Duration ;
1916use veilid_core:: veilid_core_setup_android;
2017
21- trait IntoJObject {
22- fn into_jobject ( & self ) -> JObject ;
23- }
24-
25- impl IntoJObject for GlobalRef {
26- fn into_jobject ( & self ) -> JObject {
27- unsafe { JObject :: from_raw ( self . as_raw ( ) ) }
28- }
29- }
30-
3118#[ no_mangle]
3219#[ allow( non_snake_case) ]
3320pub extern "system" fn Java_net_opendasharchive_openarchive_services_snowbird_SnowbirdBridge_initializeRustService (
34- env : JNIEnv ,
35- class : JClass ,
21+ mut env : EnvUnowned ,
22+ _class : JClass ,
3623) {
37- // match jni_globals::setup_android(env, class) {
38- // Ok(_) => log_debug!(TAG, "Rust service initialized successfully"),
39- // Err(e) => log_error!(TAG, "Failed to initialize Rust service: {:?}", e),
40- // }
24+ env. with_env ( |_env| -> JniResult < ( ) > {
25+ // match jni_globals::setup_android(env, class) {
26+ // Ok(_) => log_debug!(TAG, "Rust service initialized successfully"),
27+ // Err(e) => log_error!(TAG, "Failed to initialize Rust service: {:?}", e),
28+ // }
4129
42- log_info ! ( TAG , "SnowbirdBridge initialized" ) ;
30+ log_info ! ( TAG , "SnowbirdBridge initialized" ) ;
31+ Ok ( ( ) )
32+ } )
33+ . resolve :: < ThrowRuntimeExAndDefault > ( ) ;
4334}
4435
4536#[ no_mangle]
4637#[ allow( non_snake_case) ]
4738pub extern "system" fn Java_net_opendasharchive_openarchive_services_snowbird_SnowbirdBridge_startServer (
48- mut env : JNIEnv ,
39+ mut env : EnvUnowned ,
4940 clazz : JClass ,
5041 context : JObject ,
5142 backend_base_directory : JString ,
5243 server_socket_path : JString ,
5344) -> jstring {
54- let env_ptr = env. get_native_interface ( ) ;
55-
5645 log_debug ! ( TAG , "Bridge: starting" ) ;
5746
58- match setup_jni_environments ( & mut env, context, clazz) {
59- Ok ( _) => {
47+ // Initialize JNI globals, smoke-test the Java callback, and read Java args while
48+ // EnvUnowned is still available. veilid_core_setup_android consumes env/context.
49+ let ( backend_base_directory, server_socket_path, output) = env
50+ . with_env ( |env| -> JniResult < ( String , String , jstring ) > {
51+ jni_globals:: init_jni ( env, clazz) . map_err ( |e| {
52+ jni:: errors:: Error :: ParseFailed ( format ! ( "Failed to initialize JNI globals: {e}" ) )
53+ } ) ?;
54+ jni_smoke_test ( env) ?;
55+
56+ let backend_base_directory = backend_base_directory. try_to_string ( env) ?;
57+ let server_socket_path = server_socket_path. try_to_string ( env) ?;
58+ let output = JString :: from_str (
59+ env,
60+ format ! ( "Server started on Unix socket: {server_socket_path}" ) ,
61+ ) ?
62+ . into_raw ( ) ;
63+
6064 log_debug ! ( TAG , "JNI stuff successful" ) ;
61- }
62- Err ( e) => {
63- log_error ! ( TAG , "Error doing JNI stuff: {:?}" , e) ;
64- }
65- }
6665
67- let backend_base_directory: String = env
68- . get_string ( & backend_base_directory)
69- . expect ( "Couldn't get socket path string" )
70- . into ( ) ;
66+ Ok ( ( backend_base_directory, server_socket_path, output) )
67+ } )
68+ . resolve :: < ThrowRuntimeExAndDefault > ( ) ;
7169
72- let server_socket_path : String = env
73- . get_string ( & server_socket_path )
74- . expect ( "Couldn't get socket path string" )
75- . into ( ) ;
70+ // resolve() throws to Java and returns null on failure; do not start Veilid or the server.
71+ if output . is_null ( ) {
72+ return output ;
73+ }
7674
77- let backend_base_directory_clone = backend_base_directory. clone ( ) ;
78- let server_socket_path_clone = server_socket_path. clone ( ) ;
75+ veilid_core_setup_android ( env, context) ;
7976
8077 std:: thread:: spawn ( move || {
8178 let runtime = tokio:: runtime:: Runtime :: new ( ) . unwrap ( ) ;
8279 runtime. block_on ( async {
83- start ( & backend_base_directory_clone , & server_socket_path_clone )
80+ start ( & backend_base_directory , & server_socket_path )
8481 . await
8582 . unwrap ( ) ;
8683 } ) ;
8784 } ) ;
8885
8986 log_debug ! ( TAG , "Bridge startup complete." ) ;
9087
91- let output = env
92- . new_string ( format ! (
93- "Server started on Unix socket: {}" ,
94- server_socket_path
95- ) )
96- . expect ( "Couldn't create java string!" ) ;
97-
98- output. into_raw ( )
88+ output
9989}
10090
10191#[ no_mangle]
10292#[ allow( non_snake_case) ]
10393pub extern "system" fn Java_net_opendasharchive_openarchive_services_snowbird_SnowbirdBridge_stopServer (
104- mut env : JNIEnv ,
94+ mut env : EnvUnowned ,
10595 _clazz : JClass ,
106- ctx : JObject ,
96+ _ctx : JObject ,
10797) -> jstring {
10898 log_debug ! ( TAG , "Bridge: stopping server" ) ;
10999
110- // Create a runtime to handle async operations
111- let runtime = tokio:: runtime:: Runtime :: new ( ) . unwrap ( ) ;
112-
113- // Stop the backend server and clean up Veilid API
114- let stop_result = runtime. block_on ( async {
115- // First stop the backend
116- match server:: stop ( ) . await {
117- Ok ( _) => {
118- log_info ! ( TAG , "Backend stopped successfully" ) ;
119-
120- // Get the backend to access Veilid API
121- if let Ok ( backend) = server:: get_backend ( ) . await {
122- // Shutdown Veilid API
123- if let Some ( veilid_api) = backend. get_veilid_api ( ) . await {
124- veilid_api. shutdown ( ) . await ;
125- log_info ! ( TAG , "Veilid API shut down successfully" ) ;
100+ let stop_ok = env
101+ . with_env ( |_env| -> JniResult < bool > {
102+ // Create a runtime to handle async operations
103+ let runtime = tokio:: runtime:: Runtime :: new ( ) . unwrap ( ) ;
104+
105+ // Stop the backend server and clean up Veilid API
106+ runtime. block_on ( async {
107+ // First stop the backend
108+ match server:: stop ( ) . await {
109+ Ok ( _) => {
110+ log_info ! ( TAG , "Backend stopped successfully" ) ;
111+
112+ // Get the backend to access Veilid API
113+ if let Ok ( backend) = server:: get_backend ( ) . await {
114+ // Shutdown Veilid API
115+ if let Some ( veilid_api) = backend. get_veilid_api ( ) . await {
116+ veilid_api. shutdown ( ) . await ;
117+ log_info ! ( TAG , "Veilid API shut down successfully" ) ;
118+ }
119+ }
120+
121+ // Add a small delay to ensure tasks complete
122+ tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
123+ Ok ( true )
124+ }
125+ Err ( e) => {
126+ log_error ! ( TAG , "Error stopping server: {:?}" , e) ;
127+ Ok ( false )
126128 }
127129 }
128-
129- // Add a small delay to ensure tasks complete
130- tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
131-
132- Ok ( ( ) )
133- }
134- Err ( e) => {
135- log_error ! ( TAG , "Error stopping server: {:?}" , e) ;
136- Err ( e)
137- }
138- }
139- } ) ;
130+ } )
131+ } )
132+ . resolve :: < ThrowRuntimeExAndDefault > ( ) ;
140133
141134 // Create response string based on result
142- let response = match stop_result {
143- Ok ( _) => "Server stopped successfully" ,
144- Err ( _) => "Error stopping server" ,
135+ let response = if stop_ok {
136+ "Server stopped successfully"
137+ } else {
138+ "Error stopping server"
145139 } ;
146140
147- let output = env
148- . new_string ( response)
149- . expect ( "Couldn't create java string!" ) ;
150-
151- output. into_raw ( )
152- }
153-
154- fn with_env < F , R > ( env : & mut JNIEnv , f : F ) -> Result < R , Box < dyn Error > >
155- where
156- F : FnOnce ( JNIEnv ) -> Result < R , Box < dyn Error > > ,
157- {
158- let env_ptr = env. get_native_interface ( ) ;
159- let new_env = unsafe { JNIEnv :: from_raw ( env_ptr) . unwrap ( ) } ;
160- f ( new_env)
161- }
162-
163- fn setup_jni_environments (
164- env : & mut JNIEnv ,
165- context : JObject ,
166- clazz : JClass ,
167- ) -> Result < ( ) , Box < dyn Error > > {
168- with_env ( env, |env| Ok ( jni_globals:: init_jni ( & env, clazz) ) ) ;
169-
170- let global_context = env. new_global_ref ( context) ?;
171-
172- // Use a new JNIEnv for jni_smoke_test
173- with_env ( env, |env| {
174- jni_smoke_test ( env, global_context. into_jobject ( ) )
175- } ) ?;
176-
177- // Use another new JNIEnv for veilid_core_setup_android
178- with_env ( env, |env| {
179- veilid_core_setup_android ( env, global_context. into_jobject ( ) ) ;
180- Ok ( ( ) )
181- } ) ?;
182-
183- Ok ( ( ) )
141+ env. with_env ( |env| -> JniResult < jstring > {
142+ let output = JString :: from_str ( env, response) ?;
143+ Ok ( output. into_raw ( ) )
144+ } )
145+ . resolve :: < ThrowRuntimeExAndDefault > ( )
184146}
185147
186- fn jni_smoke_test < ' local > (
187- mut env : JNIEnv < ' local > ,
188- context : JObject < ' local > ,
189- ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
148+ fn jni_smoke_test ( env : & mut Env ) -> JniResult < ( ) > {
190149 let class_name = "net/opendasharchive/openarchive/services/snowbird/SnowbirdBridge" ;
191150 let method_name = "updateStatusFromRust" ;
192151 let method_signature = "(ILjava/lang/String;)V" ;
@@ -196,14 +155,13 @@ fn jni_smoke_test<'local>(
196155
197156 // Create a JValue for the String parameter (can be null)
198157 let error_message = env. new_string ( "Test error message" ) ?;
199- let error_message_jvalue = JValue :: Object ( & error_message) ;
200158
201159 // Call the static method
202160 env. call_static_method (
203- class_name ,
204- method_name ,
205- method_signature ,
206- & [ JValue :: Int ( status_code) , error_message_jvalue ] ,
161+ jni_str ! ( "net/opendasharchive/openarchive/services/snowbird/SnowbirdBridge" ) ,
162+ jni_str ! ( "updateStatusFromRust" ) ,
163+ jni_sig ! ( "(ILjava/lang/String;)V" ) ,
164+ & [ JValue :: Int ( status_code) , JValue :: Object ( & error_message ) ] ,
207165 ) ?;
208166
209167 Ok ( ( ) )
0 commit comments