1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5 #include <aws/mqtt/v5/mqtt5_client.h>
6
7 #include <aws/http/proxy.h>
8 #include <aws/io/event_loop.h>
9 #include <aws/io/socket.h>
10 #include <aws/io/tls_channel_handler.h>
11
12 #include <crt.h>
13 #include <http_request_utils.h>
14 #include <java_class_ids.h>
15 #include <jni.h>
16 #include <mqtt5_client_jni.h>
17 #include <mqtt5_packets.h>
18 #include <mqtt5_utils.h>
19
20 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
21 #if UINTPTR_MAX == 0xffffffff
22 # if defined(_MSC_VER)
23 # pragma warning(push)
24 # pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
25 # else
26 # pragma GCC diagnostic push
27 # pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
28 # pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
29 # endif
30 #endif
31
32 /*******************************************************************************
33 * CLIENT ONLY STRUCTS
34 ******************************************************************************/
35
36 struct aws_mqtt5_client_publish_return_data {
37 struct aws_mqtt5_client_java_jni *java_client;
38 jobject jni_publish_future;
39 };
40
41 struct aws_mqtt5_client_subscribe_return_data {
42 struct aws_mqtt5_client_java_jni *java_client;
43 jobject jni_subscribe_future;
44 };
45
46 struct aws_mqtt5_client_unsubscribe_return_data {
47 struct aws_mqtt5_client_java_jni *java_client;
48 jobject jni_unsubscribe_future;
49 };
50
51 struct aws_http_proxy_options_java_jni {
52 struct aws_http_proxy_options options;
53
54 struct aws_byte_buf proxy_host_buf;
55 struct aws_byte_cursor proxy_host_cursor;
56 struct aws_byte_buf authorization_username_buf;
57 struct aws_byte_cursor authorization_username_cursor;
58 struct aws_byte_buf authorization_password_buf;
59 struct aws_byte_cursor authorization_password_cursor;
60 };
61
62 /*******************************************************************************
63 * HELPER FUNCTION (LOGGING)
64 ******************************************************************************/
65
s_aws_mqtt5_client_log_and_throw_exception(JNIEnv * env,const char * message,int error_code)66 static void s_aws_mqtt5_client_log_and_throw_exception(JNIEnv *env, const char *message, int error_code) {
67 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "%s - error code: %i", message, error_code);
68 // raise error to update the "last_error_code"
69 aws_raise_error(error_code);
70 aws_jni_throw_runtime_exception(env, "%s - error code: %i", message, error_code);
71 }
72
73 /*******************************************************************************
74 * HTTP PROXY FUNCTIONS
75 ******************************************************************************/
76
s_aws_mqtt5_http_proxy_options_java_destroy(JNIEnv * env,struct aws_allocator * allocator,struct aws_http_proxy_options_java_jni * http_options)77 static void s_aws_mqtt5_http_proxy_options_java_destroy(
78 JNIEnv *env,
79 struct aws_allocator *allocator,
80 struct aws_http_proxy_options_java_jni *http_options) {
81 (void)env;
82
83 if (!http_options) {
84 return;
85 }
86 AWS_LOGF_DEBUG(AWS_LS_MQTT5_CLIENT, "id=%p: Destroying JavaHttpProxyOptions", (void *)http_options);
87
88 if (aws_byte_buf_is_valid(&http_options->proxy_host_buf)) {
89 aws_byte_buf_clean_up(&http_options->proxy_host_buf);
90 }
91 if (aws_byte_buf_is_valid(&http_options->authorization_username_buf)) {
92 aws_byte_buf_clean_up(&http_options->authorization_username_buf);
93 }
94 if (aws_byte_buf_is_valid(&http_options->authorization_password_buf)) {
95 aws_byte_buf_clean_up(&http_options->authorization_password_buf);
96 }
97
98 /* Frees all allocated memory */
99 aws_mem_release(allocator, http_options);
100 }
101
s_aws_mqtt5_http_proxy_options_create_from_java(JNIEnv * env,struct aws_allocator * allocator,struct aws_mqtt5_client_java_jni * java_client,jobject java_http_proxy_options)102 static struct aws_http_proxy_options_java_jni *s_aws_mqtt5_http_proxy_options_create_from_java(
103 JNIEnv *env,
104 struct aws_allocator *allocator,
105 struct aws_mqtt5_client_java_jni *java_client,
106 jobject java_http_proxy_options) {
107 /* Cannot fail */
108 struct aws_http_proxy_options_java_jni *http_options =
109 aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_options_java_jni));
110 AWS_LOGF_DEBUG(
111 AWS_LS_MQTT5_CLIENT, "JavaHttpProxyOptions=%p: Created new JavaHttpProxyOptions", (void *)http_options);
112
113 jobject jni_proxy_connection_type = (*env)->CallObjectMethod(
114 env, java_http_proxy_options, http_proxy_options_properties.proxy_get_connection_type_id);
115 if (aws_jni_check_and_clear_exception(env)) {
116 goto on_error;
117 }
118 if (jni_proxy_connection_type) {
119 jint jni_proxy_connection_type_value = (*env)->CallIntMethod(
120 env, jni_proxy_connection_type, http_proxy_connection_type_properties.proxy_get_value_id);
121 if (aws_jni_check_and_clear_exception(env)) {
122 goto on_error;
123 }
124 if (jni_proxy_connection_type_value) {
125 int32_t jni_proxy_connection_type_value_check = (int32_t)jni_proxy_connection_type_value;
126 if (jni_proxy_connection_type_value_check < 0) {
127 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "HTTP Proxy Options connection type is less than 0");
128 goto on_error;
129 } else if (jni_proxy_connection_type_value_check > AWS_HPCT_HTTP_TUNNEL) { /* The (current) maximum enum */
130 /* value */
131 AWS_LOGF_ERROR(
132 AWS_LS_MQTT5_CLIENT, "HTTP Proxy Options connection type is more than maximum allowed value");
133 goto on_error;
134 } else {
135 http_options->options.connection_type =
136 (enum aws_http_proxy_connection_type)jni_proxy_connection_type_value;
137 }
138 }
139 }
140
141 jstring jni_proxy_host = (jstring)(*env)->CallObjectMethod(
142 env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_host_id);
143 if (aws_jni_check_and_clear_exception(env)) {
144 goto on_error;
145 }
146 if (jni_proxy_host) {
147 // Get the data, copy it, and then release the JNI stuff
148 struct aws_byte_cursor tmp_cursor = aws_jni_byte_cursor_from_jstring_acquire(env, jni_proxy_host);
149 aws_byte_buf_init_copy_from_cursor(&http_options->proxy_host_buf, aws_jni_get_allocator(), tmp_cursor);
150 http_options->proxy_host_cursor = aws_byte_cursor_from_buf(&http_options->proxy_host_buf);
151 aws_jni_byte_cursor_from_jstring_release(env, jni_proxy_host, tmp_cursor);
152
153 http_options->options.host = http_options->proxy_host_cursor;
154 }
155
156 jint jni_proxy_port =
157 (*env)->CallIntMethod(env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_port_id);
158 if (aws_jni_check_and_clear_exception(env)) {
159 goto on_error;
160 }
161 if (jni_proxy_port) {
162 http_options->options.port = (uint32_t)jni_proxy_port;
163 }
164
165 jobject jni_proxy_tls_context = (*env)->CallObjectMethod(
166 env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_tls_context_id);
167 if (aws_jni_check_and_clear_exception(env)) {
168 goto on_error;
169 }
170 if (jni_proxy_tls_context) {
171 jlong jni_proxy_tls_context_long =
172 (*env)->CallLongMethod(env, jni_proxy_tls_context, crt_resource_properties.get_native_handle_method_id);
173
174 struct aws_tls_ctx *tls_ctx = (struct aws_tls_ctx *)jni_proxy_tls_context_long;
175 if (tls_ctx) {
176 aws_tls_connection_options_init_from_ctx(&java_client->http_proxy_tls_options, tls_ctx);
177 aws_tls_connection_options_set_server_name(
178 &java_client->http_proxy_tls_options, allocator, &http_options->options.host);
179 http_options->options.tls_options = &java_client->http_proxy_tls_options;
180 }
181 }
182
183 jobject jni_proxy_authorization_type = (*env)->CallObjectMethod(
184 env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_authorization_type_id);
185 if (aws_jni_check_and_clear_exception(env)) {
186 goto on_error;
187 }
188 if (jni_proxy_authorization_type) {
189 jint jni_proxy_authorization_type_value = (*env)->CallIntMethod(
190 env, jni_proxy_authorization_type, http_proxy_connection_type_properties.proxy_get_value_id);
191 if (aws_jni_check_and_clear_exception(env)) {
192 goto on_error;
193 }
194 http_options->options.auth_type = (enum aws_http_proxy_authentication_type)jni_proxy_authorization_type_value;
195 }
196
197 jstring jni_proxy_authorization_username = (jstring)(*env)->CallObjectMethod(
198 env, java_http_proxy_options, http_proxy_options_properties.proxy_get_authorization_username_id);
199 if (aws_jni_check_and_clear_exception(env)) {
200 goto on_error;
201 }
202 if (jni_proxy_authorization_username) {
203 // Get the data, copy it, and then release the JNI stuff
204 struct aws_byte_cursor tmp_cursor =
205 aws_jni_byte_cursor_from_jstring_acquire(env, jni_proxy_authorization_username);
206 aws_byte_buf_init_copy_from_cursor(
207 &http_options->authorization_username_buf, aws_jni_get_allocator(), tmp_cursor);
208 http_options->authorization_username_cursor =
209 aws_byte_cursor_from_buf(&http_options->authorization_username_buf);
210 aws_jni_byte_cursor_from_jstring_release(env, jni_proxy_authorization_username, tmp_cursor);
211
212 http_options->options.auth_username = http_options->authorization_username_cursor;
213 }
214
215 jstring jni_proxy_authorization_password = (jstring)(*env)->CallObjectMethod(
216 env, java_http_proxy_options, http_proxy_options_properties.proxy_get_authorization_password_id);
217 if (aws_jni_check_and_clear_exception(env)) {
218 goto on_error;
219 }
220 if (jni_proxy_authorization_password) {
221 // Get the data, copy it, and then release the JNI stuff
222 struct aws_byte_cursor tmp_cursor =
223 aws_jni_byte_cursor_from_jstring_acquire(env, jni_proxy_authorization_password);
224 aws_byte_buf_init_copy_from_cursor(
225 &http_options->authorization_password_buf, aws_jni_get_allocator(), tmp_cursor);
226 http_options->authorization_password_cursor =
227 aws_byte_cursor_from_buf(&http_options->authorization_password_buf);
228 aws_jni_byte_cursor_from_jstring_release(env, jni_proxy_authorization_password, tmp_cursor);
229
230 http_options->options.auth_password = http_options->authorization_password_cursor;
231 }
232
233 return http_options;
234
235 on_error:
236 s_aws_mqtt5_http_proxy_options_java_destroy(env, allocator, http_options);
237 return NULL;
238 }
239
240 /*******************************************************************************
241 * HELPER FUNCTIONS
242 ******************************************************************************/
243
aws_mqtt5_client_java_destroy(JNIEnv * env,struct aws_allocator * allocator,struct aws_mqtt5_client_java_jni * java_client)244 static void aws_mqtt5_client_java_destroy(
245 JNIEnv *env,
246 struct aws_allocator *allocator,
247 struct aws_mqtt5_client_java_jni *java_client) {
248 AWS_PRECONDITION(java_client);
249 if (!java_client) {
250 return;
251 }
252
253 AWS_LOGF_DEBUG(AWS_LS_MQTT5_CLIENT, "java_client=%p: Destroying MQTT5 client", (void *)java_client);
254
255 if (java_client->jni_client) {
256 (*env)->DeleteGlobalRef(env, java_client->jni_client);
257 }
258 if (java_client->jni_publish_events) {
259 (*env)->DeleteGlobalRef(env, java_client->jni_publish_events);
260 }
261 if (java_client->jni_lifecycle_events) {
262 (*env)->DeleteGlobalRef(env, java_client->jni_lifecycle_events);
263 }
264
265 aws_tls_connection_options_clean_up(&java_client->tls_options);
266 aws_tls_connection_options_clean_up(&java_client->http_proxy_tls_options);
267
268 /* Frees allocated memory */
269 aws_mem_release(allocator, java_client);
270 }
271
s_complete_future_with_exception(JNIEnv * env,jobject * future,int error_code)272 static void s_complete_future_with_exception(JNIEnv *env, jobject *future, int error_code) {
273 if (!env || !future) {
274 return;
275 }
276
277 jobject crt_exception = aws_jni_new_crt_exception_from_error_code(env, error_code);
278 (*env)->CallBooleanMethod(
279 env, *future, completable_future_properties.complete_exceptionally_method_id, crt_exception);
280 aws_jni_check_and_clear_exception(env);
281 (*env)->DeleteLocalRef(env, crt_exception);
282 }
283
s_aws_count_allocation(const void * pointer,size_t * counter)284 static void s_aws_count_allocation(const void *pointer, size_t *counter) {
285 if (pointer != NULL) {
286 *counter += 1;
287 }
288 }
289
290 static char s_client_string[] = "MQTT5 Client";
291
292 /*******************************************************************************
293 * MQTT5 CALLBACK FUNCTIONS
294 ******************************************************************************/
295
s_aws_mqtt5_client_java_lifecycle_event(const struct aws_mqtt5_client_lifecycle_event * event)296 static void s_aws_mqtt5_client_java_lifecycle_event(const struct aws_mqtt5_client_lifecycle_event *event) {
297
298 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)event->user_data;
299 if (!java_client) {
300 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: invalid client");
301 return;
302 }
303
304 /********** JNI ENV ACQUIRE **********/
305 JavaVM *jvm = java_client->jvm;
306 JNIEnv *env = aws_jni_acquire_thread_env(jvm);
307 if (env == NULL) {
308 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
309 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: could not get env");
310 return;
311 }
312
313 /* Calculate the number of references needed (1 is always needed for the return struct) */
314 size_t references_needed = 1;
315 if (event->connack_data != NULL) {
316 /* A ConnAck packet will need 2 references at minimum */
317 references_needed += 2;
318 /* Optionals */
319 s_aws_count_allocation(event->connack_data->assigned_client_identifier, &references_needed);
320 s_aws_count_allocation(event->connack_data->authentication_data, &references_needed);
321 s_aws_count_allocation(event->connack_data->authentication_method, &references_needed);
322 s_aws_count_allocation(event->connack_data->maximum_packet_size, &references_needed);
323 s_aws_count_allocation(event->connack_data->maximum_qos, &references_needed);
324 s_aws_count_allocation(event->connack_data->reason_string, &references_needed);
325 s_aws_count_allocation(event->connack_data->receive_maximum, &references_needed);
326 s_aws_count_allocation(event->connack_data->response_information, &references_needed);
327 s_aws_count_allocation(event->connack_data->retain_available, &references_needed);
328 s_aws_count_allocation(event->connack_data->server_keep_alive, &references_needed);
329 s_aws_count_allocation(event->connack_data->server_reference, &references_needed);
330 s_aws_count_allocation(event->connack_data->session_expiry_interval, &references_needed);
331 s_aws_count_allocation(event->connack_data->shared_subscriptions_available, &references_needed);
332 s_aws_count_allocation(event->connack_data->subscription_identifiers_available, &references_needed);
333 s_aws_count_allocation(event->connack_data->topic_alias_maximum, &references_needed);
334 s_aws_count_allocation(event->connack_data->wildcard_subscriptions_available, &references_needed);
335 /* Add user properties */
336 references_needed += event->connack_data->user_property_count * 2;
337 references_needed += 2; /* Add 2 for arrays to hold user properties */
338 }
339 if (event->disconnect_data != NULL) {
340 /* A Disconnect packet will need 1 reference at a minimum */
341 references_needed += 1;
342 /* Optionals */
343 s_aws_count_allocation(event->disconnect_data->reason_string, &references_needed);
344 s_aws_count_allocation(event->disconnect_data->server_reference, &references_needed);
345 s_aws_count_allocation(event->disconnect_data->session_expiry_interval_seconds, &references_needed);
346 /* Add user properties */
347 references_needed += event->disconnect_data->user_property_count * 2;
348 references_needed += 2; /* Add 1 for array to hold user properties */
349 }
350 if (event->settings != NULL) {
351 /* Negotiated settings only need 2 references, one for the ClientID and another for the object */
352 references_needed += 2;
353 }
354
355 /* Make a local frame so we can clean memory */
356 jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed);
357 if (local_frame_result != 0) {
358 s_aws_mqtt5_client_log_and_throw_exception(
359 env, "LifecycleEvent: could not push local JNI frame with 14 allocation minimum!", AWS_ERROR_INVALID_STATE);
360 aws_jni_release_thread_env(jvm, env);
361 return;
362 }
363
364 jobject connack_data = NULL;
365 if (event->connack_data != NULL) {
366 connack_data = s_aws_mqtt5_client_create_jni_connack_packet_from_native(env, event->connack_data);
367 if (connack_data == NULL) {
368 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: creating ConnAck packet failed!");
369 goto clean_up;
370 }
371 }
372
373 jobject disconnect_data = NULL;
374 if (event->disconnect_data != NULL) {
375 disconnect_data = s_aws_mqtt5_client_create_jni_disconnect_packet_from_native(env, event->disconnect_data);
376 if (disconnect_data == NULL) {
377 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: creating Disconnect packet failed!");
378 goto clean_up;
379 }
380 }
381
382 jobject negotiated_settings_data = NULL;
383 if (event->settings != NULL) {
384 negotiated_settings_data = s_aws_mqtt5_client_create_jni_negotiated_settings_from_native(env, event->settings);
385 }
386
387 jobject jni_lifecycle_events = java_client->jni_lifecycle_events;
388 if (!jni_lifecycle_events) {
389 s_aws_mqtt5_client_log_and_throw_exception(
390 env, "LifecycleEvent: no lifecycle events found!", AWS_ERROR_INVALID_STATE);
391 goto clean_up;
392 }
393
394 jobject java_lifecycle_return_data;
395
396 switch (event->event_type) {
397 case AWS_MQTT5_CLET_ATTEMPTING_CONNECT:
398
399 /* Make the OnAttemptingConnectReturn struct */
400 java_lifecycle_return_data = (*env)->NewObject(
401 env,
402 mqtt5_on_attempting_connect_return_properties.return_class,
403 mqtt5_on_attempting_connect_return_properties.return_constructor_id);
404 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
405
406 (*env)->CallVoidMethod(
407 env,
408 jni_lifecycle_events,
409 mqtt5_lifecycle_events_properties.lifecycle_attempting_connect_id,
410 java_client->jni_client,
411 java_lifecycle_return_data);
412 break;
413 case AWS_MQTT5_CLET_CONNECTION_SUCCESS:
414
415 /* Make the OnConnectionSuccessReturn struct */
416 java_lifecycle_return_data = (*env)->NewObject(
417 env,
418 mqtt5_on_connection_success_return_properties.return_class,
419 mqtt5_on_connection_success_return_properties.return_constructor_id,
420 connack_data,
421 negotiated_settings_data);
422 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
423
424 /* Set OnConnected BEFORE calling the callback so it is accurate in the callback itself. */
425 (*env)->CallVoidMethod(env, java_client->jni_client, mqtt5_client_properties.client_set_is_connected, true);
426
427 (*env)->CallVoidMethod(
428 env,
429 jni_lifecycle_events,
430 mqtt5_lifecycle_events_properties.lifecycle_connection_success_id,
431 java_client->jni_client,
432 java_lifecycle_return_data);
433 break;
434 case AWS_MQTT5_CLET_CONNECTION_FAILURE: {
435 jint error_code = (jint)event->error_code;
436
437 /* Make the OnConnectionFailureReturn struct */
438 java_lifecycle_return_data = (*env)->NewObject(
439 env,
440 mqtt5_on_connection_failure_return_properties.return_class,
441 mqtt5_on_connection_failure_return_properties.return_constructor_id,
442 error_code,
443 connack_data);
444 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
445
446 (*env)->CallVoidMethod(
447 env,
448 jni_lifecycle_events,
449 mqtt5_lifecycle_events_properties.lifecycle_connection_failure_id,
450 java_client->jni_client,
451 java_lifecycle_return_data);
452 break;
453 }
454 case AWS_MQTT5_CLET_DISCONNECTION: {
455 jint error_code = (jint)event->error_code;
456
457 /* Make the OnDisconnectionReturn struct */
458 java_lifecycle_return_data = (*env)->NewObject(
459 env,
460 mqtt5_on_disconnection_return_properties.return_class,
461 mqtt5_on_disconnection_return_properties.return_constructor_id,
462 error_code,
463 disconnect_data);
464 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
465
466 /* Set OnConnected BEFORE calling the callback so it is accurate in the callback itself. */
467 (*env)->CallVoidMethod(
468 env, java_client->jni_client, mqtt5_client_properties.client_set_is_connected, false);
469
470 (*env)->CallVoidMethod(
471 env,
472 jni_lifecycle_events,
473 mqtt5_lifecycle_events_properties.lifecycle_disconnection_id,
474 java_client->jni_client,
475 java_lifecycle_return_data);
476 break;
477 }
478 case AWS_MQTT5_CLET_STOPPED:
479
480 /* Make the OnStopped struct */
481 java_lifecycle_return_data = (*env)->NewObject(
482 env,
483 mqtt5_on_stopped_return_properties.return_class,
484 mqtt5_on_stopped_return_properties.return_constructor_id);
485 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
486
487 (*env)->CallVoidMethod(
488 env,
489 jni_lifecycle_events,
490 mqtt5_lifecycle_events_properties.lifecycle_stopped_id,
491 java_client->jni_client,
492 java_lifecycle_return_data);
493 break;
494 default:
495 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: unsupported event type: %i", event->event_type);
496 }
497
498 goto clean_up;
499
500 clean_up:
501
502 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
503
504 (*env)->PopLocalFrame(env, NULL);
505 /********** JNI ENV RELEASE **********/
506 aws_jni_release_thread_env(jvm, env);
507 }
508
s_aws_mqtt5_client_java_publish_received(const struct aws_mqtt5_packet_publish_view * publish,void * user_data)509 static void s_aws_mqtt5_client_java_publish_received(
510 const struct aws_mqtt5_packet_publish_view *publish,
511 void *user_data) {
512
513 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)user_data;
514 if (!java_client) {
515 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "publishReceived function: invalid client");
516 return;
517 }
518
519 if (!publish) {
520 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "publishReceived function: invalid publish packet");
521 return;
522 }
523
524 /********** JNI ENV ACQUIRE **********/
525 JavaVM *jvm = java_client->jvm;
526 JNIEnv *env = aws_jni_acquire_thread_env(jvm);
527 if (env == NULL) {
528 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
529 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "publishReceived function: could not get env");
530 return;
531 }
532
533 /* Calculate the number of references needed */
534 size_t references_needed = 0;
535 {
536 /* One reference is needed for the PublishReturn */
537 references_needed += 1;
538
539 /* A Publish packet will need 5 references at minimum */
540 references_needed += 5;
541 /* Optionals */
542 s_aws_count_allocation(publish->content_type, &references_needed);
543 s_aws_count_allocation(publish->correlation_data, &references_needed);
544 s_aws_count_allocation(publish->message_expiry_interval_seconds, &references_needed);
545 s_aws_count_allocation(publish->response_topic, &references_needed);
546 s_aws_count_allocation(publish->topic_alias, &references_needed);
547 s_aws_count_allocation(publish->payload_format, &references_needed);
548 /* Add user properties and subscription identifiers */
549 references_needed += publish->user_property_count * 2;
550 references_needed += 1; /* Add 1 for array to hold user properties */
551 if (publish->subscription_identifier_count > 0) {
552 references_needed += publish->subscription_identifier_count;
553 references_needed += 1; /* Add 1 for array */
554 }
555 }
556
557 /**
558 * Push a new local frame so any local references we make are tied to it. Then we can pop it to free memory.
559 */
560 jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed);
561 if (local_frame_result != 0) {
562 s_aws_mqtt5_client_log_and_throw_exception(
563 env,
564 "publishReceived function: could not push local JNI frame with 12 allocation minimum!",
565 AWS_ERROR_INVALID_STATE);
566 goto clean_up;
567 }
568
569 /* The return result */
570 jobject publish_packet_return_data;
571
572 /* Make the PublishPacket */
573 jobject publish_packet_data = s_aws_mqtt5_client_create_jni_publish_packet_from_native(env, publish);
574 if (publish_packet_data == NULL) {
575 goto clean_up;
576 }
577
578 /* Make the PublishReturn struct that will hold all of the data that is passed to Java */
579 publish_packet_return_data = (*env)->NewObject(
580 env,
581 mqtt5_publish_return_properties.return_class,
582 mqtt5_publish_return_properties.return_constructor_id,
583 publish_packet_data);
584 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
585
586 if (java_client->jni_publish_events) {
587 (*env)->CallVoidMethod(
588 env,
589 java_client->jni_publish_events,
590 mqtt5_publish_events_properties.publish_events_publish_received_id,
591 java_client->jni_client,
592 publish_packet_return_data);
593 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
594 }
595 goto clean_up;
596
597 clean_up:
598
599 (*env)->PopLocalFrame(env, NULL);
600 /********** JNI ENV RELEASE **********/
601 aws_jni_release_thread_env(jvm, env);
602 }
603
s_aws_mqtt5_client_java_publish_callback_destructor(JNIEnv * env,struct aws_mqtt5_client_publish_return_data * callback_return_data)604 static void s_aws_mqtt5_client_java_publish_callback_destructor(
605 JNIEnv *env,
606 struct aws_mqtt5_client_publish_return_data *callback_return_data) {
607 struct aws_allocator *allocator = aws_jni_get_allocator();
608
609 if (callback_return_data != NULL) {
610 if (callback_return_data->jni_publish_future && env != NULL) {
611 (*env)->DeleteGlobalRef(env, callback_return_data->jni_publish_future);
612 }
613 aws_mem_release(allocator, callback_return_data);
614 }
615 }
616
s_aws_mqtt5_client_java_publish_completion(enum aws_mqtt5_packet_type packet_type,const void * packet,int error_code,void * user_data)617 static void s_aws_mqtt5_client_java_publish_completion(
618 enum aws_mqtt5_packet_type packet_type,
619 const void *packet,
620 int error_code,
621 void *user_data) {
622
623 int exception_error_code = error_code;
624 JavaVM *jvm = NULL;
625 JNIEnv *env = NULL;
626 bool has_pushed_frame = false;
627
628 struct aws_mqtt5_client_publish_return_data *return_data = (struct aws_mqtt5_client_publish_return_data *)user_data;
629 if (!return_data) {
630 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: invalid return data!");
631 return;
632 }
633
634 struct aws_mqtt5_client_java_jni *java_client = return_data->java_client;
635 if (!java_client) {
636 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: invalid client");
637 goto clean_up;
638 }
639
640 /********** JNI ENV ACQUIRE **********/
641 jvm = java_client->jvm;
642 env = aws_jni_acquire_thread_env(jvm);
643 if (env == NULL) {
644 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
645 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: could not get env");
646 goto clean_up;
647 }
648
649 /* Get the future for this specific publish and complete right away if there is an error */
650 jobject jni_publish_future = return_data->jni_publish_future;
651 if (error_code != AWS_OP_SUCCESS) {
652 goto exception;
653 }
654
655 /* If this result is supposed to have a packet and does not, then error right away */
656 if (packet_type != AWS_MQTT5_PT_NONE && packet == NULL) {
657 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: packet type but no packet!");
658 goto exception;
659 }
660
661 /* Calculate the number of references needed */
662 size_t references_needed = 0;
663 if (packet_type == AWS_MQTT5_PT_PUBACK) {
664 struct aws_mqtt5_packet_puback_view *puback_packet = (struct aws_mqtt5_packet_puback_view *)packet;
665 /* A PubAck packet will need 2 references at minimum */
666 references_needed += 2;
667 /* Optionals */
668 s_aws_count_allocation(puback_packet->reason_string, &references_needed);
669 /* Add user properties */
670 references_needed += puback_packet->user_property_count * 2;
671 references_needed += 1; /* Add 1 for array to hold user properties */
672 }
673
674 /**
675 * Push a new local frame so any local references we make are tied to it. Then we can pop it to free memory.
676 */
677 jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed);
678 if (local_frame_result != 0) {
679 AWS_LOGF_ERROR(
680 AWS_LS_MQTT5_CLIENT,
681 "PublishCompletion function: could not push local JNI frame with 12 allocation minimum");
682 exception_error_code = AWS_ERROR_INVALID_STATE;
683 goto exception;
684 }
685 has_pushed_frame = true;
686
687 /* The result */
688 jobject publish_packet_result_data;
689
690 if (packet_type == AWS_MQTT5_PT_NONE) {
691 /* QoS 0 */
692 publish_packet_result_data = (*env)->NewObject(
693 env, mqtt5_publish_result_properties.result_class, mqtt5_publish_result_properties.result_constructor_id);
694 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
695
696 } else if (packet_type == AWS_MQTT5_PT_PUBACK) {
697 /* QoS 1 */
698 struct aws_mqtt5_packet_puback_view *puback_packet = (struct aws_mqtt5_packet_puback_view *)packet;
699
700 /* Make the PubAck packet */
701 jobject puback_packet_data = s_aws_mqtt5_client_create_jni_puback_packet_from_native(env, puback_packet);
702 if (puback_packet_data == NULL) {
703 exception_error_code = AWS_ERROR_INVALID_STATE;
704 goto exception;
705 }
706
707 /* Make the result and populate it with data made above */
708 publish_packet_result_data = (*env)->NewObject(
709 env,
710 mqtt5_publish_result_properties.result_class,
711 mqtt5_publish_result_properties.result_puback_constructor_id,
712 puback_packet_data);
713 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
714
715 } else {
716 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function called with unknown packet type!");
717 exception_error_code = AWS_ERROR_INVALID_STATE;
718 goto exception;
719 }
720
721 /* Complete the future */
722 (*env)->CallBooleanMethod(
723 env, jni_publish_future, completable_future_properties.complete_method_id, publish_packet_result_data);
724 if (aws_jni_check_and_clear_exception(env)) {
725 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: exception when completing future");
726 goto exception;
727 }
728
729 goto clean_up;
730
731 exception:
732 s_complete_future_with_exception(env, &jni_publish_future, exception_error_code);
733 goto clean_up;
734
735 clean_up:
736 s_aws_mqtt5_client_java_publish_callback_destructor(env, return_data);
737 if (env != NULL) {
738 if (has_pushed_frame) {
739 (*env)->PopLocalFrame(env, NULL);
740 }
741 /********** JNI ENV RELEASE **********/
742 aws_jni_release_thread_env(jvm, env);
743 }
744 return;
745 }
746
s_aws_mqtt5_client_java_subscribe_callback_destructor(JNIEnv * env,struct aws_mqtt5_client_subscribe_return_data * callback_return_data)747 static void s_aws_mqtt5_client_java_subscribe_callback_destructor(
748 JNIEnv *env,
749 struct aws_mqtt5_client_subscribe_return_data *callback_return_data) {
750 struct aws_allocator *allocator = aws_jni_get_allocator();
751
752 if (callback_return_data != NULL) {
753 if (callback_return_data->jni_subscribe_future && env != NULL) {
754 (*env)->DeleteGlobalRef(env, callback_return_data->jni_subscribe_future);
755 }
756 aws_mem_release(allocator, callback_return_data);
757 }
758 }
759
s_aws_mqtt5_client_java_subscribe_completion(const struct aws_mqtt5_packet_suback_view * suback,int error_code,void * user_data)760 static void s_aws_mqtt5_client_java_subscribe_completion(
761 const struct aws_mqtt5_packet_suback_view *suback,
762 int error_code,
763 void *user_data) {
764
765 int exception_error_code = error_code;
766 JNIEnv *env = NULL;
767 JavaVM *jvm = NULL;
768 bool has_pushed_frame = false;
769
770 struct aws_mqtt5_client_subscribe_return_data *return_data =
771 (struct aws_mqtt5_client_subscribe_return_data *)user_data;
772 if (!return_data) {
773 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: invalid return data!");
774 return;
775 }
776 struct aws_mqtt5_client_java_jni *java_client = return_data->java_client;
777 if (!java_client) {
778 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: invalid client");
779 goto clean_up;
780 }
781
782 /********** JNI ENV ACQUIRE **********/
783 jvm = java_client->jvm;
784 env = aws_jni_acquire_thread_env(jvm);
785 if (env == NULL) {
786 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
787 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: could not get env");
788 goto clean_up;
789 }
790
791 /* Get the future for this specific subscribe */
792 jobject jni_subscribe_future = return_data->jni_subscribe_future;
793
794 if (error_code != AWS_OP_SUCCESS) {
795 exception_error_code = error_code;
796 goto exception;
797 }
798
799 /* Calculate the number of references needed */
800 size_t references_needed = 0;
801 if (suback != NULL) {
802 /* A SubAck packet will need 1 references at minimum */
803 references_needed += 1;
804 /* Optionals */
805 s_aws_count_allocation(suback->reason_string, &references_needed);
806 /* Add user properties and reason codes */
807 references_needed += (suback->user_property_count) * 2;
808 references_needed += 1; /* Add 1 for arrays to hold user properties */
809 if (suback->reason_code_count > 0) {
810 references_needed += suback->reason_code_count;
811 references_needed += 1; /* Add 1 for arrays to hold reason codes */
812 }
813 }
814
815 /**
816 * Push a new local frame so any local references we make are tied to it. Then we can pop it to free memory.
817 */
818 jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed);
819 if (local_frame_result != 0) {
820 AWS_LOGF_ERROR(
821 AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: could not push local JNI frame with 4 allocation minimum");
822 exception_error_code = AWS_ERROR_INVALID_STATE;
823 goto exception;
824 }
825 has_pushed_frame = true;
826
827 /* The SubAck to return (if present) */
828 jobject suback_packet_data = NULL;
829
830 if (suback != NULL) {
831 suback_packet_data = (*env)->NewObject(
832 env,
833 mqtt5_suback_packet_properties.suback_packet_class,
834 mqtt5_suback_packet_properties.suback_constructor_id);
835
836 if (s_set_jni_string_field_in_packet(
837 env,
838 suback->reason_string,
839 suback_packet_data,
840 mqtt5_suback_packet_properties.suback_reason_string_field_id,
841 "reason string",
842 true) != AWS_OP_SUCCESS) {
843 goto clean_up;
844 }
845
846 if (suback->reason_codes != NULL) {
847 if (suback->reason_code_count > 0) {
848 for (size_t i = 0; i < suback->reason_code_count; ++i) {
849 const enum aws_mqtt5_suback_reason_code *reason_code_data = &suback->reason_codes[i];
850 if (s_set_int_enum_in_packet(
851 env,
852 (int *)reason_code_data,
853 suback_packet_data,
854 mqtt5_suback_packet_properties.suback_native_add_suback_code_id,
855 false) != AWS_OP_SUCCESS) {
856 AWS_LOGF_ERROR(
857 AWS_LS_MQTT5_CLIENT,
858 "Error when creating SubAckPacket from native: Could not set reason code");
859 exception_error_code = AWS_ERROR_INVALID_STATE;
860 goto exception;
861 }
862 }
863 }
864 }
865
866 if (s_set_user_properties_field(
867 env,
868 suback->user_property_count,
869 suback->user_properties,
870 suback_packet_data,
871 mqtt5_suback_packet_properties.suback_user_properties_field_id) == AWS_OP_ERR) {
872 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "When creating PubAckPacket from native could not add user property!");
873 exception_error_code = AWS_ERROR_INVALID_STATE;
874 goto exception;
875 }
876 }
877
878 /* Complete the promise */
879 (*env)->CallBooleanMethod(
880 env, jni_subscribe_future, completable_future_properties.complete_method_id, suback_packet_data);
881 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
882
883 goto clean_up;
884
885 exception:
886 s_complete_future_with_exception(env, &jni_subscribe_future, exception_error_code);
887 goto clean_up;
888
889 clean_up:
890 s_aws_mqtt5_client_java_subscribe_callback_destructor(env, return_data);
891 if (env != NULL) {
892 if (has_pushed_frame) {
893 (*env)->PopLocalFrame(env, NULL);
894 }
895 /********** JNI ENV RELEASE **********/
896 aws_jni_release_thread_env(jvm, env);
897 }
898 }
899
s_aws_mqtt5_client_java_unsubscribe_callback_destructor(JNIEnv * env,struct aws_mqtt5_client_unsubscribe_return_data * callback_return_data)900 static void s_aws_mqtt5_client_java_unsubscribe_callback_destructor(
901 JNIEnv *env,
902 struct aws_mqtt5_client_unsubscribe_return_data *callback_return_data) {
903 struct aws_allocator *allocator = aws_jni_get_allocator();
904
905 if (callback_return_data != NULL) {
906 if (callback_return_data->jni_unsubscribe_future && env != NULL) {
907 (*env)->DeleteGlobalRef(env, callback_return_data->jni_unsubscribe_future);
908 }
909 aws_mem_release(allocator, callback_return_data);
910 }
911 }
912
s_aws_mqtt5_client_java_unsubscribe_completion(const struct aws_mqtt5_packet_unsuback_view * unsuback,int error_code,void * user_data)913 static void s_aws_mqtt5_client_java_unsubscribe_completion(
914 const struct aws_mqtt5_packet_unsuback_view *unsuback,
915 int error_code,
916 void *user_data) {
917
918 int exception_error_code = error_code;
919 JNIEnv *env = NULL;
920 JavaVM *jvm = NULL;
921 bool has_pushed_frame = false;
922
923 struct aws_mqtt5_client_unsubscribe_return_data *return_data =
924 (struct aws_mqtt5_client_unsubscribe_return_data *)user_data;
925 if (!return_data) {
926 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: invalid return data!");
927 return;
928 }
929
930 struct aws_mqtt5_client_java_jni *java_client = return_data->java_client;
931 if (!java_client) {
932 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: invalid client");
933 goto clean_up;
934 }
935
936 /********** JNI ENV ACQUIRE **********/
937 jvm = java_client->jvm;
938 env = aws_jni_acquire_thread_env(jvm);
939 if (env == NULL) {
940 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
941 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: could not get env");
942 goto clean_up;
943 }
944
945 /* Get the future for this specific unsubscribe */
946 jobject jni_unsubscribe_future = return_data->jni_unsubscribe_future;
947
948 /* Calculate the number of references needed */
949 size_t references_needed = 0;
950 if (unsuback != NULL) {
951 /* A UnsubAck packet will need 1 reference at minimum */
952 references_needed += 1;
953 /* Optionals */
954 s_aws_count_allocation(unsuback->reason_string, &references_needed);
955 /* Add user properties and reason codes */
956 references_needed += (unsuback->user_property_count) * 2;
957 references_needed += 1; /* Add 1 for array to hold user properties */
958 if (unsuback->reason_code_count > 0) {
959 references_needed += unsuback->reason_code_count;
960 references_needed += 1; /* Add 1 for array to hold reason codes */
961 }
962 }
963
964 /**
965 * Push a new local frame so any local allocations we make are tied to it. Then we can pop it to free memory.
966 */
967 jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed);
968 if (local_frame_result != 0) {
969 AWS_LOGF_ERROR(
970 AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: could not push local JNI frame with 4 allocation minimum");
971 exception_error_code = AWS_ERROR_INVALID_STATE;
972 goto exception;
973 }
974 has_pushed_frame = true;
975
976 if (error_code != AWS_OP_SUCCESS) {
977 exception_error_code = error_code;
978 goto exception;
979 }
980
981 jobject unsuback_packet_data = (*env)->NewObject(
982 env,
983 mqtt5_unsuback_packet_properties.unsuback_packet_class,
984 mqtt5_unsuback_packet_properties.unsuback_constructor_id);
985
986 if (s_set_jni_string_field_in_packet(
987 env,
988 unsuback->reason_string,
989 unsuback_packet_data,
990 mqtt5_unsuback_packet_properties.unsuback_reason_string_field_id,
991 "reason string",
992 true) != AWS_OP_SUCCESS) {
993 goto clean_up;
994 }
995
996 if (unsuback->reason_codes) {
997 if (unsuback->reason_code_count > 0) {
998 for (size_t i = 0; i < unsuback->reason_code_count; ++i) {
999 const enum aws_mqtt5_unsuback_reason_code *reason_code_data = &unsuback->reason_codes[i];
1000 if (s_set_int_enum_in_packet(
1001 env,
1002 (int *)reason_code_data,
1003 unsuback_packet_data,
1004 mqtt5_unsuback_packet_properties.unsuback_native_add_unsuback_code_id,
1005 false) != AWS_OP_SUCCESS) {
1006 AWS_LOGF_ERROR(
1007 AWS_LS_MQTT5_CLIENT,
1008 "Error when creating UnsubAckPacket from native: Could not set reason code");
1009 exception_error_code = AWS_ERROR_INVALID_STATE;
1010 goto exception;
1011 }
1012 }
1013 }
1014 }
1015
1016 if (s_set_user_properties_field(
1017 env,
1018 unsuback->user_property_count,
1019 unsuback->user_properties,
1020 unsuback_packet_data,
1021 mqtt5_unsuback_packet_properties.unsuback_user_properties_field_id) == AWS_OP_ERR) {
1022 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "When creating UnsubAckPacket from native could not add user property!");
1023 exception_error_code = AWS_ERROR_INVALID_STATE;
1024 goto exception;
1025 }
1026
1027 /* Complete the promise */
1028 (*env)->CallBooleanMethod(
1029 env, jni_unsubscribe_future, completable_future_properties.complete_method_id, unsuback_packet_data);
1030 aws_jni_check_and_clear_exception(env); /* To hide JNI warning */
1031
1032 goto clean_up;
1033
1034 exception:
1035 s_complete_future_with_exception(env, &jni_unsubscribe_future, exception_error_code);
1036 goto clean_up;
1037
1038 clean_up:
1039 s_aws_mqtt5_client_java_unsubscribe_callback_destructor(env, return_data);
1040 if (env != NULL) {
1041 if (has_pushed_frame) {
1042 (*env)->PopLocalFrame(env, NULL);
1043 }
1044 /********** JNI ENV RELEASE **********/
1045 aws_jni_release_thread_env(jvm, env);
1046 }
1047 }
1048
s_aws_mqtt5_client_java_termination(void * complete_ctx)1049 static void s_aws_mqtt5_client_java_termination(void *complete_ctx) {
1050 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)complete_ctx;
1051 if (!java_client) {
1052 AWS_LOGF_ERROR(
1053 AWS_LS_MQTT5_CLIENT, "MQTT5 client termination function in JNI called, but with invalid java_client");
1054 return;
1055 }
1056
1057 /********** JNI ENV ACQUIRE **********/
1058 JavaVM *jvm = java_client->jvm;
1059 JNIEnv *env = aws_jni_acquire_thread_env(jvm);
1060 if (env == NULL) {
1061 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
1062 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "MQTT5 client termination function in JNI called, but could not get env");
1063 return;
1064 }
1065
1066 (*env)->CallVoidMethod(env, java_client->jni_client, crt_resource_properties.release_references);
1067 java_client->client = NULL;
1068
1069 struct aws_allocator *allocator = aws_jni_get_allocator();
1070 aws_mqtt5_client_java_destroy(env, allocator, java_client);
1071
1072 /********** JNI ENV RELEASE **********/
1073 aws_jni_release_thread_env(jvm, env);
1074 }
1075
1076 /*******************************************************************************
1077 * MQTT5 CLIENT FUNCTIONS
1078 ******************************************************************************/
1079
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalStart(JNIEnv * env,jclass jni_class,jlong jni_client)1080 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalStart(
1081 JNIEnv *env,
1082 jclass jni_class,
1083 jlong jni_client) {
1084 (void)jni_class;
1085 aws_cache_jni_ids(env);
1086
1087 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client;
1088 if (!java_client) {
1089 s_aws_mqtt5_client_log_and_throw_exception(
1090 env, "Mqtt5Client.start: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
1091 return;
1092 }
1093 if (!java_client->client) {
1094 s_aws_mqtt5_client_log_and_throw_exception(
1095 env, "Mqtt5Client.start: Invalid/null native client", AWS_ERROR_INVALID_ARGUMENT);
1096 return;
1097 }
1098 int return_result = aws_mqtt5_client_start(java_client->client);
1099
1100 if (return_result != AWS_OP_SUCCESS) {
1101 s_aws_mqtt5_client_log_and_throw_exception(
1102 env, "Mqtt5Client.start: aws_mqtt5_client_start returned a non AWS_OP_SUCCESS code!", aws_last_error());
1103 }
1104 }
1105
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalStop(JNIEnv * env,jclass jni_class,jlong jni_client,jobject jni_disconnect_packet)1106 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalStop(
1107 JNIEnv *env,
1108 jclass jni_class,
1109 jlong jni_client,
1110 jobject jni_disconnect_packet) {
1111 (void)jni_class;
1112 aws_cache_jni_ids(env);
1113
1114 struct aws_allocator *allocator = aws_jni_get_allocator();
1115 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client;
1116 if (!java_client) {
1117 s_aws_mqtt5_client_log_and_throw_exception(
1118 env, "Mqtt5Client.stop: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
1119 return;
1120 }
1121 if (!java_client->client) {
1122 s_aws_mqtt5_client_log_and_throw_exception(
1123 env, "Mqtt5Client.stop: Invalid/null native client", AWS_ERROR_INVALID_ARGUMENT);
1124 return;
1125 }
1126
1127 struct aws_mqtt5_packet_disconnect_view_java_jni *java_disconnect_packet = NULL;
1128 int return_result = AWS_OP_ERR;
1129
1130 if (jni_disconnect_packet) {
1131 java_disconnect_packet =
1132 aws_mqtt5_packet_disconnect_view_create_from_java(env, allocator, jni_disconnect_packet);
1133 if (!java_disconnect_packet) {
1134 s_aws_mqtt5_client_log_and_throw_exception(
1135 env, "Mqtt5Client.stop: Invalid/null disconnect packet", aws_last_error());
1136 goto clean_up;
1137 }
1138 }
1139
1140 return_result = aws_mqtt5_client_stop(
1141 java_client->client, aws_mqtt5_packet_disconnect_view_get_packet(java_disconnect_packet), NULL);
1142 if (return_result != AWS_OP_SUCCESS) {
1143 s_aws_mqtt5_client_log_and_throw_exception(
1144 env, "Mqtt5Client.stop: aws_mqtt5_client_stop returned a non AWS_OP_SUCCESS code!", return_result);
1145 }
1146 goto clean_up;
1147
1148 clean_up:
1149 if (java_disconnect_packet) {
1150 aws_mqtt5_packet_disconnect_view_java_destroy(env, allocator, java_disconnect_packet);
1151 }
1152 return;
1153 }
1154
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalPublish(JNIEnv * env,jclass jni_class,jlong jni_client,jobject jni_publish_packet,jobject jni_publish_future)1155 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalPublish(
1156 JNIEnv *env,
1157 jclass jni_class,
1158 jlong jni_client,
1159 jobject jni_publish_packet,
1160 jobject jni_publish_future) {
1161 (void)jni_class;
1162 aws_cache_jni_ids(env);
1163
1164 struct aws_allocator *allocator = aws_jni_get_allocator();
1165 struct aws_mqtt5_packet_publish_view_java_jni *java_publish_packet = NULL;
1166 struct aws_mqtt5_client_publish_return_data *return_data = NULL;
1167 int error_code = 0;
1168
1169 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client;
1170 if (!java_client) {
1171 s_aws_mqtt5_client_log_and_throw_exception(
1172 env, "Mqtt5Client.publish: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
1173 return;
1174 }
1175 if (!jni_publish_future) {
1176 s_aws_mqtt5_client_log_and_throw_exception(
1177 env, "Mqtt5Client.publish: Invalid/null publish future", AWS_ERROR_INVALID_ARGUMENT);
1178 return;
1179 }
1180
1181 if (!java_client->client) {
1182 error_code = AWS_ERROR_INVALID_ARGUMENT;
1183 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Invalid/null native client");
1184 goto exception;
1185 }
1186 if (!jni_publish_packet) {
1187 error_code = AWS_ERROR_INVALID_ARGUMENT;
1188 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Invalid/Null publish packet!");
1189 goto exception;
1190 }
1191
1192 /* Cannot fail */
1193 return_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_publish_return_data));
1194 return_data->java_client = java_client;
1195 return_data->jni_publish_future = (*env)->NewGlobalRef(env, jni_publish_future);
1196
1197 struct aws_mqtt5_publish_completion_options completion_options = {
1198 .completion_callback = &s_aws_mqtt5_client_java_publish_completion,
1199 .completion_user_data = return_data,
1200 };
1201
1202 java_publish_packet = aws_mqtt5_packet_publish_view_create_from_java(env, allocator, jni_publish_packet);
1203 if (!java_publish_packet) {
1204 error_code = aws_last_error();
1205 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Could not create native publish packet!");
1206 goto exception;
1207 }
1208
1209 int return_result = aws_mqtt5_client_publish(
1210 java_client->client, aws_mqtt5_packet_publish_view_get_packet(java_publish_packet), &completion_options);
1211 if (return_result != AWS_OP_SUCCESS) {
1212 error_code = aws_last_error();
1213 AWS_LOGF_ERROR(
1214 AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Could not publish packet! Error code: %i", return_result);
1215 goto exception;
1216 }
1217 goto clean_up;
1218
1219 exception:
1220 s_complete_future_with_exception(
1221 env,
1222 &jni_publish_future,
1223 (error_code == AWS_ERROR_SUCCESS) ? AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE : error_code);
1224 if (java_publish_packet) {
1225 aws_mqtt5_packet_publish_view_java_destroy(env, allocator, java_publish_packet);
1226 }
1227 if (return_data) {
1228 s_aws_mqtt5_client_java_publish_callback_destructor(env, return_data);
1229 }
1230 return;
1231
1232 clean_up:
1233 if (java_publish_packet) {
1234 aws_mqtt5_packet_publish_view_java_destroy(env, allocator, java_publish_packet);
1235 }
1236 }
1237
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalSubscribe(JNIEnv * env,jclass jni_class,jlong jni_client,jobject jni_subscribe_packet,jobject jni_subscribe_future)1238 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalSubscribe(
1239 JNIEnv *env,
1240 jclass jni_class,
1241 jlong jni_client,
1242 jobject jni_subscribe_packet,
1243 jobject jni_subscribe_future) {
1244 (void)jni_class;
1245 aws_cache_jni_ids(env);
1246
1247 struct aws_allocator *allocator = aws_jni_get_allocator();
1248 struct aws_mqtt5_client_subscribe_return_data *return_data = NULL;
1249 struct aws_mqtt5_packet_subscribe_view_java_jni *java_subscribe_packet = NULL;
1250 int error_code = AWS_ERROR_SUCCESS;
1251
1252 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client;
1253 if (!java_client) {
1254 s_aws_mqtt5_client_log_and_throw_exception(
1255 env, "Mqtt5Client.subscribe: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
1256 return;
1257 }
1258 if (!jni_subscribe_future) {
1259 s_aws_mqtt5_client_log_and_throw_exception(
1260 env, "Mqtt5Client.subscribe: Invalid/null subscribe future", AWS_ERROR_INVALID_ARGUMENT);
1261 return;
1262 }
1263
1264 if (!java_client->client) {
1265 error_code = AWS_ERROR_INVALID_ARGUMENT;
1266 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Invalid/null native client");
1267 goto exception;
1268 }
1269 if (!jni_subscribe_packet) {
1270 error_code = AWS_ERROR_INVALID_ARGUMENT;
1271 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Invalid/Null subscribe packet!");
1272 goto exception;
1273 }
1274
1275 /* Cannot fail */
1276 return_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_subscribe_return_data));
1277 return_data->java_client = java_client;
1278 return_data->jni_subscribe_future = (*env)->NewGlobalRef(env, jni_subscribe_future);
1279
1280 struct aws_mqtt5_subscribe_completion_options completion_options = {
1281 .completion_callback = &s_aws_mqtt5_client_java_subscribe_completion,
1282 .completion_user_data = return_data,
1283 };
1284
1285 java_subscribe_packet = aws_mqtt5_packet_subscribe_view_create_from_java(env, allocator, jni_subscribe_packet);
1286 if (java_subscribe_packet == NULL) {
1287 error_code = aws_last_error();
1288 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Could not create native subscribe packet!");
1289 goto exception;
1290 }
1291
1292 int return_result = aws_mqtt5_client_subscribe(
1293 java_client->client, aws_mqtt5_packet_subscribe_view_get_packet(java_subscribe_packet), &completion_options);
1294 if (return_result != AWS_OP_SUCCESS) {
1295 error_code = aws_last_error();
1296 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Subscribe failed! Error code: %i", return_result);
1297 goto exception;
1298 }
1299 goto clean_up;
1300
1301 exception:
1302 s_complete_future_with_exception(
1303 env,
1304 &jni_subscribe_future,
1305 (error_code == AWS_ERROR_SUCCESS) ? AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE : error_code);
1306 if (java_subscribe_packet) {
1307 aws_mqtt5_packet_subscribe_view_java_destroy(env, allocator, java_subscribe_packet);
1308 }
1309 if (return_data) {
1310 s_aws_mqtt5_client_java_subscribe_callback_destructor(env, return_data);
1311 }
1312 return;
1313
1314 clean_up:
1315 if (java_subscribe_packet) {
1316 aws_mqtt5_packet_subscribe_view_java_destroy(env, allocator, java_subscribe_packet);
1317 }
1318 }
1319
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalUnsubscribe(JNIEnv * env,jclass jni_class,jlong jni_client,jobject jni_unsubscribe_packet,jobject jni_unsubscribe_future)1320 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalUnsubscribe(
1321 JNIEnv *env,
1322 jclass jni_class,
1323 jlong jni_client,
1324 jobject jni_unsubscribe_packet,
1325 jobject jni_unsubscribe_future) {
1326 (void)jni_class;
1327 aws_cache_jni_ids(env);
1328
1329 struct aws_allocator *allocator = aws_jni_get_allocator();
1330 struct aws_mqtt5_client_unsubscribe_return_data *return_data = NULL;
1331 struct aws_mqtt5_packet_unsubscribe_view_java_jni *java_unsubscribe_packet = NULL;
1332 int error_code = AWS_ERROR_SUCCESS;
1333
1334 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client;
1335 if (!java_client) {
1336 s_aws_mqtt5_client_log_and_throw_exception(
1337 env, "Mqtt5Client.unsubscribe: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
1338 return;
1339 }
1340 if (!jni_unsubscribe_future) {
1341 s_aws_mqtt5_client_log_and_throw_exception(
1342 env, "Mqtt5Client.unsubscribe: Invalid/null unsubscribe future", AWS_ERROR_INVALID_ARGUMENT);
1343 return;
1344 }
1345
1346 if (!java_client->client) {
1347 error_code = AWS_ERROR_INVALID_ARGUMENT;
1348 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Invalid/null native client");
1349 goto exception;
1350 }
1351 if (!jni_unsubscribe_packet) {
1352 error_code = AWS_ERROR_INVALID_ARGUMENT;
1353 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Invalid/Null unsubscribe packet!");
1354 goto exception;
1355 }
1356
1357 /* Cannot fail */
1358 return_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_unsubscribe_return_data));
1359 return_data->java_client = java_client;
1360 return_data->jni_unsubscribe_future = (*env)->NewGlobalRef(env, jni_unsubscribe_future);
1361
1362 struct aws_mqtt5_unsubscribe_completion_options completion_options = {
1363 .completion_callback = &s_aws_mqtt5_client_java_unsubscribe_completion,
1364 .completion_user_data = return_data,
1365 };
1366
1367 java_unsubscribe_packet =
1368 aws_mqtt5_packet_unsubscribe_view_create_from_java(env, allocator, jni_unsubscribe_packet);
1369 if (!java_unsubscribe_packet) {
1370 error_code = aws_last_error();
1371 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Could not create native unsubscribe packet!");
1372 goto exception;
1373 }
1374
1375 int return_result = aws_mqtt5_client_unsubscribe(
1376 java_client->client,
1377 aws_mqtt5_packet_unsubscribe_view_get_packet(java_unsubscribe_packet),
1378 &completion_options);
1379 if (return_result != AWS_OP_SUCCESS) {
1380 error_code = aws_last_error();
1381 AWS_LOGF_ERROR(
1382 AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Unsubscribe failed! Error code: %i", return_result);
1383 goto exception;
1384 }
1385 goto clean_up;
1386
1387 exception:
1388 s_complete_future_with_exception(
1389 env,
1390 &jni_unsubscribe_future,
1391 (error_code == AWS_ERROR_SUCCESS) ? AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE : error_code);
1392 if (java_unsubscribe_packet) {
1393 aws_mqtt5_packet_unsubscribe_view_java_destroy(env, allocator, java_unsubscribe_packet);
1394 }
1395 if (return_data) {
1396 s_aws_mqtt5_client_java_unsubscribe_callback_destructor(env, return_data);
1397 }
1398 return;
1399
1400 clean_up:
1401 if (java_unsubscribe_packet) {
1402 aws_mqtt5_packet_unsubscribe_view_java_destroy(env, allocator, java_unsubscribe_packet);
1403 }
1404 }
1405
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalGetOperationStatistics(JNIEnv * env,jclass jni_class,jlong jni_client)1406 JNIEXPORT jobject JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalGetOperationStatistics(
1407 JNIEnv *env,
1408 jclass jni_class,
1409 jlong jni_client) {
1410 (void)jni_class;
1411 aws_cache_jni_ids(env);
1412
1413 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client;
1414 if (!java_client) {
1415 s_aws_mqtt5_client_log_and_throw_exception(
1416 env, "Mqtt5Client.getOperationStatistics: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
1417 return NULL;
1418 }
1419 if (!java_client->client) {
1420 s_aws_mqtt5_client_log_and_throw_exception(
1421 env, "Mqtt5Client.getOperationStatistics: Invalid/null native client", AWS_ERROR_INVALID_ARGUMENT);
1422 return NULL;
1423 }
1424
1425 /* Construct Java object */
1426 jobject jni_operation_statistics = (*env)->NewObject(
1427 env,
1428 mqtt5_client_operation_statistics_properties.statistics_class,
1429 mqtt5_client_operation_statistics_properties.statistics_constructor_id);
1430 if (jni_operation_statistics == NULL) {
1431 return NULL;
1432 }
1433
1434 struct aws_mqtt5_client_operation_statistics client_stats;
1435 aws_mqtt5_client_get_stats(java_client->client, &client_stats);
1436
1437 (*env)->SetLongField(
1438 env,
1439 jni_operation_statistics,
1440 mqtt5_client_operation_statistics_properties.incomplete_operation_count_field_id,
1441 (jlong)client_stats.incomplete_operation_count);
1442 if (aws_jni_check_and_clear_exception(env)) {
1443 aws_raise_error(AWS_ERROR_INVALID_STATE);
1444 aws_jni_throw_runtime_exception(
1445 env, "Mqtt5Client.getOperationStatistics: could not create incomplete operation count");
1446 return NULL;
1447 }
1448
1449 (*env)->SetLongField(
1450 env,
1451 jni_operation_statistics,
1452 mqtt5_client_operation_statistics_properties.incomplete_operation_size_field_id,
1453 (jlong)client_stats.incomplete_operation_size);
1454 if (aws_jni_check_and_clear_exception(env)) {
1455 aws_raise_error(AWS_ERROR_INVALID_STATE);
1456 aws_jni_throw_runtime_exception(
1457 env, "Mqtt5Client.getOperationStatistics: could not create incomplete operation size");
1458 return NULL;
1459 }
1460
1461 (*env)->SetLongField(
1462 env,
1463 jni_operation_statistics,
1464 mqtt5_client_operation_statistics_properties.unacked_operation_count_field_id,
1465 (jlong)client_stats.unacked_operation_count);
1466 if (aws_jni_check_and_clear_exception(env)) {
1467 aws_raise_error(AWS_ERROR_INVALID_STATE);
1468 aws_jni_throw_runtime_exception(
1469 env, "Mqtt5Client.getOperationStatistics: could not create unacked operation count");
1470 return NULL;
1471 }
1472
1473 (*env)->SetLongField(
1474 env,
1475 jni_operation_statistics,
1476 mqtt5_client_operation_statistics_properties.unacked_operation_size_field_id,
1477 (jlong)client_stats.unacked_operation_size);
1478 if (aws_jni_check_and_clear_exception(env)) {
1479 aws_raise_error(AWS_ERROR_INVALID_STATE);
1480 aws_jni_throw_runtime_exception(
1481 env, "Mqtt5Client.getOperationStatistics: could not create unacked operation size");
1482 return NULL;
1483 }
1484
1485 return jni_operation_statistics;
1486 }
1487
1488 /*******************************************************************************
1489 * WEBSOCKET FUNCTIONS
1490 ******************************************************************************/
1491 struct mqtt5_jni_ws_handshake {
1492 struct aws_mqtt5_client_java_jni *java_client;
1493 struct aws_http_message *http_request;
1494 aws_mqtt5_transform_websocket_handshake_complete_fn *complete_fn;
1495 void *complete_ctx;
1496 struct aws_allocator *allocator;
1497 };
1498
s_ws_handshake_destroy(struct mqtt5_jni_ws_handshake * ws_handshake)1499 static void s_ws_handshake_destroy(struct mqtt5_jni_ws_handshake *ws_handshake) {
1500 if (!ws_handshake) {
1501 return;
1502 }
1503 aws_mem_release(ws_handshake->allocator, ws_handshake);
1504 }
1505
s_aws_mqtt5_client_java_websocket_handshake_transform(struct aws_http_message * request,void * user_data,aws_mqtt5_transform_websocket_handshake_complete_fn * complete_fn,void * complete_ctx)1506 static void s_aws_mqtt5_client_java_websocket_handshake_transform(
1507 struct aws_http_message *request,
1508 void *user_data,
1509 aws_mqtt5_transform_websocket_handshake_complete_fn *complete_fn,
1510 void *complete_ctx) {
1511
1512 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)user_data;
1513 if (!java_client) {
1514 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Websocket handshake function in JNI called without valid client");
1515 return;
1516 }
1517 if (!java_client->jni_client || !java_client->client) {
1518 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Websocket handshake function in JNI called with already freed client");
1519 return;
1520 }
1521
1522 /********** JNI ENV ACQUIRE **********/
1523 JNIEnv *env = aws_jni_acquire_thread_env(java_client->jvm);
1524 if (env == NULL) {
1525 /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */
1526 complete_fn(request, AWS_ERROR_INVALID_STATE, complete_ctx);
1527 return;
1528 }
1529
1530 struct aws_allocator *alloc = aws_jni_get_allocator();
1531
1532 /* Cannot fail */
1533 struct mqtt5_jni_ws_handshake *ws_handshake = aws_mem_calloc(alloc, 1, sizeof(struct mqtt5_jni_ws_handshake));
1534
1535 ws_handshake->java_client = java_client;
1536 ws_handshake->complete_ctx = complete_ctx;
1537 ws_handshake->complete_fn = complete_fn;
1538 ws_handshake->http_request = request;
1539 ws_handshake->allocator = alloc;
1540
1541 jobject java_http_request = aws_java_http_request_from_native(env, request, NULL);
1542 if (!java_http_request) {
1543 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Could not create a HttpRequest for Java in MQTT5 client");
1544 aws_raise_error(AWS_ERROR_UNKNOWN);
1545 goto error;
1546 }
1547
1548 if (java_client->jni_client) {
1549 jobject jni_client = java_client->jni_client;
1550 (*env)->CallVoidMethod(
1551 env, jni_client, mqtt5_client_properties.client_on_websocket_handshake_id, java_http_request, ws_handshake);
1552 AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
1553 }
1554
1555 (*env)->DeleteLocalRef(env, java_http_request);
1556 aws_jni_release_thread_env(java_client->jvm, env);
1557 /********** JNI ENV RELEASE SUCCESS PATH **********/
1558
1559 return;
1560
1561 error:;
1562 int error_code = aws_last_error();
1563 s_ws_handshake_destroy(ws_handshake);
1564 complete_fn(request, error_code, complete_ctx);
1565 aws_jni_release_thread_env(java_client->jvm, env);
1566 /********** JNI ENV RELEASE FAILURE PATH **********/
1567 }
1568
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalWebsocketHandshakeComplete(JNIEnv * env,jclass jni_class,jlong jni_connection,jbyteArray jni_marshalled_request,jobject jni_throwable,jlong jni_user_data)1569 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalWebsocketHandshakeComplete(
1570 JNIEnv *env,
1571 jclass jni_class,
1572 jlong jni_connection,
1573 jbyteArray jni_marshalled_request,
1574 jobject jni_throwable,
1575 jlong jni_user_data) {
1576 (void)jni_class;
1577 (void)jni_connection;
1578 aws_cache_jni_ids(env);
1579
1580 struct mqtt5_jni_ws_handshake *ws_handshake = (void *)jni_user_data;
1581 int error_code = AWS_ERROR_SUCCESS;
1582
1583 if (!ws_handshake) {
1584 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Websocket handshake complete function in JNI called without handshake");
1585 return;
1586 }
1587
1588 if (jni_throwable != NULL) {
1589 if ((*env)->IsInstanceOf(env, jni_throwable, crt_runtime_exception_properties.crt_runtime_exception_class)) {
1590 error_code = (*env)->GetIntField(env, jni_throwable, crt_runtime_exception_properties.error_code_field_id);
1591 }
1592 if (error_code == AWS_ERROR_SUCCESS) {
1593 error_code = AWS_ERROR_UNKNOWN; /* is there anything more that could be done here? */
1594 }
1595 goto done;
1596 }
1597
1598 if (aws_apply_java_http_request_changes_to_native_request(
1599 env, jni_marshalled_request, NULL, ws_handshake->http_request)) {
1600 error_code = aws_last_error();
1601 goto done;
1602 }
1603
1604 done:
1605 ws_handshake->complete_fn(ws_handshake->http_request, error_code, ws_handshake->complete_ctx);
1606 s_ws_handshake_destroy(ws_handshake);
1607 }
1608
s_initialize_topic_aliasing_options(JNIEnv * env,struct aws_mqtt5_client_topic_alias_options * topic_aliasing_options,jobject jni_topic_aliasing_options)1609 static int s_initialize_topic_aliasing_options(
1610 JNIEnv *env,
1611 struct aws_mqtt5_client_topic_alias_options *topic_aliasing_options,
1612 jobject jni_topic_aliasing_options) {
1613
1614 jobject jni_outbound_behavior = (*env)->GetObjectField(
1615 env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.outbound_behavior_field_id);
1616 if (jni_outbound_behavior != NULL) {
1617 jint enum_value = (*env)->CallIntMethod(
1618 env, jni_outbound_behavior, mqtt5_outbound_topic_alias_behavior_type_properties.get_value_method_id);
1619 if (aws_jni_check_and_clear_exception(env)) {
1620 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Error getting native value from OutboundTopicAliasBehaviorType");
1621 return aws_raise_error(AWS_ERROR_INVALID_STATE);
1622 }
1623
1624 topic_aliasing_options->outbound_topic_alias_behavior =
1625 (enum aws_mqtt5_client_outbound_topic_alias_behavior_type)enum_value;
1626 }
1627
1628 jobject jni_outbound_cache_max_size = (*env)->GetObjectField(
1629 env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.outbound_cache_max_size_field_id);
1630 if (jni_outbound_cache_max_size != NULL) {
1631 jint int_value =
1632 (*env)->CallIntMethod(env, jni_outbound_cache_max_size, boxed_integer_properties.integer_get_value_id);
1633
1634 if (int_value < 0 || int_value > UINT16_MAX) {
1635 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Invalid outbound cache size value: %d", int_value);
1636 return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1637 }
1638
1639 topic_aliasing_options->outbound_alias_cache_max_size = (uint16_t)int_value;
1640 }
1641
1642 jobject jni_inbound_behavior = (*env)->GetObjectField(
1643 env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.inbound_behavior_field_id);
1644 if (jni_inbound_behavior != NULL) {
1645 jint enum_value = (*env)->CallIntMethod(
1646 env, jni_inbound_behavior, mqtt5_inbound_topic_alias_behavior_type_properties.get_value_method_id);
1647 if (aws_jni_check_and_clear_exception(env)) {
1648 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Error getting native value from InboundTopicAliasBehaviorType");
1649 return aws_raise_error(AWS_ERROR_INVALID_STATE);
1650 }
1651
1652 topic_aliasing_options->inbound_topic_alias_behavior =
1653 (enum aws_mqtt5_client_inbound_topic_alias_behavior_type)enum_value;
1654 }
1655
1656 jobject jni_inbound_cache_max_size = (*env)->GetObjectField(
1657 env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.inbound_cache_max_size_field_id);
1658 if (jni_inbound_cache_max_size != NULL) {
1659 jint int_value =
1660 (*env)->CallIntMethod(env, jni_inbound_cache_max_size, boxed_integer_properties.integer_get_value_id);
1661
1662 if (int_value < 0 || int_value > UINT16_MAX) {
1663 AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Invalid inbound cache size value: %d", int_value);
1664 return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1665 }
1666
1667 topic_aliasing_options->inbound_alias_cache_size = (uint16_t)int_value;
1668 }
1669
1670 return AWS_OP_SUCCESS;
1671 }
1672
1673 /*******************************************************************************
1674 * JNI FUNCTIONS
1675 ******************************************************************************/
1676
1677 /* Create and Destroy
1678 **************************************/
1679
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientNew(JNIEnv * env,jclass jni_class,jobject jni_options,jobject jni_connect_options,jobject jni_bootstrap,jobject jni_client)1680 JNIEXPORT jlong JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientNew(
1681 JNIEnv *env,
1682 jclass jni_class,
1683 jobject jni_options,
1684 jobject jni_connect_options,
1685 jobject jni_bootstrap,
1686 jobject jni_client) {
1687 (void)jni_class;
1688 aws_cache_jni_ids(env);
1689
1690 struct aws_allocator *allocator = aws_jni_get_allocator();
1691 struct aws_mqtt5_packet_connect_view_java_jni *connect_options = NULL;
1692 struct aws_mqtt5_client_options client_options;
1693 AWS_ZERO_STRUCT(client_options);
1694 struct aws_http_proxy_options_java_jni *java_http_proxy_options = NULL;
1695 struct aws_byte_buf host_name_buf;
1696
1697 /* Needed to track if optionals are set or not */
1698 bool was_value_set = false;
1699
1700 /**
1701 * Push a new local frame so any local allocations we make are tied to it. Then we can pop it to free memory.
1702 * In Java JNI allocations here, we have 21 allocations so we need at least that many.
1703 * It should expand if we use more.
1704 * (NOTE: We cannot get the exact here because we are pulling from Java objects and we have no way to know how many
1705 * that will need)
1706 */
1707 jint local_frame_result = (*env)->PushLocalFrame(env, (jint)21);
1708 if (local_frame_result != 0) {
1709 s_aws_mqtt5_client_log_and_throw_exception(
1710 env,
1711 "MQTT5 client new: could not push local JNI frame with 21 allocation minimum",
1712 AWS_ERROR_INVALID_STATE);
1713 return (jlong)NULL;
1714 }
1715
1716 struct aws_mqtt5_client_java_jni *java_client =
1717 aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_java_jni));
1718 AWS_LOGF_DEBUG(AWS_LS_MQTT5_CLIENT, "java_client=%p: Initializing MQTT5 client", (void *)java_client);
1719 if (java_client == NULL) {
1720 s_aws_mqtt5_client_log_and_throw_exception(
1721 env, "MQTT5 client new: could not initialize new client", AWS_ERROR_INVALID_STATE);
1722 goto clean_up;
1723 }
1724
1725 if (aws_get_string_from_jobject(
1726 env,
1727 jni_options,
1728 mqtt5_client_options_properties.options_host_name_field_id,
1729 s_client_string,
1730 "Host Name",
1731 &host_name_buf,
1732 &client_options.host_name,
1733 false,
1734 &was_value_set) != AWS_OP_SUCCESS) {
1735 s_aws_mqtt5_client_log_and_throw_exception(
1736 env, "MQTT5 client new: Could not get host name from options", AWS_ERROR_INVALID_STATE);
1737 goto clean_up;
1738 }
1739
1740 uint32_t port = 0;
1741 if (aws_get_uint32_from_jobject(
1742 env,
1743 jni_options,
1744 mqtt5_client_options_properties.options_port_field_id,
1745 s_client_string,
1746 "port",
1747 &port,
1748 false,
1749 &was_value_set) != AWS_OP_SUCCESS) {
1750 s_aws_mqtt5_client_log_and_throw_exception(
1751 env, "MQTT5 client new: Could not get port from options", AWS_ERROR_INVALID_STATE);
1752 goto clean_up;
1753 }
1754 if (was_value_set) {
1755 client_options.port = port;
1756 }
1757
1758 if (!jni_bootstrap) {
1759 s_aws_mqtt5_client_log_and_throw_exception(
1760 env, "MQTT5 client new: no bootstrap found", AWS_ERROR_INVALID_ARGUMENT);
1761 goto clean_up;
1762 }
1763 jlong jni_bootstrap_pointer =
1764 (*env)->CallLongMethod(env, jni_bootstrap, crt_resource_properties.get_native_handle_method_id);
1765 if (aws_jni_check_and_clear_exception(env)) {
1766 s_aws_mqtt5_client_log_and_throw_exception(
1767 env, "MQTT5 client new: could not get native handle for bootstrap", AWS_ERROR_INVALID_ARGUMENT);
1768 goto clean_up;
1769 }
1770 struct aws_client_bootstrap *bootstrap = (struct aws_client_bootstrap *)jni_bootstrap_pointer;
1771 client_options.bootstrap = bootstrap;
1772
1773 struct aws_socket_options *socket_options = NULL;
1774 struct aws_socket_options tmp_socket_options;
1775 jobject jni_socket_options =
1776 (*env)->CallObjectMethod(env, jni_options, mqtt5_client_options_properties.options_get_socket_options_id);
1777 if (aws_jni_check_and_clear_exception(env)) {
1778 s_aws_mqtt5_client_log_and_throw_exception(
1779 env, "MQTT5 client new: error getting socket options", AWS_ERROR_INVALID_STATE);
1780 goto clean_up;
1781 }
1782 if (jni_socket_options) {
1783 jlong jni_socket_options_pointer =
1784 (*env)->CallLongMethod(env, jni_socket_options, crt_resource_properties.get_native_handle_method_id);
1785 if (aws_jni_check_and_clear_exception(env)) {
1786 s_aws_mqtt5_client_log_and_throw_exception(
1787 env, "MQTT5 client new: could not get native handle for socket options", AWS_ERROR_INVALID_ARGUMENT);
1788 goto clean_up;
1789 }
1790 socket_options = (struct aws_socket_options *)jni_socket_options_pointer;
1791 }
1792 if (socket_options == NULL) {
1793 tmp_socket_options.type = AWS_SOCKET_STREAM;
1794 tmp_socket_options.domain = AWS_SOCKET_IPV4;
1795 tmp_socket_options.connect_timeout_ms = 10000;
1796 client_options.socket_options = &tmp_socket_options;
1797 } else {
1798 client_options.socket_options = socket_options;
1799 }
1800
1801 jobject jni_tls_options =
1802 (*env)->CallObjectMethod(env, jni_options, mqtt5_client_options_properties.options_get_tls_options_id);
1803 if (aws_jni_check_and_clear_exception(env)) {
1804 s_aws_mqtt5_client_log_and_throw_exception(
1805 env, "MQTT5 client new: error getting tls options", AWS_ERROR_INVALID_STATE);
1806 goto clean_up;
1807 }
1808 if (jni_tls_options) {
1809 jlong jni_tls_pointer =
1810 (*env)->CallLongMethod(env, jni_tls_options, crt_resource_properties.get_native_handle_method_id);
1811 if (aws_jni_check_and_clear_exception(env)) {
1812 s_aws_mqtt5_client_log_and_throw_exception(
1813 env, "MQTT5 client new: could not get native handle for tls options", AWS_ERROR_INVALID_ARGUMENT);
1814 goto clean_up;
1815 }
1816 struct aws_tls_ctx *tls_ctx = (struct aws_tls_ctx *)jni_tls_pointer;
1817 if (tls_ctx) {
1818 aws_tls_connection_options_init_from_ctx(&java_client->tls_options, tls_ctx);
1819 aws_tls_connection_options_set_server_name(&java_client->tls_options, allocator, &client_options.host_name);
1820 client_options.tls_options = &java_client->tls_options;
1821 }
1822 } else {
1823 client_options.tls_options = NULL;
1824 }
1825
1826 jobject jni_http_proxy_options =
1827 (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.http_proxy_options_field_id);
1828 if (aws_jni_check_and_clear_exception(env)) {
1829 s_aws_mqtt5_client_log_and_throw_exception(
1830 env, "MQTT5 client new: error getting http proxy options", AWS_ERROR_INVALID_STATE);
1831 goto clean_up;
1832 }
1833 if (jni_http_proxy_options) {
1834 java_http_proxy_options =
1835 s_aws_mqtt5_http_proxy_options_create_from_java(env, allocator, java_client, jni_http_proxy_options);
1836 client_options.http_proxy_options = &java_http_proxy_options->options;
1837
1838 if (client_options.http_proxy_options->connection_type != AWS_HPCT_HTTP_TUNNEL) {
1839 s_aws_mqtt5_client_log_and_throw_exception(
1840 env, "MQTT5 client new: http proxy connection type has to be set to tunnel", AWS_ERROR_INVALID_STATE);
1841 goto clean_up;
1842 }
1843 }
1844
1845 if (jni_connect_options) {
1846 connect_options = aws_mqtt5_packet_connect_view_create_from_java(env, allocator, jni_connect_options);
1847 if (connect_options != NULL || aws_jni_check_and_clear_exception(env)) {
1848 client_options.connect_options = aws_mqtt5_packet_connect_view_get_packet(connect_options);
1849 } else {
1850 s_aws_mqtt5_client_log_and_throw_exception(
1851 env, "MQTT5 client new: error getting connect options", AWS_ERROR_INVALID_STATE);
1852 goto clean_up;
1853 }
1854 }
1855
1856 uint32_t session_behavior = UINT32_MAX;
1857 if (aws_get_enum_from_jobject(
1858 env,
1859 jni_options,
1860 mqtt5_client_options_properties.options_get_session_behavior_id,
1861 s_client_string,
1862 "session behavior",
1863 mqtt5_client_session_behavior_properties.client_get_value_id,
1864 &session_behavior,
1865 true,
1866 &was_value_set) == AWS_OP_ERR) {
1867 s_aws_mqtt5_client_log_and_throw_exception(
1868 env, "MQTT5 client new: Could not get session behavior from options", AWS_ERROR_INVALID_STATE);
1869 goto clean_up;
1870 }
1871 if (was_value_set) {
1872 client_options.session_behavior = (enum aws_mqtt5_client_session_behavior_type)session_behavior;
1873 }
1874
1875 uint32_t extended_validation_and_flow_control_options = UINT32_MAX;
1876 if (aws_get_enum_from_jobject(
1877 env,
1878 jni_options,
1879 mqtt5_client_options_properties.options_get_extended_validation_and_flow_control_options_id,
1880 s_client_string,
1881 "extended validation and flow control",
1882 mqtt5_client_extended_validation_and_flow_control_options.client_get_value_id,
1883 &extended_validation_and_flow_control_options,
1884 true,
1885 &was_value_set) == AWS_OP_ERR) {
1886 s_aws_mqtt5_client_log_and_throw_exception(
1887 env,
1888 "MQTT5 client new: Could not extended validation and flow control from options",
1889 AWS_ERROR_INVALID_STATE);
1890 goto clean_up;
1891 }
1892 if (was_value_set) {
1893 client_options.extended_validation_and_flow_control_options =
1894 (enum aws_mqtt5_extended_validation_and_flow_control_options)extended_validation_and_flow_control_options;
1895 }
1896
1897 uint32_t offline_queue_enum = UINT32_MAX;
1898 if (aws_get_enum_from_jobject(
1899 env,
1900 jni_options,
1901 mqtt5_client_options_properties.options_get_offline_queue_behavior_id,
1902 s_client_string,
1903 "offline queue behavior",
1904 mqtt5_client_offline_queue_behavior_type_properties.client_get_value_id,
1905 &offline_queue_enum,
1906 true,
1907 &was_value_set) == AWS_OP_ERR) {
1908 s_aws_mqtt5_client_log_and_throw_exception(
1909 env, "MQTT5 client new: Could not get offline queue behavior from options", AWS_ERROR_INVALID_STATE);
1910 goto clean_up;
1911 }
1912 if (was_value_set) {
1913 client_options.offline_queue_behavior = (enum aws_mqtt5_client_operation_queue_behavior_type)offline_queue_enum;
1914 }
1915
1916 uint32_t retry_jitter_enum = UINT32_MAX;
1917 if (aws_get_enum_from_jobject(
1918 env,
1919 jni_options,
1920 mqtt5_client_options_properties.options_get_retry_jitter_mode_id,
1921 s_client_string,
1922 "retry jitter mode",
1923 mqtt5_client_jitter_mode_properties.client_get_value_id,
1924 &retry_jitter_enum,
1925 true,
1926 &was_value_set) == AWS_OP_ERR) {
1927 s_aws_mqtt5_client_log_and_throw_exception(
1928 env, "MQTT5 client new: Could not get retry jitter mode from options", AWS_ERROR_INVALID_STATE);
1929 goto clean_up;
1930 }
1931 if (was_value_set) {
1932 client_options.retry_jitter_mode = (enum aws_exponential_backoff_jitter_mode)retry_jitter_enum;
1933 }
1934
1935 uint64_t min_reconnect_delay_ms = 0;
1936 if (aws_get_uint64_from_jobject(
1937 env,
1938 jni_options,
1939 mqtt5_client_options_properties.min_reconnect_delay_ms_field_id,
1940 s_client_string,
1941 "minimum reconnect delay",
1942 &min_reconnect_delay_ms,
1943 true,
1944 &was_value_set) != AWS_OP_SUCCESS) {
1945 s_aws_mqtt5_client_log_and_throw_exception(
1946 env, "MQTT5 client new: Could not get minimum reconnect delay from options", AWS_ERROR_INVALID_STATE);
1947 goto clean_up;
1948 }
1949 if (was_value_set) {
1950 client_options.min_reconnect_delay_ms = min_reconnect_delay_ms;
1951 }
1952
1953 uint64_t max_reconnect_delay_ms = 0;
1954 if (aws_get_uint64_from_jobject(
1955 env,
1956 jni_options,
1957 mqtt5_client_options_properties.max_reconnect_delay_ms_field_id,
1958 s_client_string,
1959 "maximum reconnect delay",
1960 &max_reconnect_delay_ms,
1961 true,
1962 &was_value_set) != AWS_OP_SUCCESS) {
1963 s_aws_mqtt5_client_log_and_throw_exception(
1964 env, "MQTT5 client new: Could not get maximum reconnect delay from options", AWS_ERROR_INVALID_STATE);
1965 goto clean_up;
1966 }
1967 if (was_value_set) {
1968 client_options.max_reconnect_delay_ms = max_reconnect_delay_ms;
1969 }
1970
1971 uint64_t min_connected_time_to_reset_reconnect_delay_ms = 0;
1972 if (aws_get_uint64_from_jobject(
1973 env,
1974 jni_options,
1975 mqtt5_client_options_properties.min_connected_time_to_reset_reconnect_delay_ms_field_id,
1976 s_client_string,
1977 "minimum connected time to reset reconnect delay",
1978 &min_connected_time_to_reset_reconnect_delay_ms,
1979 true,
1980 &was_value_set) != AWS_OP_SUCCESS) {
1981 s_aws_mqtt5_client_log_and_throw_exception(
1982 env,
1983 "MQTT5 client new: Could not get minimum connected time to reset reconnect delay from options",
1984 AWS_ERROR_INVALID_STATE);
1985 goto clean_up;
1986 }
1987 if (was_value_set) {
1988 client_options.min_connected_time_to_reset_reconnect_delay_ms = min_connected_time_to_reset_reconnect_delay_ms;
1989 }
1990
1991 uint32_t ping_timeout = 0;
1992 if (aws_get_uint32_from_jobject(
1993 env,
1994 jni_options,
1995 mqtt5_client_options_properties.ping_timeout_ms_field_id,
1996 s_client_string,
1997 "ping timeout",
1998 &ping_timeout,
1999 true,
2000 &was_value_set) != AWS_OP_SUCCESS) {
2001 s_aws_mqtt5_client_log_and_throw_exception(
2002 env, "MQTT5 client new: Could not get ping timeout from options", AWS_ERROR_INVALID_STATE);
2003 goto clean_up;
2004 }
2005 if (was_value_set) {
2006 client_options.ping_timeout_ms = ping_timeout;
2007 }
2008
2009 uint32_t connack_timeout = 0;
2010 if (aws_get_uint32_from_jobject(
2011 env,
2012 jni_options,
2013 mqtt5_client_options_properties.connack_timeout_ms_field_id,
2014 s_client_string,
2015 "ConnAck timeout",
2016 &connack_timeout,
2017 true,
2018 &was_value_set) != AWS_OP_SUCCESS) {
2019 s_aws_mqtt5_client_log_and_throw_exception(
2020 env, "MQTT5 client new: Could not get ConnAck timeout from options", AWS_ERROR_INVALID_STATE);
2021 goto clean_up;
2022 }
2023 if (was_value_set) {
2024 client_options.connack_timeout_ms = connack_timeout;
2025 }
2026
2027 uint32_t ack_timeout = 0;
2028 if (aws_get_uint32_from_jobject(
2029 env,
2030 jni_options,
2031 mqtt5_client_options_properties.ack_timeout_seconds_field_id,
2032 s_client_string,
2033 "Ack timeout",
2034 &ack_timeout,
2035 true,
2036 &was_value_set) != AWS_OP_SUCCESS) {
2037 s_aws_mqtt5_client_log_and_throw_exception(
2038 env, "MQTT5 client new: Could not get Ack timeout from options", AWS_ERROR_INVALID_STATE);
2039 goto clean_up;
2040 }
2041 if (was_value_set) {
2042 client_options.ack_timeout_seconds = ack_timeout;
2043 }
2044
2045 jint jvmresult = (*env)->GetJavaVM(env, &java_client->jvm);
2046 if (jvmresult != 0) {
2047 s_aws_mqtt5_client_log_and_throw_exception(env, "MQTT5 client new: Unable to get JVM", AWS_ERROR_INVALID_STATE);
2048 goto clean_up;
2049 }
2050 java_client->jni_client = (*env)->NewGlobalRef(env, jni_client);
2051
2052 client_options.lifecycle_event_handler = &s_aws_mqtt5_client_java_lifecycle_event;
2053 client_options.lifecycle_event_handler_user_data = (void *)java_client;
2054
2055 client_options.publish_received_handler = &s_aws_mqtt5_client_java_publish_received;
2056 client_options.publish_received_handler_user_data = (void *)java_client;
2057
2058 /* Are we using websockets? */
2059 jobject jni_websocket_handshake =
2060 (*env)->GetObjectField(env, jni_client, mqtt5_client_properties.websocket_handshake_field_id);
2061 if (aws_jni_check_and_clear_exception(env)) {
2062 s_aws_mqtt5_client_log_and_throw_exception(
2063 env, "MQTT5 client new: error getting websocket handshake transform", AWS_ERROR_INVALID_STATE);
2064 goto clean_up;
2065 }
2066 if (jni_websocket_handshake) {
2067 client_options.websocket_handshake_transform = &s_aws_mqtt5_client_java_websocket_handshake_transform;
2068 client_options.websocket_handshake_transform_user_data = (void *)java_client;
2069 }
2070
2071 jobject jni_publish_events =
2072 (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.publish_events_field_id);
2073 if (aws_jni_check_and_clear_exception(env)) {
2074 s_aws_mqtt5_client_log_and_throw_exception(
2075 env, "MQTT5 client new: error getting publish events", AWS_ERROR_INVALID_STATE);
2076 goto clean_up;
2077 }
2078 if (jni_publish_events != NULL) {
2079 java_client->jni_publish_events = (*env)->NewGlobalRef(env, jni_publish_events);
2080 }
2081
2082 jobject jni_lifecycle_events =
2083 (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.lifecycle_events_field_id);
2084 if (aws_jni_check_and_clear_exception(env)) {
2085 s_aws_mqtt5_client_log_and_throw_exception(
2086 env, "MQTT5 client new: error getting lifecycle events", AWS_ERROR_INVALID_STATE);
2087 goto clean_up;
2088 }
2089 if (jni_lifecycle_events != NULL) {
2090 java_client->jni_lifecycle_events = (*env)->NewGlobalRef(env, jni_lifecycle_events);
2091 }
2092
2093 struct aws_mqtt5_client_topic_alias_options topic_aliasing_options;
2094 AWS_ZERO_STRUCT(topic_aliasing_options);
2095 jobject jni_topic_aliasing_options =
2096 (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.topic_aliasing_options_field_id);
2097
2098 if (jni_topic_aliasing_options != NULL) {
2099 if (s_initialize_topic_aliasing_options(env, &topic_aliasing_options, jni_topic_aliasing_options) ==
2100 AWS_OP_SUCCESS) {
2101 client_options.topic_aliasing_options = &topic_aliasing_options;
2102 }
2103 }
2104
2105 client_options.client_termination_handler = &s_aws_mqtt5_client_java_termination;
2106 client_options.client_termination_handler_user_data = (void *)java_client;
2107
2108 /* Make the MQTT5 client */
2109 java_client->client = aws_mqtt5_client_new(allocator, &client_options);
2110 /* Did we successfully make a client? If not, then throw an exception */
2111 if (java_client->client == NULL) {
2112 s_aws_mqtt5_client_log_and_throw_exception(
2113 env,
2114 "MQTT5 client new: Was unable to create client due to option configuration! Enable error logging to see "
2115 "reason",
2116 AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
2117 goto clean_up;
2118 }
2119 goto clean_up;
2120
2121 clean_up:
2122
2123 aws_mqtt5_packet_connect_view_java_destroy(env, allocator, connect_options);
2124 s_aws_mqtt5_http_proxy_options_java_destroy(env, allocator, java_http_proxy_options);
2125 if (aws_byte_buf_is_valid(&host_name_buf)) {
2126 aws_byte_buf_clean_up(&host_name_buf);
2127 }
2128 (*env)->PopLocalFrame(env, NULL);
2129
2130 if (java_client->client != NULL) {
2131 return (jlong)java_client;
2132 }
2133
2134 aws_mqtt5_client_java_destroy(env, allocator, java_client);
2135 return (jlong)NULL;
2136 }
2137
Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientDestroy(JNIEnv * env,jclass jni_class,jlong jni_mqtt_client)2138 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientDestroy(
2139 JNIEnv *env,
2140 jclass jni_class,
2141 jlong jni_mqtt_client) {
2142 (void)jni_class;
2143 aws_cache_jni_ids(env);
2144
2145 struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_mqtt_client;
2146 if (!java_client) {
2147 s_aws_mqtt5_client_log_and_throw_exception(
2148 env, "MQTT5 client destroy: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT);
2149 return;
2150 }
2151
2152 // If the client is NOT null it can be shut down normally
2153 struct aws_allocator *allocator = aws_jni_get_allocator();
2154 if (java_client->client) {
2155 aws_mqtt5_client_release(java_client->client);
2156 } else {
2157 aws_mqtt5_client_java_destroy(env, allocator, java_client);
2158 }
2159 }
2160
2161 #if UINTPTR_MAX == 0xffffffff
2162 # if defined(_MSC_VER)
2163 # pragma warning(pop)
2164 # else
2165 # pragma GCC diagnostic pop
2166 # endif
2167 #endif
2168