xref: /aosp_15_r20/external/aws-crt-java/src/native/mqtt5_client.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
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