16
16
17
17
//! Provides native methods for Java TestKit support.
18
18
19
- use self :: time_provider:: JavaTimeProvider ;
19
+ use std:: { panic, sync:: Arc } ;
20
+
20
21
use exonum:: {
21
- blockchain:: Block ,
22
+ blockchain:: { Block , InstanceCollection , InstanceConfig } ,
22
23
crypto:: { PublicKey , SecretKey } ,
23
24
helpers:: ValidatorId ,
24
- messages:: { RawTransaction , Signed } ,
25
+ merkledb:: BinaryValue ,
26
+ runtime:: InstanceSpec ,
25
27
} ;
26
- use exonum_merkledb:: BinaryValue ;
27
28
use exonum_testkit:: { TestKit , TestKitBuilder } ;
28
- use exonum_time:: { time_provider:: TimeProvider , TimeService } ;
29
- use handle:: { cast_handle, drop_handle, to_handle, Handle } ;
29
+ use exonum_time:: { time_provider:: TimeProvider , TimeServiceFactory } ;
30
30
use jni:: {
31
31
objects:: { JObject , JValue } ,
32
32
sys:: { jboolean, jbyteArray, jobjectArray, jshort} ,
33
33
Executor , JNIEnv ,
34
34
} ;
35
- use proxy :: ServiceProxy ;
36
- use std :: { panic , sync :: Arc } ;
35
+
36
+ use handle :: { cast_handle , drop_handle , to_handle , Handle } ;
37
37
use storage:: View ;
38
- use utils:: { unwrap_exc_or, unwrap_exc_or_default} ;
38
+ use utils:: { convert_to_string, unwrap_exc_or, unwrap_exc_or_default} ;
39
+ use { JavaRuntimeProxy , JniError , JniResult } ;
40
+
41
+ use self :: time_provider:: JavaTimeProvider ;
39
42
40
43
mod time_provider;
41
44
42
45
const KEYPAIR_CLASS : & str = "com/exonum/binding/common/crypto/KeyPair" ;
43
46
const KEYPAIR_CTOR_SIGNATURE : & str = "([B[B)Lcom/exonum/binding/common/crypto/KeyPair;" ;
44
47
const EMULATED_NODE_CLASS : & str = "com/exonum/binding/testkit/EmulatedNode" ;
45
48
const EMULATED_NODE_CTOR_SIGNATURE : & str = "(ILcom/exonum/binding/common/crypto/KeyPair;)V" ;
49
+ const SERVICE_SPECS_FIELD_TYPE : & str = "[Lcom/exonum/binding/testkit/ServiceSpec;" ;
50
+ const TIME_PROVIDER_FIELD_TYPE : & str = "Lcom/exonum/binding/testkit/TimeProviderAdapter;" ;
46
51
47
52
/// Creates TestKit instance with specified services and wires public API handlers.
48
53
/// The caller is responsible for properly destroying TestKit instance and freeing
@@ -54,7 +59,8 @@ pub extern "system" fn Java_com_exonum_binding_testkit_TestKit_nativeCreateTestK
54
59
services : jobjectArray ,
55
60
auditor : jboolean ,
56
61
validator_count : jshort ,
57
- time_provider : JObject ,
62
+ time_service_spec : JObject ,
63
+ runtime_adapter : JObject ,
58
64
) -> Handle {
59
65
let res = panic:: catch_unwind ( || {
60
66
let mut builder = if auditor == jni:: sys:: JNI_TRUE {
@@ -65,20 +71,18 @@ pub extern "system" fn Java_com_exonum_binding_testkit_TestKit_nativeCreateTestK
65
71
builder = builder. with_validators ( validator_count as _ ) ;
66
72
let builder = {
67
73
let executor = Executor :: new ( Arc :: new ( env. get_java_vm ( ) ?) ) ;
68
- let num_services = env. get_array_length ( services) ?;
69
- for i in 0 ..num_services {
70
- let service = env. get_object_array_element ( services, i) ?;
71
- let global_ref = env. new_global_ref ( service) ?;
72
- let service = ServiceProxy :: from_global_ref ( executor. clone ( ) , global_ref) ;
73
- builder = builder. with_service ( service) ;
74
- }
75
74
76
- // Handle Time Service
77
- if !time_provider. is_null ( ) {
78
- let provider = JavaTimeProvider :: new ( executor. clone ( ) , time_provider) ;
79
- builder = builder. with_service ( TimeService :: with_provider (
80
- Box :: new ( provider) as Box < dyn TimeProvider >
81
- ) ) ;
75
+ let runtime =
76
+ JavaRuntimeProxy :: new ( executor. clone ( ) , env. new_global_ref ( runtime_adapter) ?) ;
77
+ builder = builder
78
+ . with_additional_runtime ( runtime)
79
+ // TODO: Rewrite with protobuf: ECR-3689
80
+ . with_instances ( instance_configs_from_java_array ( & env, services) ?) ;
81
+
82
+ if let Some ( instance) =
83
+ time_service_instance_from_java ( & env, executor. clone ( ) , time_service_spec) ?
84
+ {
85
+ builder = builder. with_rust_service ( instance) ;
82
86
}
83
87
84
88
builder
@@ -156,9 +160,7 @@ pub extern "system" fn Java_com_exonum_binding_testkit_TestKit_nativeCreateBlock
156
160
env. auto_local ( env. get_object_array_element ( transactions, i as _ ) ?) ;
157
161
let serialized_tx: jbyteArray = serialized_tx_object. as_obj ( ) . into_inner ( ) ;
158
162
let serialized_tx = env. convert_byte_array ( serialized_tx) ?;
159
- let transaction: Signed < RawTransaction > =
160
- BinaryValue :: from_bytes ( serialized_tx. into ( ) ) . unwrap ( ) ;
161
- raw_transactions. push ( transaction) ;
163
+ raw_transactions. push ( BinaryValue :: from_bytes ( serialized_tx. into ( ) ) . unwrap ( ) ) ;
162
164
}
163
165
let block = testkit
164
166
. create_block_with_transactions ( raw_transactions. into_iter ( ) )
@@ -202,7 +204,7 @@ fn serialize_block(env: &JNIEnv, block: Block) -> jni::errors::Result<jbyteArray
202
204
203
205
fn create_java_keypair < ' a > (
204
206
env : & ' a JNIEnv ,
205
- keypair : ( & PublicKey , & SecretKey ) ,
207
+ keypair : ( PublicKey , SecretKey ) ,
206
208
) -> jni:: errors:: Result < JValue < ' a > > {
207
209
let public_key_byte_array: JObject = env. byte_array_from_slice ( & keypair. 0 [ ..] ) ?. into ( ) ;
208
210
let secret_key_byte_array: JObject = env. byte_array_from_slice ( & keypair. 1 [ ..] ) ?. into ( ) ;
@@ -213,3 +215,132 @@ fn create_java_keypair<'a>(
213
215
& [ secret_key_byte_array. into ( ) , public_key_byte_array. into ( ) ] ,
214
216
)
215
217
}
218
+
219
+ // Converts Java array of `TestKitServiceInstances` to vector of `InstanceConfig`.
220
+ //
221
+ // `TestKitServiceInstances` representation:
222
+ // String artifactId;
223
+ // byte[] deployArguments;
224
+ // ServiceSpec[] serviceSpecs;
225
+ fn instance_configs_from_java_array (
226
+ env : & JNIEnv ,
227
+ service_artifact_specs : jobjectArray ,
228
+ ) -> JniResult < Vec < InstanceConfig > > {
229
+ let mut instance_configs = vec ! [ ] ;
230
+ let num_artifacts = env. get_array_length ( service_artifact_specs) ?;
231
+ for i in 0 ..num_artifacts {
232
+ env. with_local_frame ( 8 , || {
233
+ let artifact_spec_obj = env. get_object_array_element ( service_artifact_specs, i) ?;
234
+
235
+ let artifact_id = get_field_as_string ( env, artifact_spec_obj, "artifactId" ) ?;
236
+ let deploy_args: jbyteArray = env
237
+ . get_field ( artifact_spec_obj, "deployArguments" , "[B" ) ?
238
+ . l ( ) ?
239
+ . into_inner ( ) ;
240
+ let deploy_args = env. convert_byte_array ( deploy_args) ?;
241
+ let service_specs_obj: jobjectArray = env
242
+ . get_field ( artifact_spec_obj, "serviceSpecs" , SERVICE_SPECS_FIELD_TYPE ) ?
243
+ . l ( ) ?
244
+ . into_inner ( ) ;
245
+ // TODO: Avoid deploy arguments duplication after ECR-3690
246
+ let configs = parse_service_specs ( env, service_specs_obj, artifact_id, deploy_args) ?;
247
+ instance_configs. extend ( configs) ;
248
+
249
+ Ok ( JObject :: null ( ) )
250
+ } ) ?;
251
+ }
252
+ Ok ( instance_configs)
253
+ }
254
+
255
+ // Converts Java array of `ServiceSpec` instances into vector of `InstanceConfig` for specific artifact.
256
+ fn parse_service_specs (
257
+ env : & JNIEnv ,
258
+ specs_array : jobjectArray ,
259
+ artifact_id : String ,
260
+ deploy_args : Vec < u8 > ,
261
+ ) -> JniResult < Vec < InstanceConfig > > {
262
+ let num_specs = env. get_array_length ( specs_array) ?;
263
+
264
+ let mut instance_configs = vec ! [ ] ;
265
+ for i in 0 ..num_specs {
266
+ env. with_local_frame ( 8 , || {
267
+ let service_spec = env. get_object_array_element ( specs_array, i) ?;
268
+ let ( spec, config) = parse_instance_spec ( & env, service_spec, & artifact_id) ?;
269
+ let cfg = InstanceConfig :: new ( spec, Some ( deploy_args. to_bytes ( ) ) , config) ;
270
+ instance_configs. push ( cfg) ;
271
+
272
+ Ok ( JObject :: null ( ) )
273
+ } ) ?;
274
+ }
275
+
276
+ Ok ( instance_configs)
277
+ }
278
+
279
+ // Parses the `ServiceSpec` instance.
280
+ //
281
+ // `ServiceSpec` representation:
282
+ // String serviceName;
283
+ // int serviceId;
284
+ // byte[] configuration;
285
+ fn parse_instance_spec (
286
+ env : & JNIEnv ,
287
+ service_spec_obj : JObject ,
288
+ artifact_id : impl AsRef < str > ,
289
+ ) -> JniResult < ( InstanceSpec , Vec < u8 > ) > {
290
+ let ( service_id, service_name) = get_service_id_and_name ( env, service_spec_obj) ?;
291
+ let config_params: jbyteArray = env
292
+ . get_field ( service_spec_obj, "configuration" , "[B" ) ?
293
+ . l ( ) ?
294
+ . into_inner ( ) ;
295
+ let config = env. convert_byte_array ( config_params) ?;
296
+ let spec = InstanceSpec :: new ( service_id, service_name, artifact_id) . map_err ( |err| {
297
+ JniError :: from ( format ! (
298
+ "Unable to create instance specification for the service with id {}: {}" ,
299
+ service_id, err
300
+ ) )
301
+ } ) ?;
302
+
303
+ Ok ( ( spec, config) )
304
+ }
305
+
306
+ // Creates `InstanceCollection` from `TimeServiceSpec` object.
307
+ //
308
+ // `TimeServiceSpec`
309
+ // TimeProviderAdapter timeProvider;
310
+ // String serviceName;
311
+ // int serviceId;
312
+ fn time_service_instance_from_java (
313
+ env : & JNIEnv ,
314
+ executor : Executor ,
315
+ time_service_spec : JObject ,
316
+ ) -> JniResult < Option < InstanceCollection > > {
317
+ if time_service_spec. is_null ( ) {
318
+ return Ok ( None ) ;
319
+ }
320
+
321
+ let ( service_id, service_name) = get_service_id_and_name ( env, time_service_spec) ?;
322
+ let time_provider = env
323
+ . get_field ( time_service_spec, "timeProvider" , TIME_PROVIDER_FIELD_TYPE ) ?
324
+ . l ( ) ?;
325
+
326
+ let provider = JavaTimeProvider :: new ( executor. clone ( ) , time_provider) ;
327
+ let factory = TimeServiceFactory :: with_provider ( Arc :: new ( provider) as Arc < dyn TimeProvider > ) ;
328
+ let instance = InstanceCollection :: new ( factory) . with_instance ( service_id, service_name, ( ) ) ;
329
+
330
+ Ok ( Some ( instance) )
331
+ }
332
+
333
+ // Returns id and name value from corresponding instances of Java objects.
334
+ fn get_service_id_and_name ( env : & JNIEnv , service_obj : JObject ) -> JniResult < ( u32 , String ) > {
335
+ let service_id = env. get_field ( service_obj, "serviceId" , "I" ) ?. i ( ) ? as u32 ;
336
+ let service_name = get_field_as_string ( env, service_obj, "serviceName" ) ?;
337
+ Ok ( ( service_id, service_name) )
338
+ }
339
+
340
+ // Returns String value of instance's field.
341
+ fn get_field_as_string ( env : & JNIEnv , obj : JObject , field_name : & str ) -> JniResult < String > {
342
+ convert_to_string (
343
+ env,
344
+ env. get_field ( obj, field_name, "Ljava/lang/String;" ) ?. l ( ) ?,
345
+ )
346
+ }
0 commit comments