/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include #include #include #include #include #include #include #include /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */ #if UINTPTR_MAX == 0xffffffff # if defined(_MSC_VER) # pragma warning(push) # pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */ # else # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpointer-to-int-cast" # pragma GCC diagnostic ignored "-Wint-to-pointer-cast" # endif #endif /******************************************************************************* * CLIENT ONLY STRUCTS ******************************************************************************/ struct aws_mqtt5_client_publish_return_data { struct aws_mqtt5_client_java_jni *java_client; jobject jni_publish_future; }; struct aws_mqtt5_client_subscribe_return_data { struct aws_mqtt5_client_java_jni *java_client; jobject jni_subscribe_future; }; struct aws_mqtt5_client_unsubscribe_return_data { struct aws_mqtt5_client_java_jni *java_client; jobject jni_unsubscribe_future; }; struct aws_http_proxy_options_java_jni { struct aws_http_proxy_options options; struct aws_byte_buf proxy_host_buf; struct aws_byte_cursor proxy_host_cursor; struct aws_byte_buf authorization_username_buf; struct aws_byte_cursor authorization_username_cursor; struct aws_byte_buf authorization_password_buf; struct aws_byte_cursor authorization_password_cursor; }; /******************************************************************************* * HELPER FUNCTION (LOGGING) ******************************************************************************/ static void s_aws_mqtt5_client_log_and_throw_exception(JNIEnv *env, const char *message, int error_code) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "%s - error code: %i", message, error_code); // raise error to update the "last_error_code" aws_raise_error(error_code); aws_jni_throw_runtime_exception(env, "%s - error code: %i", message, error_code); } /******************************************************************************* * HTTP PROXY FUNCTIONS ******************************************************************************/ static void s_aws_mqtt5_http_proxy_options_java_destroy( JNIEnv *env, struct aws_allocator *allocator, struct aws_http_proxy_options_java_jni *http_options) { (void)env; if (!http_options) { return; } AWS_LOGF_DEBUG(AWS_LS_MQTT5_CLIENT, "id=%p: Destroying JavaHttpProxyOptions", (void *)http_options); if (aws_byte_buf_is_valid(&http_options->proxy_host_buf)) { aws_byte_buf_clean_up(&http_options->proxy_host_buf); } if (aws_byte_buf_is_valid(&http_options->authorization_username_buf)) { aws_byte_buf_clean_up(&http_options->authorization_username_buf); } if (aws_byte_buf_is_valid(&http_options->authorization_password_buf)) { aws_byte_buf_clean_up(&http_options->authorization_password_buf); } /* Frees all allocated memory */ aws_mem_release(allocator, http_options); } static struct aws_http_proxy_options_java_jni *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) { /* Cannot fail */ struct aws_http_proxy_options_java_jni *http_options = aws_mem_calloc(allocator, 1, sizeof(struct aws_http_proxy_options_java_jni)); AWS_LOGF_DEBUG( AWS_LS_MQTT5_CLIENT, "JavaHttpProxyOptions=%p: Created new JavaHttpProxyOptions", (void *)http_options); jobject jni_proxy_connection_type = (*env)->CallObjectMethod( env, java_http_proxy_options, http_proxy_options_properties.proxy_get_connection_type_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_connection_type) { jint jni_proxy_connection_type_value = (*env)->CallIntMethod( env, jni_proxy_connection_type, http_proxy_connection_type_properties.proxy_get_value_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_connection_type_value) { int32_t jni_proxy_connection_type_value_check = (int32_t)jni_proxy_connection_type_value; if (jni_proxy_connection_type_value_check < 0) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "HTTP Proxy Options connection type is less than 0"); goto on_error; } else if (jni_proxy_connection_type_value_check > AWS_HPCT_HTTP_TUNNEL) { /* The (current) maximum enum */ /* value */ AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "HTTP Proxy Options connection type is more than maximum allowed value"); goto on_error; } else { http_options->options.connection_type = (enum aws_http_proxy_connection_type)jni_proxy_connection_type_value; } } } jstring jni_proxy_host = (jstring)(*env)->CallObjectMethod( env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_host_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_host) { // Get the data, copy it, and then release the JNI stuff struct aws_byte_cursor tmp_cursor = aws_jni_byte_cursor_from_jstring_acquire(env, jni_proxy_host); aws_byte_buf_init_copy_from_cursor(&http_options->proxy_host_buf, aws_jni_get_allocator(), tmp_cursor); http_options->proxy_host_cursor = aws_byte_cursor_from_buf(&http_options->proxy_host_buf); aws_jni_byte_cursor_from_jstring_release(env, jni_proxy_host, tmp_cursor); http_options->options.host = http_options->proxy_host_cursor; } jint jni_proxy_port = (*env)->CallIntMethod(env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_port_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_port) { http_options->options.port = (uint32_t)jni_proxy_port; } jobject jni_proxy_tls_context = (*env)->CallObjectMethod( env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_tls_context_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_tls_context) { jlong jni_proxy_tls_context_long = (*env)->CallLongMethod(env, jni_proxy_tls_context, crt_resource_properties.get_native_handle_method_id); struct aws_tls_ctx *tls_ctx = (struct aws_tls_ctx *)jni_proxy_tls_context_long; if (tls_ctx) { aws_tls_connection_options_init_from_ctx(&java_client->http_proxy_tls_options, tls_ctx); aws_tls_connection_options_set_server_name( &java_client->http_proxy_tls_options, allocator, &http_options->options.host); http_options->options.tls_options = &java_client->http_proxy_tls_options; } } jobject jni_proxy_authorization_type = (*env)->CallObjectMethod( env, java_http_proxy_options, http_proxy_options_properties.proxy_get_proxy_authorization_type_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_authorization_type) { jint jni_proxy_authorization_type_value = (*env)->CallIntMethod( env, jni_proxy_authorization_type, http_proxy_connection_type_properties.proxy_get_value_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } http_options->options.auth_type = (enum aws_http_proxy_authentication_type)jni_proxy_authorization_type_value; } jstring jni_proxy_authorization_username = (jstring)(*env)->CallObjectMethod( env, java_http_proxy_options, http_proxy_options_properties.proxy_get_authorization_username_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_authorization_username) { // Get the data, copy it, and then release the JNI stuff struct aws_byte_cursor tmp_cursor = aws_jni_byte_cursor_from_jstring_acquire(env, jni_proxy_authorization_username); aws_byte_buf_init_copy_from_cursor( &http_options->authorization_username_buf, aws_jni_get_allocator(), tmp_cursor); http_options->authorization_username_cursor = aws_byte_cursor_from_buf(&http_options->authorization_username_buf); aws_jni_byte_cursor_from_jstring_release(env, jni_proxy_authorization_username, tmp_cursor); http_options->options.auth_username = http_options->authorization_username_cursor; } jstring jni_proxy_authorization_password = (jstring)(*env)->CallObjectMethod( env, java_http_proxy_options, http_proxy_options_properties.proxy_get_authorization_password_id); if (aws_jni_check_and_clear_exception(env)) { goto on_error; } if (jni_proxy_authorization_password) { // Get the data, copy it, and then release the JNI stuff struct aws_byte_cursor tmp_cursor = aws_jni_byte_cursor_from_jstring_acquire(env, jni_proxy_authorization_password); aws_byte_buf_init_copy_from_cursor( &http_options->authorization_password_buf, aws_jni_get_allocator(), tmp_cursor); http_options->authorization_password_cursor = aws_byte_cursor_from_buf(&http_options->authorization_password_buf); aws_jni_byte_cursor_from_jstring_release(env, jni_proxy_authorization_password, tmp_cursor); http_options->options.auth_password = http_options->authorization_password_cursor; } return http_options; on_error: s_aws_mqtt5_http_proxy_options_java_destroy(env, allocator, http_options); return NULL; } /******************************************************************************* * HELPER FUNCTIONS ******************************************************************************/ static void aws_mqtt5_client_java_destroy( JNIEnv *env, struct aws_allocator *allocator, struct aws_mqtt5_client_java_jni *java_client) { AWS_PRECONDITION(java_client); if (!java_client) { return; } AWS_LOGF_DEBUG(AWS_LS_MQTT5_CLIENT, "java_client=%p: Destroying MQTT5 client", (void *)java_client); if (java_client->jni_client) { (*env)->DeleteGlobalRef(env, java_client->jni_client); } if (java_client->jni_publish_events) { (*env)->DeleteGlobalRef(env, java_client->jni_publish_events); } if (java_client->jni_lifecycle_events) { (*env)->DeleteGlobalRef(env, java_client->jni_lifecycle_events); } aws_tls_connection_options_clean_up(&java_client->tls_options); aws_tls_connection_options_clean_up(&java_client->http_proxy_tls_options); /* Frees allocated memory */ aws_mem_release(allocator, java_client); } static void s_complete_future_with_exception(JNIEnv *env, jobject *future, int error_code) { if (!env || !future) { return; } jobject crt_exception = aws_jni_new_crt_exception_from_error_code(env, error_code); (*env)->CallBooleanMethod( env, *future, completable_future_properties.complete_exceptionally_method_id, crt_exception); aws_jni_check_and_clear_exception(env); (*env)->DeleteLocalRef(env, crt_exception); } static void s_aws_count_allocation(const void *pointer, size_t *counter) { if (pointer != NULL) { *counter += 1; } } static char s_client_string[] = "MQTT5 Client"; /******************************************************************************* * MQTT5 CALLBACK FUNCTIONS ******************************************************************************/ static void s_aws_mqtt5_client_java_lifecycle_event(const struct aws_mqtt5_client_lifecycle_event *event) { struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)event->user_data; if (!java_client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: invalid client"); return; } /********** JNI ENV ACQUIRE **********/ JavaVM *jvm = java_client->jvm; JNIEnv *env = aws_jni_acquire_thread_env(jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: could not get env"); return; } /* Calculate the number of references needed (1 is always needed for the return struct) */ size_t references_needed = 1; if (event->connack_data != NULL) { /* A ConnAck packet will need 2 references at minimum */ references_needed += 2; /* Optionals */ s_aws_count_allocation(event->connack_data->assigned_client_identifier, &references_needed); s_aws_count_allocation(event->connack_data->authentication_data, &references_needed); s_aws_count_allocation(event->connack_data->authentication_method, &references_needed); s_aws_count_allocation(event->connack_data->maximum_packet_size, &references_needed); s_aws_count_allocation(event->connack_data->maximum_qos, &references_needed); s_aws_count_allocation(event->connack_data->reason_string, &references_needed); s_aws_count_allocation(event->connack_data->receive_maximum, &references_needed); s_aws_count_allocation(event->connack_data->response_information, &references_needed); s_aws_count_allocation(event->connack_data->retain_available, &references_needed); s_aws_count_allocation(event->connack_data->server_keep_alive, &references_needed); s_aws_count_allocation(event->connack_data->server_reference, &references_needed); s_aws_count_allocation(event->connack_data->session_expiry_interval, &references_needed); s_aws_count_allocation(event->connack_data->shared_subscriptions_available, &references_needed); s_aws_count_allocation(event->connack_data->subscription_identifiers_available, &references_needed); s_aws_count_allocation(event->connack_data->topic_alias_maximum, &references_needed); s_aws_count_allocation(event->connack_data->wildcard_subscriptions_available, &references_needed); /* Add user properties */ references_needed += event->connack_data->user_property_count * 2; references_needed += 2; /* Add 2 for arrays to hold user properties */ } if (event->disconnect_data != NULL) { /* A Disconnect packet will need 1 reference at a minimum */ references_needed += 1; /* Optionals */ s_aws_count_allocation(event->disconnect_data->reason_string, &references_needed); s_aws_count_allocation(event->disconnect_data->server_reference, &references_needed); s_aws_count_allocation(event->disconnect_data->session_expiry_interval_seconds, &references_needed); /* Add user properties */ references_needed += event->disconnect_data->user_property_count * 2; references_needed += 2; /* Add 1 for array to hold user properties */ } if (event->settings != NULL) { /* Negotiated settings only need 2 references, one for the ClientID and another for the object */ references_needed += 2; } /* Make a local frame so we can clean memory */ jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed); if (local_frame_result != 0) { s_aws_mqtt5_client_log_and_throw_exception( env, "LifecycleEvent: could not push local JNI frame with 14 allocation minimum!", AWS_ERROR_INVALID_STATE); aws_jni_release_thread_env(jvm, env); return; } jobject connack_data = NULL; if (event->connack_data != NULL) { connack_data = s_aws_mqtt5_client_create_jni_connack_packet_from_native(env, event->connack_data); if (connack_data == NULL) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: creating ConnAck packet failed!"); goto clean_up; } } jobject disconnect_data = NULL; if (event->disconnect_data != NULL) { disconnect_data = s_aws_mqtt5_client_create_jni_disconnect_packet_from_native(env, event->disconnect_data); if (disconnect_data == NULL) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: creating Disconnect packet failed!"); goto clean_up; } } jobject negotiated_settings_data = NULL; if (event->settings != NULL) { negotiated_settings_data = s_aws_mqtt5_client_create_jni_negotiated_settings_from_native(env, event->settings); } jobject jni_lifecycle_events = java_client->jni_lifecycle_events; if (!jni_lifecycle_events) { s_aws_mqtt5_client_log_and_throw_exception( env, "LifecycleEvent: no lifecycle events found!", AWS_ERROR_INVALID_STATE); goto clean_up; } jobject java_lifecycle_return_data; switch (event->event_type) { case AWS_MQTT5_CLET_ATTEMPTING_CONNECT: /* Make the OnAttemptingConnectReturn struct */ java_lifecycle_return_data = (*env)->NewObject( env, mqtt5_on_attempting_connect_return_properties.return_class, mqtt5_on_attempting_connect_return_properties.return_constructor_id); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ (*env)->CallVoidMethod( env, jni_lifecycle_events, mqtt5_lifecycle_events_properties.lifecycle_attempting_connect_id, java_client->jni_client, java_lifecycle_return_data); break; case AWS_MQTT5_CLET_CONNECTION_SUCCESS: /* Make the OnConnectionSuccessReturn struct */ java_lifecycle_return_data = (*env)->NewObject( env, mqtt5_on_connection_success_return_properties.return_class, mqtt5_on_connection_success_return_properties.return_constructor_id, connack_data, negotiated_settings_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ /* Set OnConnected BEFORE calling the callback so it is accurate in the callback itself. */ (*env)->CallVoidMethod(env, java_client->jni_client, mqtt5_client_properties.client_set_is_connected, true); (*env)->CallVoidMethod( env, jni_lifecycle_events, mqtt5_lifecycle_events_properties.lifecycle_connection_success_id, java_client->jni_client, java_lifecycle_return_data); break; case AWS_MQTT5_CLET_CONNECTION_FAILURE: { jint error_code = (jint)event->error_code; /* Make the OnConnectionFailureReturn struct */ java_lifecycle_return_data = (*env)->NewObject( env, mqtt5_on_connection_failure_return_properties.return_class, mqtt5_on_connection_failure_return_properties.return_constructor_id, error_code, connack_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ (*env)->CallVoidMethod( env, jni_lifecycle_events, mqtt5_lifecycle_events_properties.lifecycle_connection_failure_id, java_client->jni_client, java_lifecycle_return_data); break; } case AWS_MQTT5_CLET_DISCONNECTION: { jint error_code = (jint)event->error_code; /* Make the OnDisconnectionReturn struct */ java_lifecycle_return_data = (*env)->NewObject( env, mqtt5_on_disconnection_return_properties.return_class, mqtt5_on_disconnection_return_properties.return_constructor_id, error_code, disconnect_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ /* Set OnConnected BEFORE calling the callback so it is accurate in the callback itself. */ (*env)->CallVoidMethod( env, java_client->jni_client, mqtt5_client_properties.client_set_is_connected, false); (*env)->CallVoidMethod( env, jni_lifecycle_events, mqtt5_lifecycle_events_properties.lifecycle_disconnection_id, java_client->jni_client, java_lifecycle_return_data); break; } case AWS_MQTT5_CLET_STOPPED: /* Make the OnStopped struct */ java_lifecycle_return_data = (*env)->NewObject( env, mqtt5_on_stopped_return_properties.return_class, mqtt5_on_stopped_return_properties.return_constructor_id); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ (*env)->CallVoidMethod( env, jni_lifecycle_events, mqtt5_lifecycle_events_properties.lifecycle_stopped_id, java_client->jni_client, java_lifecycle_return_data); break; default: AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "LifecycleEvent: unsupported event type: %i", event->event_type); } goto clean_up; clean_up: aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ (*env)->PopLocalFrame(env, NULL); /********** JNI ENV RELEASE **********/ aws_jni_release_thread_env(jvm, env); } static void s_aws_mqtt5_client_java_publish_received( const struct aws_mqtt5_packet_publish_view *publish, void *user_data) { struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)user_data; if (!java_client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "publishReceived function: invalid client"); return; } if (!publish) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "publishReceived function: invalid publish packet"); return; } /********** JNI ENV ACQUIRE **********/ JavaVM *jvm = java_client->jvm; JNIEnv *env = aws_jni_acquire_thread_env(jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "publishReceived function: could not get env"); return; } /* Calculate the number of references needed */ size_t references_needed = 0; { /* One reference is needed for the PublishReturn */ references_needed += 1; /* A Publish packet will need 5 references at minimum */ references_needed += 5; /* Optionals */ s_aws_count_allocation(publish->content_type, &references_needed); s_aws_count_allocation(publish->correlation_data, &references_needed); s_aws_count_allocation(publish->message_expiry_interval_seconds, &references_needed); s_aws_count_allocation(publish->response_topic, &references_needed); s_aws_count_allocation(publish->topic_alias, &references_needed); s_aws_count_allocation(publish->payload_format, &references_needed); /* Add user properties and subscription identifiers */ references_needed += publish->user_property_count * 2; references_needed += 1; /* Add 1 for array to hold user properties */ if (publish->subscription_identifier_count > 0) { references_needed += publish->subscription_identifier_count; references_needed += 1; /* Add 1 for array */ } } /** * Push a new local frame so any local references we make are tied to it. Then we can pop it to free memory. */ jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed); if (local_frame_result != 0) { s_aws_mqtt5_client_log_and_throw_exception( env, "publishReceived function: could not push local JNI frame with 12 allocation minimum!", AWS_ERROR_INVALID_STATE); goto clean_up; } /* The return result */ jobject publish_packet_return_data; /* Make the PublishPacket */ jobject publish_packet_data = s_aws_mqtt5_client_create_jni_publish_packet_from_native(env, publish); if (publish_packet_data == NULL) { goto clean_up; } /* Make the PublishReturn struct that will hold all of the data that is passed to Java */ publish_packet_return_data = (*env)->NewObject( env, mqtt5_publish_return_properties.return_class, mqtt5_publish_return_properties.return_constructor_id, publish_packet_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ if (java_client->jni_publish_events) { (*env)->CallVoidMethod( env, java_client->jni_publish_events, mqtt5_publish_events_properties.publish_events_publish_received_id, java_client->jni_client, publish_packet_return_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ } goto clean_up; clean_up: (*env)->PopLocalFrame(env, NULL); /********** JNI ENV RELEASE **********/ aws_jni_release_thread_env(jvm, env); } static void s_aws_mqtt5_client_java_publish_callback_destructor( JNIEnv *env, struct aws_mqtt5_client_publish_return_data *callback_return_data) { struct aws_allocator *allocator = aws_jni_get_allocator(); if (callback_return_data != NULL) { if (callback_return_data->jni_publish_future && env != NULL) { (*env)->DeleteGlobalRef(env, callback_return_data->jni_publish_future); } aws_mem_release(allocator, callback_return_data); } } static void s_aws_mqtt5_client_java_publish_completion( enum aws_mqtt5_packet_type packet_type, const void *packet, int error_code, void *user_data) { int exception_error_code = error_code; JavaVM *jvm = NULL; JNIEnv *env = NULL; bool has_pushed_frame = false; struct aws_mqtt5_client_publish_return_data *return_data = (struct aws_mqtt5_client_publish_return_data *)user_data; if (!return_data) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: invalid return data!"); return; } struct aws_mqtt5_client_java_jni *java_client = return_data->java_client; if (!java_client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: invalid client"); goto clean_up; } /********** JNI ENV ACQUIRE **********/ jvm = java_client->jvm; env = aws_jni_acquire_thread_env(jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: could not get env"); goto clean_up; } /* Get the future for this specific publish and complete right away if there is an error */ jobject jni_publish_future = return_data->jni_publish_future; if (error_code != AWS_OP_SUCCESS) { goto exception; } /* If this result is supposed to have a packet and does not, then error right away */ if (packet_type != AWS_MQTT5_PT_NONE && packet == NULL) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: packet type but no packet!"); goto exception; } /* Calculate the number of references needed */ size_t references_needed = 0; if (packet_type == AWS_MQTT5_PT_PUBACK) { struct aws_mqtt5_packet_puback_view *puback_packet = (struct aws_mqtt5_packet_puback_view *)packet; /* A PubAck packet will need 2 references at minimum */ references_needed += 2; /* Optionals */ s_aws_count_allocation(puback_packet->reason_string, &references_needed); /* Add user properties */ references_needed += puback_packet->user_property_count * 2; references_needed += 1; /* Add 1 for array to hold user properties */ } /** * Push a new local frame so any local references we make are tied to it. Then we can pop it to free memory. */ jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed); if (local_frame_result != 0) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "PublishCompletion function: could not push local JNI frame with 12 allocation minimum"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } has_pushed_frame = true; /* The result */ jobject publish_packet_result_data; if (packet_type == AWS_MQTT5_PT_NONE) { /* QoS 0 */ publish_packet_result_data = (*env)->NewObject( env, mqtt5_publish_result_properties.result_class, mqtt5_publish_result_properties.result_constructor_id); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ } else if (packet_type == AWS_MQTT5_PT_PUBACK) { /* QoS 1 */ struct aws_mqtt5_packet_puback_view *puback_packet = (struct aws_mqtt5_packet_puback_view *)packet; /* Make the PubAck packet */ jobject puback_packet_data = s_aws_mqtt5_client_create_jni_puback_packet_from_native(env, puback_packet); if (puback_packet_data == NULL) { exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } /* Make the result and populate it with data made above */ publish_packet_result_data = (*env)->NewObject( env, mqtt5_publish_result_properties.result_class, mqtt5_publish_result_properties.result_puback_constructor_id, puback_packet_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ } else { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function called with unknown packet type!"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } /* Complete the future */ (*env)->CallBooleanMethod( env, jni_publish_future, completable_future_properties.complete_method_id, publish_packet_result_data); if (aws_jni_check_and_clear_exception(env)) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "PublishCompletion function: exception when completing future"); goto exception; } goto clean_up; exception: s_complete_future_with_exception(env, &jni_publish_future, exception_error_code); goto clean_up; clean_up: s_aws_mqtt5_client_java_publish_callback_destructor(env, return_data); if (env != NULL) { if (has_pushed_frame) { (*env)->PopLocalFrame(env, NULL); } /********** JNI ENV RELEASE **********/ aws_jni_release_thread_env(jvm, env); } return; } static void s_aws_mqtt5_client_java_subscribe_callback_destructor( JNIEnv *env, struct aws_mqtt5_client_subscribe_return_data *callback_return_data) { struct aws_allocator *allocator = aws_jni_get_allocator(); if (callback_return_data != NULL) { if (callback_return_data->jni_subscribe_future && env != NULL) { (*env)->DeleteGlobalRef(env, callback_return_data->jni_subscribe_future); } aws_mem_release(allocator, callback_return_data); } } static void s_aws_mqtt5_client_java_subscribe_completion( const struct aws_mqtt5_packet_suback_view *suback, int error_code, void *user_data) { int exception_error_code = error_code; JNIEnv *env = NULL; JavaVM *jvm = NULL; bool has_pushed_frame = false; struct aws_mqtt5_client_subscribe_return_data *return_data = (struct aws_mqtt5_client_subscribe_return_data *)user_data; if (!return_data) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: invalid return data!"); return; } struct aws_mqtt5_client_java_jni *java_client = return_data->java_client; if (!java_client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: invalid client"); goto clean_up; } /********** JNI ENV ACQUIRE **********/ jvm = java_client->jvm; env = aws_jni_acquire_thread_env(jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: could not get env"); goto clean_up; } /* Get the future for this specific subscribe */ jobject jni_subscribe_future = return_data->jni_subscribe_future; if (error_code != AWS_OP_SUCCESS) { exception_error_code = error_code; goto exception; } /* Calculate the number of references needed */ size_t references_needed = 0; if (suback != NULL) { /* A SubAck packet will need 1 references at minimum */ references_needed += 1; /* Optionals */ s_aws_count_allocation(suback->reason_string, &references_needed); /* Add user properties and reason codes */ references_needed += (suback->user_property_count) * 2; references_needed += 1; /* Add 1 for arrays to hold user properties */ if (suback->reason_code_count > 0) { references_needed += suback->reason_code_count; references_needed += 1; /* Add 1 for arrays to hold reason codes */ } } /** * Push a new local frame so any local references we make are tied to it. Then we can pop it to free memory. */ jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed); if (local_frame_result != 0) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "SubscribeCompletion: could not push local JNI frame with 4 allocation minimum"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } has_pushed_frame = true; /* The SubAck to return (if present) */ jobject suback_packet_data = NULL; if (suback != NULL) { suback_packet_data = (*env)->NewObject( env, mqtt5_suback_packet_properties.suback_packet_class, mqtt5_suback_packet_properties.suback_constructor_id); if (s_set_jni_string_field_in_packet( env, suback->reason_string, suback_packet_data, mqtt5_suback_packet_properties.suback_reason_string_field_id, "reason string", true) != AWS_OP_SUCCESS) { goto clean_up; } if (suback->reason_codes != NULL) { if (suback->reason_code_count > 0) { for (size_t i = 0; i < suback->reason_code_count; ++i) { const enum aws_mqtt5_suback_reason_code *reason_code_data = &suback->reason_codes[i]; if (s_set_int_enum_in_packet( env, (int *)reason_code_data, suback_packet_data, mqtt5_suback_packet_properties.suback_native_add_suback_code_id, false) != AWS_OP_SUCCESS) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "Error when creating SubAckPacket from native: Could not set reason code"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } } } } if (s_set_user_properties_field( env, suback->user_property_count, suback->user_properties, suback_packet_data, mqtt5_suback_packet_properties.suback_user_properties_field_id) == AWS_OP_ERR) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "When creating PubAckPacket from native could not add user property!"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } } /* Complete the promise */ (*env)->CallBooleanMethod( env, jni_subscribe_future, completable_future_properties.complete_method_id, suback_packet_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ goto clean_up; exception: s_complete_future_with_exception(env, &jni_subscribe_future, exception_error_code); goto clean_up; clean_up: s_aws_mqtt5_client_java_subscribe_callback_destructor(env, return_data); if (env != NULL) { if (has_pushed_frame) { (*env)->PopLocalFrame(env, NULL); } /********** JNI ENV RELEASE **********/ aws_jni_release_thread_env(jvm, env); } } static void s_aws_mqtt5_client_java_unsubscribe_callback_destructor( JNIEnv *env, struct aws_mqtt5_client_unsubscribe_return_data *callback_return_data) { struct aws_allocator *allocator = aws_jni_get_allocator(); if (callback_return_data != NULL) { if (callback_return_data->jni_unsubscribe_future && env != NULL) { (*env)->DeleteGlobalRef(env, callback_return_data->jni_unsubscribe_future); } aws_mem_release(allocator, callback_return_data); } } static void s_aws_mqtt5_client_java_unsubscribe_completion( const struct aws_mqtt5_packet_unsuback_view *unsuback, int error_code, void *user_data) { int exception_error_code = error_code; JNIEnv *env = NULL; JavaVM *jvm = NULL; bool has_pushed_frame = false; struct aws_mqtt5_client_unsubscribe_return_data *return_data = (struct aws_mqtt5_client_unsubscribe_return_data *)user_data; if (!return_data) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: invalid return data!"); return; } struct aws_mqtt5_client_java_jni *java_client = return_data->java_client; if (!java_client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: invalid client"); goto clean_up; } /********** JNI ENV ACQUIRE **********/ jvm = java_client->jvm; env = aws_jni_acquire_thread_env(jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: could not get env"); goto clean_up; } /* Get the future for this specific unsubscribe */ jobject jni_unsubscribe_future = return_data->jni_unsubscribe_future; /* Calculate the number of references needed */ size_t references_needed = 0; if (unsuback != NULL) { /* A UnsubAck packet will need 1 reference at minimum */ references_needed += 1; /* Optionals */ s_aws_count_allocation(unsuback->reason_string, &references_needed); /* Add user properties and reason codes */ references_needed += (unsuback->user_property_count) * 2; references_needed += 1; /* Add 1 for array to hold user properties */ if (unsuback->reason_code_count > 0) { references_needed += unsuback->reason_code_count; references_needed += 1; /* Add 1 for array to hold reason codes */ } } /** * Push a new local frame so any local allocations we make are tied to it. Then we can pop it to free memory. */ jint local_frame_result = (*env)->PushLocalFrame(env, (jint)references_needed); if (local_frame_result != 0) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "UnsubscribeCompletion: could not push local JNI frame with 4 allocation minimum"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } has_pushed_frame = true; if (error_code != AWS_OP_SUCCESS) { exception_error_code = error_code; goto exception; } jobject unsuback_packet_data = (*env)->NewObject( env, mqtt5_unsuback_packet_properties.unsuback_packet_class, mqtt5_unsuback_packet_properties.unsuback_constructor_id); if (s_set_jni_string_field_in_packet( env, unsuback->reason_string, unsuback_packet_data, mqtt5_unsuback_packet_properties.unsuback_reason_string_field_id, "reason string", true) != AWS_OP_SUCCESS) { goto clean_up; } if (unsuback->reason_codes) { if (unsuback->reason_code_count > 0) { for (size_t i = 0; i < unsuback->reason_code_count; ++i) { const enum aws_mqtt5_unsuback_reason_code *reason_code_data = &unsuback->reason_codes[i]; if (s_set_int_enum_in_packet( env, (int *)reason_code_data, unsuback_packet_data, mqtt5_unsuback_packet_properties.unsuback_native_add_unsuback_code_id, false) != AWS_OP_SUCCESS) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "Error when creating UnsubAckPacket from native: Could not set reason code"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } } } } if (s_set_user_properties_field( env, unsuback->user_property_count, unsuback->user_properties, unsuback_packet_data, mqtt5_unsuback_packet_properties.unsuback_user_properties_field_id) == AWS_OP_ERR) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "When creating UnsubAckPacket from native could not add user property!"); exception_error_code = AWS_ERROR_INVALID_STATE; goto exception; } /* Complete the promise */ (*env)->CallBooleanMethod( env, jni_unsubscribe_future, completable_future_properties.complete_method_id, unsuback_packet_data); aws_jni_check_and_clear_exception(env); /* To hide JNI warning */ goto clean_up; exception: s_complete_future_with_exception(env, &jni_unsubscribe_future, exception_error_code); goto clean_up; clean_up: s_aws_mqtt5_client_java_unsubscribe_callback_destructor(env, return_data); if (env != NULL) { if (has_pushed_frame) { (*env)->PopLocalFrame(env, NULL); } /********** JNI ENV RELEASE **********/ aws_jni_release_thread_env(jvm, env); } } static void s_aws_mqtt5_client_java_termination(void *complete_ctx) { struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)complete_ctx; if (!java_client) { AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "MQTT5 client termination function in JNI called, but with invalid java_client"); return; } /********** JNI ENV ACQUIRE **********/ JavaVM *jvm = java_client->jvm; JNIEnv *env = aws_jni_acquire_thread_env(jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "MQTT5 client termination function in JNI called, but could not get env"); return; } (*env)->CallVoidMethod(env, java_client->jni_client, crt_resource_properties.release_references); java_client->client = NULL; struct aws_allocator *allocator = aws_jni_get_allocator(); aws_mqtt5_client_java_destroy(env, allocator, java_client); /********** JNI ENV RELEASE **********/ aws_jni_release_thread_env(jvm, env); } /******************************************************************************* * MQTT5 CLIENT FUNCTIONS ******************************************************************************/ JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalStart( JNIEnv *env, jclass jni_class, jlong jni_client) { (void)jni_class; aws_cache_jni_ids(env); struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.start: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return; } if (!java_client->client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.start: Invalid/null native client", AWS_ERROR_INVALID_ARGUMENT); return; } int return_result = aws_mqtt5_client_start(java_client->client); if (return_result != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.start: aws_mqtt5_client_start returned a non AWS_OP_SUCCESS code!", aws_last_error()); } } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalStop( JNIEnv *env, jclass jni_class, jlong jni_client, jobject jni_disconnect_packet) { (void)jni_class; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.stop: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return; } if (!java_client->client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.stop: Invalid/null native client", AWS_ERROR_INVALID_ARGUMENT); return; } struct aws_mqtt5_packet_disconnect_view_java_jni *java_disconnect_packet = NULL; int return_result = AWS_OP_ERR; if (jni_disconnect_packet) { java_disconnect_packet = aws_mqtt5_packet_disconnect_view_create_from_java(env, allocator, jni_disconnect_packet); if (!java_disconnect_packet) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.stop: Invalid/null disconnect packet", aws_last_error()); goto clean_up; } } return_result = aws_mqtt5_client_stop( java_client->client, aws_mqtt5_packet_disconnect_view_get_packet(java_disconnect_packet), NULL); if (return_result != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.stop: aws_mqtt5_client_stop returned a non AWS_OP_SUCCESS code!", return_result); } goto clean_up; clean_up: if (java_disconnect_packet) { aws_mqtt5_packet_disconnect_view_java_destroy(env, allocator, java_disconnect_packet); } return; } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalPublish( JNIEnv *env, jclass jni_class, jlong jni_client, jobject jni_publish_packet, jobject jni_publish_future) { (void)jni_class; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct aws_mqtt5_packet_publish_view_java_jni *java_publish_packet = NULL; struct aws_mqtt5_client_publish_return_data *return_data = NULL; int error_code = 0; struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.publish: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return; } if (!jni_publish_future) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.publish: Invalid/null publish future", AWS_ERROR_INVALID_ARGUMENT); return; } if (!java_client->client) { error_code = AWS_ERROR_INVALID_ARGUMENT; AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Invalid/null native client"); goto exception; } if (!jni_publish_packet) { error_code = AWS_ERROR_INVALID_ARGUMENT; AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Invalid/Null publish packet!"); goto exception; } /* Cannot fail */ return_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_publish_return_data)); return_data->java_client = java_client; return_data->jni_publish_future = (*env)->NewGlobalRef(env, jni_publish_future); struct aws_mqtt5_publish_completion_options completion_options = { .completion_callback = &s_aws_mqtt5_client_java_publish_completion, .completion_user_data = return_data, }; java_publish_packet = aws_mqtt5_packet_publish_view_create_from_java(env, allocator, jni_publish_packet); if (!java_publish_packet) { error_code = aws_last_error(); AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Could not create native publish packet!"); goto exception; } int return_result = aws_mqtt5_client_publish( java_client->client, aws_mqtt5_packet_publish_view_get_packet(java_publish_packet), &completion_options); if (return_result != AWS_OP_SUCCESS) { error_code = aws_last_error(); AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "Mqtt5Client.publish: Could not publish packet! Error code: %i", return_result); goto exception; } goto clean_up; exception: s_complete_future_with_exception( env, &jni_publish_future, (error_code == AWS_ERROR_SUCCESS) ? AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE : error_code); if (java_publish_packet) { aws_mqtt5_packet_publish_view_java_destroy(env, allocator, java_publish_packet); } if (return_data) { s_aws_mqtt5_client_java_publish_callback_destructor(env, return_data); } return; clean_up: if (java_publish_packet) { aws_mqtt5_packet_publish_view_java_destroy(env, allocator, java_publish_packet); } } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalSubscribe( JNIEnv *env, jclass jni_class, jlong jni_client, jobject jni_subscribe_packet, jobject jni_subscribe_future) { (void)jni_class; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct aws_mqtt5_client_subscribe_return_data *return_data = NULL; struct aws_mqtt5_packet_subscribe_view_java_jni *java_subscribe_packet = NULL; int error_code = AWS_ERROR_SUCCESS; struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.subscribe: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return; } if (!jni_subscribe_future) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.subscribe: Invalid/null subscribe future", AWS_ERROR_INVALID_ARGUMENT); return; } if (!java_client->client) { error_code = AWS_ERROR_INVALID_ARGUMENT; AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Invalid/null native client"); goto exception; } if (!jni_subscribe_packet) { error_code = AWS_ERROR_INVALID_ARGUMENT; AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Invalid/Null subscribe packet!"); goto exception; } /* Cannot fail */ return_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_subscribe_return_data)); return_data->java_client = java_client; return_data->jni_subscribe_future = (*env)->NewGlobalRef(env, jni_subscribe_future); struct aws_mqtt5_subscribe_completion_options completion_options = { .completion_callback = &s_aws_mqtt5_client_java_subscribe_completion, .completion_user_data = return_data, }; java_subscribe_packet = aws_mqtt5_packet_subscribe_view_create_from_java(env, allocator, jni_subscribe_packet); if (java_subscribe_packet == NULL) { error_code = aws_last_error(); AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Could not create native subscribe packet!"); goto exception; } int return_result = aws_mqtt5_client_subscribe( java_client->client, aws_mqtt5_packet_subscribe_view_get_packet(java_subscribe_packet), &completion_options); if (return_result != AWS_OP_SUCCESS) { error_code = aws_last_error(); AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.subscribe: Subscribe failed! Error code: %i", return_result); goto exception; } goto clean_up; exception: s_complete_future_with_exception( env, &jni_subscribe_future, (error_code == AWS_ERROR_SUCCESS) ? AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE : error_code); if (java_subscribe_packet) { aws_mqtt5_packet_subscribe_view_java_destroy(env, allocator, java_subscribe_packet); } if (return_data) { s_aws_mqtt5_client_java_subscribe_callback_destructor(env, return_data); } return; clean_up: if (java_subscribe_packet) { aws_mqtt5_packet_subscribe_view_java_destroy(env, allocator, java_subscribe_packet); } } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalUnsubscribe( JNIEnv *env, jclass jni_class, jlong jni_client, jobject jni_unsubscribe_packet, jobject jni_unsubscribe_future) { (void)jni_class; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct aws_mqtt5_client_unsubscribe_return_data *return_data = NULL; struct aws_mqtt5_packet_unsubscribe_view_java_jni *java_unsubscribe_packet = NULL; int error_code = AWS_ERROR_SUCCESS; struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.unsubscribe: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return; } if (!jni_unsubscribe_future) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.unsubscribe: Invalid/null unsubscribe future", AWS_ERROR_INVALID_ARGUMENT); return; } if (!java_client->client) { error_code = AWS_ERROR_INVALID_ARGUMENT; AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Invalid/null native client"); goto exception; } if (!jni_unsubscribe_packet) { error_code = AWS_ERROR_INVALID_ARGUMENT; AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Invalid/Null unsubscribe packet!"); goto exception; } /* Cannot fail */ return_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_unsubscribe_return_data)); return_data->java_client = java_client; return_data->jni_unsubscribe_future = (*env)->NewGlobalRef(env, jni_unsubscribe_future); struct aws_mqtt5_unsubscribe_completion_options completion_options = { .completion_callback = &s_aws_mqtt5_client_java_unsubscribe_completion, .completion_user_data = return_data, }; java_unsubscribe_packet = aws_mqtt5_packet_unsubscribe_view_create_from_java(env, allocator, jni_unsubscribe_packet); if (!java_unsubscribe_packet) { error_code = aws_last_error(); AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Could not create native unsubscribe packet!"); goto exception; } int return_result = aws_mqtt5_client_unsubscribe( java_client->client, aws_mqtt5_packet_unsubscribe_view_get_packet(java_unsubscribe_packet), &completion_options); if (return_result != AWS_OP_SUCCESS) { error_code = aws_last_error(); AWS_LOGF_ERROR( AWS_LS_MQTT5_CLIENT, "Mqtt5Client.unsubscribe: Unsubscribe failed! Error code: %i", return_result); goto exception; } goto clean_up; exception: s_complete_future_with_exception( env, &jni_unsubscribe_future, (error_code == AWS_ERROR_SUCCESS) ? AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE : error_code); if (java_unsubscribe_packet) { aws_mqtt5_packet_unsubscribe_view_java_destroy(env, allocator, java_unsubscribe_packet); } if (return_data) { s_aws_mqtt5_client_java_unsubscribe_callback_destructor(env, return_data); } return; clean_up: if (java_unsubscribe_packet) { aws_mqtt5_packet_unsubscribe_view_java_destroy(env, allocator, java_unsubscribe_packet); } } JNIEXPORT jobject JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientInternalGetOperationStatistics( JNIEnv *env, jclass jni_class, jlong jni_client) { (void)jni_class; aws_cache_jni_ids(env); struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.getOperationStatistics: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return NULL; } if (!java_client->client) { s_aws_mqtt5_client_log_and_throw_exception( env, "Mqtt5Client.getOperationStatistics: Invalid/null native client", AWS_ERROR_INVALID_ARGUMENT); return NULL; } /* Construct Java object */ jobject jni_operation_statistics = (*env)->NewObject( env, mqtt5_client_operation_statistics_properties.statistics_class, mqtt5_client_operation_statistics_properties.statistics_constructor_id); if (jni_operation_statistics == NULL) { return NULL; } struct aws_mqtt5_client_operation_statistics client_stats; aws_mqtt5_client_get_stats(java_client->client, &client_stats); (*env)->SetLongField( env, jni_operation_statistics, mqtt5_client_operation_statistics_properties.incomplete_operation_count_field_id, (jlong)client_stats.incomplete_operation_count); if (aws_jni_check_and_clear_exception(env)) { aws_raise_error(AWS_ERROR_INVALID_STATE); aws_jni_throw_runtime_exception( env, "Mqtt5Client.getOperationStatistics: could not create incomplete operation count"); return NULL; } (*env)->SetLongField( env, jni_operation_statistics, mqtt5_client_operation_statistics_properties.incomplete_operation_size_field_id, (jlong)client_stats.incomplete_operation_size); if (aws_jni_check_and_clear_exception(env)) { aws_raise_error(AWS_ERROR_INVALID_STATE); aws_jni_throw_runtime_exception( env, "Mqtt5Client.getOperationStatistics: could not create incomplete operation size"); return NULL; } (*env)->SetLongField( env, jni_operation_statistics, mqtt5_client_operation_statistics_properties.unacked_operation_count_field_id, (jlong)client_stats.unacked_operation_count); if (aws_jni_check_and_clear_exception(env)) { aws_raise_error(AWS_ERROR_INVALID_STATE); aws_jni_throw_runtime_exception( env, "Mqtt5Client.getOperationStatistics: could not create unacked operation count"); return NULL; } (*env)->SetLongField( env, jni_operation_statistics, mqtt5_client_operation_statistics_properties.unacked_operation_size_field_id, (jlong)client_stats.unacked_operation_size); if (aws_jni_check_and_clear_exception(env)) { aws_raise_error(AWS_ERROR_INVALID_STATE); aws_jni_throw_runtime_exception( env, "Mqtt5Client.getOperationStatistics: could not create unacked operation size"); return NULL; } return jni_operation_statistics; } /******************************************************************************* * WEBSOCKET FUNCTIONS ******************************************************************************/ struct mqtt5_jni_ws_handshake { struct aws_mqtt5_client_java_jni *java_client; struct aws_http_message *http_request; aws_mqtt5_transform_websocket_handshake_complete_fn *complete_fn; void *complete_ctx; struct aws_allocator *allocator; }; static void s_ws_handshake_destroy(struct mqtt5_jni_ws_handshake *ws_handshake) { if (!ws_handshake) { return; } aws_mem_release(ws_handshake->allocator, ws_handshake); } static void 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) { struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)user_data; if (!java_client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Websocket handshake function in JNI called without valid client"); return; } if (!java_client->jni_client || !java_client->client) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Websocket handshake function in JNI called with already freed client"); return; } /********** JNI ENV ACQUIRE **********/ JNIEnv *env = aws_jni_acquire_thread_env(java_client->jvm); if (env == NULL) { /* If we can't get an environment, then the JVM is probably shutting down. Don't crash. */ complete_fn(request, AWS_ERROR_INVALID_STATE, complete_ctx); return; } struct aws_allocator *alloc = aws_jni_get_allocator(); /* Cannot fail */ struct mqtt5_jni_ws_handshake *ws_handshake = aws_mem_calloc(alloc, 1, sizeof(struct mqtt5_jni_ws_handshake)); ws_handshake->java_client = java_client; ws_handshake->complete_ctx = complete_ctx; ws_handshake->complete_fn = complete_fn; ws_handshake->http_request = request; ws_handshake->allocator = alloc; jobject java_http_request = aws_java_http_request_from_native(env, request, NULL); if (!java_http_request) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Could not create a HttpRequest for Java in MQTT5 client"); aws_raise_error(AWS_ERROR_UNKNOWN); goto error; } if (java_client->jni_client) { jobject jni_client = java_client->jni_client; (*env)->CallVoidMethod( env, jni_client, mqtt5_client_properties.client_on_websocket_handshake_id, java_http_request, ws_handshake); AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env)); } (*env)->DeleteLocalRef(env, java_http_request); aws_jni_release_thread_env(java_client->jvm, env); /********** JNI ENV RELEASE SUCCESS PATH **********/ return; error:; int error_code = aws_last_error(); s_ws_handshake_destroy(ws_handshake); complete_fn(request, error_code, complete_ctx); aws_jni_release_thread_env(java_client->jvm, env); /********** JNI ENV RELEASE FAILURE PATH **********/ } JNIEXPORT void JNICALL 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) { (void)jni_class; (void)jni_connection; aws_cache_jni_ids(env); struct mqtt5_jni_ws_handshake *ws_handshake = (void *)jni_user_data; int error_code = AWS_ERROR_SUCCESS; if (!ws_handshake) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Websocket handshake complete function in JNI called without handshake"); return; } if (jni_throwable != NULL) { if ((*env)->IsInstanceOf(env, jni_throwable, crt_runtime_exception_properties.crt_runtime_exception_class)) { error_code = (*env)->GetIntField(env, jni_throwable, crt_runtime_exception_properties.error_code_field_id); } if (error_code == AWS_ERROR_SUCCESS) { error_code = AWS_ERROR_UNKNOWN; /* is there anything more that could be done here? */ } goto done; } if (aws_apply_java_http_request_changes_to_native_request( env, jni_marshalled_request, NULL, ws_handshake->http_request)) { error_code = aws_last_error(); goto done; } done: ws_handshake->complete_fn(ws_handshake->http_request, error_code, ws_handshake->complete_ctx); s_ws_handshake_destroy(ws_handshake); } static int s_initialize_topic_aliasing_options( JNIEnv *env, struct aws_mqtt5_client_topic_alias_options *topic_aliasing_options, jobject jni_topic_aliasing_options) { jobject jni_outbound_behavior = (*env)->GetObjectField( env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.outbound_behavior_field_id); if (jni_outbound_behavior != NULL) { jint enum_value = (*env)->CallIntMethod( env, jni_outbound_behavior, mqtt5_outbound_topic_alias_behavior_type_properties.get_value_method_id); if (aws_jni_check_and_clear_exception(env)) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Error getting native value from OutboundTopicAliasBehaviorType"); return aws_raise_error(AWS_ERROR_INVALID_STATE); } topic_aliasing_options->outbound_topic_alias_behavior = (enum aws_mqtt5_client_outbound_topic_alias_behavior_type)enum_value; } jobject jni_outbound_cache_max_size = (*env)->GetObjectField( env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.outbound_cache_max_size_field_id); if (jni_outbound_cache_max_size != NULL) { jint int_value = (*env)->CallIntMethod(env, jni_outbound_cache_max_size, boxed_integer_properties.integer_get_value_id); if (int_value < 0 || int_value > UINT16_MAX) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Invalid outbound cache size value: %d", int_value); return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } topic_aliasing_options->outbound_alias_cache_max_size = (uint16_t)int_value; } jobject jni_inbound_behavior = (*env)->GetObjectField( env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.inbound_behavior_field_id); if (jni_inbound_behavior != NULL) { jint enum_value = (*env)->CallIntMethod( env, jni_inbound_behavior, mqtt5_inbound_topic_alias_behavior_type_properties.get_value_method_id); if (aws_jni_check_and_clear_exception(env)) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Error getting native value from InboundTopicAliasBehaviorType"); return aws_raise_error(AWS_ERROR_INVALID_STATE); } topic_aliasing_options->inbound_topic_alias_behavior = (enum aws_mqtt5_client_inbound_topic_alias_behavior_type)enum_value; } jobject jni_inbound_cache_max_size = (*env)->GetObjectField( env, jni_topic_aliasing_options, mqtt5_topic_aliasing_options_properties.inbound_cache_max_size_field_id); if (jni_inbound_cache_max_size != NULL) { jint int_value = (*env)->CallIntMethod(env, jni_inbound_cache_max_size, boxed_integer_properties.integer_get_value_id); if (int_value < 0 || int_value > UINT16_MAX) { AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Invalid inbound cache size value: %d", int_value); return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } topic_aliasing_options->inbound_alias_cache_size = (uint16_t)int_value; } return AWS_OP_SUCCESS; } /******************************************************************************* * JNI FUNCTIONS ******************************************************************************/ /* Create and Destroy **************************************/ JNIEXPORT jlong JNICALL 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) { (void)jni_class; aws_cache_jni_ids(env); struct aws_allocator *allocator = aws_jni_get_allocator(); struct aws_mqtt5_packet_connect_view_java_jni *connect_options = NULL; struct aws_mqtt5_client_options client_options; AWS_ZERO_STRUCT(client_options); struct aws_http_proxy_options_java_jni *java_http_proxy_options = NULL; struct aws_byte_buf host_name_buf; /* Needed to track if optionals are set or not */ bool was_value_set = false; /** * Push a new local frame so any local allocations we make are tied to it. Then we can pop it to free memory. * In Java JNI allocations here, we have 21 allocations so we need at least that many. * It should expand if we use more. * (NOTE: We cannot get the exact here because we are pulling from Java objects and we have no way to know how many * that will need) */ jint local_frame_result = (*env)->PushLocalFrame(env, (jint)21); if (local_frame_result != 0) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: could not push local JNI frame with 21 allocation minimum", AWS_ERROR_INVALID_STATE); return (jlong)NULL; } struct aws_mqtt5_client_java_jni *java_client = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_client_java_jni)); AWS_LOGF_DEBUG(AWS_LS_MQTT5_CLIENT, "java_client=%p: Initializing MQTT5 client", (void *)java_client); if (java_client == NULL) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: could not initialize new client", AWS_ERROR_INVALID_STATE); goto clean_up; } if (aws_get_string_from_jobject( env, jni_options, mqtt5_client_options_properties.options_host_name_field_id, s_client_string, "Host Name", &host_name_buf, &client_options.host_name, false, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get host name from options", AWS_ERROR_INVALID_STATE); goto clean_up; } uint32_t port = 0; if (aws_get_uint32_from_jobject( env, jni_options, mqtt5_client_options_properties.options_port_field_id, s_client_string, "port", &port, false, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get port from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.port = port; } if (!jni_bootstrap) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: no bootstrap found", AWS_ERROR_INVALID_ARGUMENT); goto clean_up; } jlong jni_bootstrap_pointer = (*env)->CallLongMethod(env, jni_bootstrap, crt_resource_properties.get_native_handle_method_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: could not get native handle for bootstrap", AWS_ERROR_INVALID_ARGUMENT); goto clean_up; } struct aws_client_bootstrap *bootstrap = (struct aws_client_bootstrap *)jni_bootstrap_pointer; client_options.bootstrap = bootstrap; struct aws_socket_options *socket_options = NULL; struct aws_socket_options tmp_socket_options; jobject jni_socket_options = (*env)->CallObjectMethod(env, jni_options, mqtt5_client_options_properties.options_get_socket_options_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting socket options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (jni_socket_options) { jlong jni_socket_options_pointer = (*env)->CallLongMethod(env, jni_socket_options, crt_resource_properties.get_native_handle_method_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: could not get native handle for socket options", AWS_ERROR_INVALID_ARGUMENT); goto clean_up; } socket_options = (struct aws_socket_options *)jni_socket_options_pointer; } if (socket_options == NULL) { tmp_socket_options.type = AWS_SOCKET_STREAM; tmp_socket_options.domain = AWS_SOCKET_IPV4; tmp_socket_options.connect_timeout_ms = 10000; client_options.socket_options = &tmp_socket_options; } else { client_options.socket_options = socket_options; } jobject jni_tls_options = (*env)->CallObjectMethod(env, jni_options, mqtt5_client_options_properties.options_get_tls_options_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting tls options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (jni_tls_options) { jlong jni_tls_pointer = (*env)->CallLongMethod(env, jni_tls_options, crt_resource_properties.get_native_handle_method_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: could not get native handle for tls options", AWS_ERROR_INVALID_ARGUMENT); goto clean_up; } struct aws_tls_ctx *tls_ctx = (struct aws_tls_ctx *)jni_tls_pointer; if (tls_ctx) { aws_tls_connection_options_init_from_ctx(&java_client->tls_options, tls_ctx); aws_tls_connection_options_set_server_name(&java_client->tls_options, allocator, &client_options.host_name); client_options.tls_options = &java_client->tls_options; } } else { client_options.tls_options = NULL; } jobject jni_http_proxy_options = (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.http_proxy_options_field_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting http proxy options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (jni_http_proxy_options) { java_http_proxy_options = s_aws_mqtt5_http_proxy_options_create_from_java(env, allocator, java_client, jni_http_proxy_options); client_options.http_proxy_options = &java_http_proxy_options->options; if (client_options.http_proxy_options->connection_type != AWS_HPCT_HTTP_TUNNEL) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: http proxy connection type has to be set to tunnel", AWS_ERROR_INVALID_STATE); goto clean_up; } } if (jni_connect_options) { connect_options = aws_mqtt5_packet_connect_view_create_from_java(env, allocator, jni_connect_options); if (connect_options != NULL || aws_jni_check_and_clear_exception(env)) { client_options.connect_options = aws_mqtt5_packet_connect_view_get_packet(connect_options); } else { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting connect options", AWS_ERROR_INVALID_STATE); goto clean_up; } } uint32_t session_behavior = UINT32_MAX; if (aws_get_enum_from_jobject( env, jni_options, mqtt5_client_options_properties.options_get_session_behavior_id, s_client_string, "session behavior", mqtt5_client_session_behavior_properties.client_get_value_id, &session_behavior, true, &was_value_set) == AWS_OP_ERR) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get session behavior from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.session_behavior = (enum aws_mqtt5_client_session_behavior_type)session_behavior; } uint32_t extended_validation_and_flow_control_options = UINT32_MAX; if (aws_get_enum_from_jobject( env, jni_options, mqtt5_client_options_properties.options_get_extended_validation_and_flow_control_options_id, s_client_string, "extended validation and flow control", mqtt5_client_extended_validation_and_flow_control_options.client_get_value_id, &extended_validation_and_flow_control_options, true, &was_value_set) == AWS_OP_ERR) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not extended validation and flow control from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.extended_validation_and_flow_control_options = (enum aws_mqtt5_extended_validation_and_flow_control_options)extended_validation_and_flow_control_options; } uint32_t offline_queue_enum = UINT32_MAX; if (aws_get_enum_from_jobject( env, jni_options, mqtt5_client_options_properties.options_get_offline_queue_behavior_id, s_client_string, "offline queue behavior", mqtt5_client_offline_queue_behavior_type_properties.client_get_value_id, &offline_queue_enum, true, &was_value_set) == AWS_OP_ERR) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get offline queue behavior from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.offline_queue_behavior = (enum aws_mqtt5_client_operation_queue_behavior_type)offline_queue_enum; } uint32_t retry_jitter_enum = UINT32_MAX; if (aws_get_enum_from_jobject( env, jni_options, mqtt5_client_options_properties.options_get_retry_jitter_mode_id, s_client_string, "retry jitter mode", mqtt5_client_jitter_mode_properties.client_get_value_id, &retry_jitter_enum, true, &was_value_set) == AWS_OP_ERR) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get retry jitter mode from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.retry_jitter_mode = (enum aws_exponential_backoff_jitter_mode)retry_jitter_enum; } uint64_t min_reconnect_delay_ms = 0; if (aws_get_uint64_from_jobject( env, jni_options, mqtt5_client_options_properties.min_reconnect_delay_ms_field_id, s_client_string, "minimum reconnect delay", &min_reconnect_delay_ms, true, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get minimum reconnect delay from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.min_reconnect_delay_ms = min_reconnect_delay_ms; } uint64_t max_reconnect_delay_ms = 0; if (aws_get_uint64_from_jobject( env, jni_options, mqtt5_client_options_properties.max_reconnect_delay_ms_field_id, s_client_string, "maximum reconnect delay", &max_reconnect_delay_ms, true, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get maximum reconnect delay from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.max_reconnect_delay_ms = max_reconnect_delay_ms; } uint64_t min_connected_time_to_reset_reconnect_delay_ms = 0; if (aws_get_uint64_from_jobject( env, jni_options, mqtt5_client_options_properties.min_connected_time_to_reset_reconnect_delay_ms_field_id, s_client_string, "minimum connected time to reset reconnect delay", &min_connected_time_to_reset_reconnect_delay_ms, true, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get minimum connected time to reset reconnect delay from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.min_connected_time_to_reset_reconnect_delay_ms = min_connected_time_to_reset_reconnect_delay_ms; } uint32_t ping_timeout = 0; if (aws_get_uint32_from_jobject( env, jni_options, mqtt5_client_options_properties.ping_timeout_ms_field_id, s_client_string, "ping timeout", &ping_timeout, true, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get ping timeout from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.ping_timeout_ms = ping_timeout; } uint32_t connack_timeout = 0; if (aws_get_uint32_from_jobject( env, jni_options, mqtt5_client_options_properties.connack_timeout_ms_field_id, s_client_string, "ConnAck timeout", &connack_timeout, true, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get ConnAck timeout from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.connack_timeout_ms = connack_timeout; } uint32_t ack_timeout = 0; if (aws_get_uint32_from_jobject( env, jni_options, mqtt5_client_options_properties.ack_timeout_seconds_field_id, s_client_string, "Ack timeout", &ack_timeout, true, &was_value_set) != AWS_OP_SUCCESS) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Could not get Ack timeout from options", AWS_ERROR_INVALID_STATE); goto clean_up; } if (was_value_set) { client_options.ack_timeout_seconds = ack_timeout; } jint jvmresult = (*env)->GetJavaVM(env, &java_client->jvm); if (jvmresult != 0) { s_aws_mqtt5_client_log_and_throw_exception(env, "MQTT5 client new: Unable to get JVM", AWS_ERROR_INVALID_STATE); goto clean_up; } java_client->jni_client = (*env)->NewGlobalRef(env, jni_client); client_options.lifecycle_event_handler = &s_aws_mqtt5_client_java_lifecycle_event; client_options.lifecycle_event_handler_user_data = (void *)java_client; client_options.publish_received_handler = &s_aws_mqtt5_client_java_publish_received; client_options.publish_received_handler_user_data = (void *)java_client; /* Are we using websockets? */ jobject jni_websocket_handshake = (*env)->GetObjectField(env, jni_client, mqtt5_client_properties.websocket_handshake_field_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting websocket handshake transform", AWS_ERROR_INVALID_STATE); goto clean_up; } if (jni_websocket_handshake) { client_options.websocket_handshake_transform = &s_aws_mqtt5_client_java_websocket_handshake_transform; client_options.websocket_handshake_transform_user_data = (void *)java_client; } jobject jni_publish_events = (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.publish_events_field_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting publish events", AWS_ERROR_INVALID_STATE); goto clean_up; } if (jni_publish_events != NULL) { java_client->jni_publish_events = (*env)->NewGlobalRef(env, jni_publish_events); } jobject jni_lifecycle_events = (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.lifecycle_events_field_id); if (aws_jni_check_and_clear_exception(env)) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: error getting lifecycle events", AWS_ERROR_INVALID_STATE); goto clean_up; } if (jni_lifecycle_events != NULL) { java_client->jni_lifecycle_events = (*env)->NewGlobalRef(env, jni_lifecycle_events); } struct aws_mqtt5_client_topic_alias_options topic_aliasing_options; AWS_ZERO_STRUCT(topic_aliasing_options); jobject jni_topic_aliasing_options = (*env)->GetObjectField(env, jni_options, mqtt5_client_options_properties.topic_aliasing_options_field_id); if (jni_topic_aliasing_options != NULL) { if (s_initialize_topic_aliasing_options(env, &topic_aliasing_options, jni_topic_aliasing_options) == AWS_OP_SUCCESS) { client_options.topic_aliasing_options = &topic_aliasing_options; } } client_options.client_termination_handler = &s_aws_mqtt5_client_java_termination; client_options.client_termination_handler_user_data = (void *)java_client; /* Make the MQTT5 client */ java_client->client = aws_mqtt5_client_new(allocator, &client_options); /* Did we successfully make a client? If not, then throw an exception */ if (java_client->client == NULL) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client new: Was unable to create client due to option configuration! Enable error logging to see " "reason", AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION); goto clean_up; } goto clean_up; clean_up: aws_mqtt5_packet_connect_view_java_destroy(env, allocator, connect_options); s_aws_mqtt5_http_proxy_options_java_destroy(env, allocator, java_http_proxy_options); if (aws_byte_buf_is_valid(&host_name_buf)) { aws_byte_buf_clean_up(&host_name_buf); } (*env)->PopLocalFrame(env, NULL); if (java_client->client != NULL) { return (jlong)java_client; } aws_mqtt5_client_java_destroy(env, allocator, java_client); return (jlong)NULL; } JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_mqtt5_Mqtt5Client_mqtt5ClientDestroy( JNIEnv *env, jclass jni_class, jlong jni_mqtt_client) { (void)jni_class; aws_cache_jni_ids(env); struct aws_mqtt5_client_java_jni *java_client = (struct aws_mqtt5_client_java_jni *)jni_mqtt_client; if (!java_client) { s_aws_mqtt5_client_log_and_throw_exception( env, "MQTT5 client destroy: Invalid/null client", AWS_ERROR_INVALID_ARGUMENT); return; } // If the client is NOT null it can be shut down normally struct aws_allocator *allocator = aws_jni_get_allocator(); if (java_client->client) { aws_mqtt5_client_release(java_client->client); } else { aws_mqtt5_client_java_destroy(env, allocator, java_client); } } #if UINTPTR_MAX == 0xffffffff # if defined(_MSC_VER) # pragma warning(pop) # else # pragma GCC diagnostic pop # endif #endif