diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/README.md b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/README.md index 2e2e230ff..e595e925e 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/README.md +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/README.md @@ -120,7 +120,7 @@ mvn clean spring-boot:run ## Verify This Sample -Verify in your app’s logs that a similar message was posted: +Verify in your app’s logs that similar messages were posted: ```shell Message was sent successfully for queue1. ... diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/main.tf b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/main.tf index 6bc3ab503..4a9584bb1 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/main.tf +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/main.tf @@ -27,8 +27,8 @@ resource "azurerm_resource_group" "main" { location = var.location tags = { - "terraform" = "true" - "application-name" = var.application_name + "terraform" = "true" + "application-name" = var.application_name "spring-cloud-azure-sample" = var.sample_tag_value } } @@ -55,9 +55,8 @@ resource "azurerm_servicebus_namespace" "servicebus_namespace_01" { } resource "azurerm_servicebus_queue" "application_queue_01" { - name = "queue1" - namespace_name = azurerm_servicebus_namespace.servicebus_namespace_01.name - resource_group_name = azurerm_resource_group.main.name + name = "queue1" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace_01.id enable_partitioning = false max_delivery_count = 10 @@ -89,9 +88,8 @@ resource "azurerm_servicebus_namespace" "servicebus_namespace_02" { } resource "azurerm_servicebus_queue" "application_queue_02" { - name = "queue2" - namespace_name = azurerm_servicebus_namespace.servicebus_namespace_02.name - resource_group_name = azurerm_resource_group.main.name + name = "queue2" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace_02.id enable_partitioning = false max_delivery_count = 10 @@ -104,19 +102,19 @@ resource "azurerm_servicebus_queue" "application_queue_02" { data "azurerm_client_config" "client_config" { } -resource "azurerm_role_assignment" "servicebus_01_data_sender" { +resource "azurerm_role_assignment" "role_servicebus_01_data_sender" { scope = azurerm_servicebus_queue.application_queue_01.id role_definition_name = "Azure Service Bus Data Sender" principal_id = data.azurerm_client_config.client_config.object_id } -resource "azurerm_role_assignment" "servicebus_01_data_receiver" { +resource "azurerm_role_assignment" "role_servicebus_01_data_receiver" { scope = azurerm_servicebus_queue.application_queue_01.id role_definition_name = "Azure Service Bus Data Receiver" principal_id = data.azurerm_client_config.client_config.object_id } -resource "azurerm_role_assignment" "servicebus_02_data_sender" { +resource "azurerm_role_assignment" "role_servicebus_02_data_sender" { scope = azurerm_servicebus_queue.application_queue_02.id role_definition_name = "Azure Service Bus Data Sender" principal_id = data.azurerm_client_config.client_config.object_id diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/outputs.tf b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/outputs.tf index 199ce6820..abcaa22b5 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/outputs.tf +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/outputs.tf @@ -1,11 +1,9 @@ output "AZURE_SERVICEBUS_NAMESPACE_01" { value = azurerm_servicebus_namespace.servicebus_namespace_01.name - description = "The servicebus_01 namespace." + description = "The name of servicebus_01 namespace." } output "AZURE_SERVICEBUS_NAMESPACE_02" { value = azurerm_servicebus_namespace.servicebus_namespace_02.name - description = "The servicebus_02 namespace." + description = "The name of servicebus_02 namespace." } - - diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/variables.tf b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/variables.tf index b7bccba09..9aa805112 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/variables.tf +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/multiple-namespaces/terraform/variables.tf @@ -1,17 +1,17 @@ variable "application_name" { type = string - description = "The name of your application" - default = "servicebusapp" + description = "The name of your application." + default = "multiple-namespaces-servicebus" } variable "location" { type = string - description = "The Azure region where all resources in this example should be created" + description = "The Azure region where all resources in this example should be created." default = "eastus" } variable "sample_tag_value" { type = string - description = "The value of spring-cloud-azure-sample tag" + description = "The value of spring-cloud-azure-sample tag." default = "true" -} \ No newline at end of file +} diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/README.md b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/README.md index fbf0928d2..5f9e3ef29 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/README.md +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/README.md @@ -73,24 +73,34 @@ terraform -chdir=./terraform apply ``` - - - It may take a few minutes to run the script. After successful running, you will see prompt information like below: ```shell - +azurecaf_name.servicebus: Creating... +azurecaf_name.topic: Creating... +azurecaf_name.resource_group: Creating... +azurecaf_name.resource_group: Creation complete after 0s [id=mvwycgvrvqxrbyiy] +azurecaf_name.servicebus: Creation complete after 0s [id=kfvxhnbckoaabrfh] +azurecaf_name.topic: Creation complete after 0s [id=tixdrtltwgohxbde] ... -azurerm_servicebus_namespace_authorization_rule.application: Creation complete after 13s ... -azurerm_servicebus_subscription.application: Creation complete after 7s ... -azurerm_role_assignment.servicebus_data_owner: Still creating... [20s elapsed] -azurerm_role_assignment.servicebus_data_owner: Creation complete after 28s ... +azurerm_servicebus_namespace.servicebus_namespace: Creating... +... +azurerm_servicebus_namespace.servicebus_namespace: ... +azurerm_servicebus_queue.application: Creating... +azurerm_servicebus_topic.application: Creating... +azurerm_role_assignment.role_servicebus_data_owner: Creating... +... +azurerm_servicebus_subscription.application: Creating... +azurerm_servicebus_queue.application: Creation complete after 9s ... +... +azurerm_role_assignment.role_servicebus_data_owner: Still creating... [30s elapsed] +azurerm_role_assignment.role_servicebus_data_owner: ... -Apply complete! Resources: 11 added, 0 changed, 0 destroyed. +Apply complete! Resources: 9 added, 0 changed, 0 destroyed. Outputs: -SERVICEBUS_NAMESPACE = "${YOUR_SERVICEBUS_NAMESPACE}" +... ``` @@ -116,17 +126,17 @@ mvn clean spring-boot:run 1. Send a POST request to service bus queue - $ curl -X POST http://localhost:8080/queues?message=hello + curl -X POST http://localhost:8080/queues?message=hello -2. Verify in your app’s logs that a similar message was posted: +2. Verify in your app’s logs that similar messages were posted: New message received: 'hello' Message 'hello' successfully checkpointed 3. Send a POST request to service bus topic - $ curl -X POST http://localhost:8080/topics?message=hello + curl -X POST http://localhost:8080/topics?message=hello -4. Verify in your app’s logs that a similar message was posted: +4. Verify in your app’s logs that similar messages were posted: New message received: 'hello' Message 'hello' successfully checkpointed diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/main.tf b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/main.tf index 7edc3e8a3..67bb75b7c 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/main.tf +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/main.tf @@ -27,8 +27,8 @@ resource "azurerm_resource_group" "main" { location = var.location tags = { - "terraform" = "true" - "application-name" = var.application_name + "terraform" = "true" + "application-name" = var.application_name "spring-cloud-azure-sample" = var.sample_tag_value } } @@ -53,25 +53,9 @@ resource "azurerm_servicebus_namespace" "servicebus_namespace" { } } -resource "azurecaf_name" "servicebus_namespace_authorization_rule" { - name = var.application_name - resource_type = "azurerm_servicebus_namespace_authorization_rule" -} - -resource "azurerm_servicebus_namespace_authorization_rule" "application" { - name = azurecaf_name.servicebus_namespace_authorization_rule.result - namespace_name = azurerm_servicebus_namespace.servicebus_namespace.name - resource_group_name = azurerm_resource_group.main.name - - listen = true - send = true - manage = true -} - resource "azurerm_servicebus_queue" "application" { name = "queue1" - namespace_name = azurerm_servicebus_namespace.servicebus_namespace.name - resource_group_name = azurerm_resource_group.main.name + namespace_id = azurerm_servicebus_namespace.servicebus_namespace.id enable_partitioning = false max_delivery_count = 10 @@ -88,22 +72,20 @@ resource "azurecaf_name" "topic" { resource "azurerm_servicebus_topic" "application" { name = "topic1" - namespace_name = azurerm_servicebus_namespace.servicebus_namespace.name - resource_group_name = azurerm_resource_group.main.name + namespace_id = azurerm_servicebus_namespace.servicebus_namespace.id } resource "azurerm_servicebus_subscription" "application" { name = "group1" - resource_group_name = azurerm_resource_group.main.name - namespace_name = azurerm_servicebus_namespace.servicebus_namespace.name - topic_name = azurerm_servicebus_topic.application.name + topic_id = azurerm_servicebus_topic.application.id + max_delivery_count = 1 } data "azurerm_client_config" "client_config" { } -resource "azurerm_role_assignment" "servicebus_data_owner" { +resource "azurerm_role_assignment" "role_servicebus_data_owner" { scope = azurerm_servicebus_namespace.servicebus_namespace.id role_definition_name = "Azure Service Bus Data Owner" principal_id = data.azurerm_client_config.client_config.object_id diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/outputs.tf b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/outputs.tf index 155b11f6f..4aa5129c6 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/outputs.tf +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/outputs.tf @@ -1,5 +1,4 @@ output "SERVICEBUS_NAMESPACE" { value = azurerm_servicebus_namespace.servicebus_namespace.name - description = "The servicebus namespace." + description = "The name of servicebus namespace." } - diff --git a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/variables.tf b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/variables.tf index 8f1b98e64..9e513c3e8 100644 --- a/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/variables.tf +++ b/servicebus/spring-cloud-azure-starter-integration-servicebus/single-namespace/terraform/variables.tf @@ -1,17 +1,17 @@ variable "application_name" { type = string - description = "The name of your application" - default = "servicebusapp" + description = "The name of your application." + default = "single-namespaces-servicebus" } variable "location" { type = string - description = "The Azure region where all resources in this example should be created" + description = "The Azure region where all resources in this example should be created." default = "eastus" } variable "sample_tag_value" { type = string - description = "The value of spring-cloud-azure-sample tag" + description = "The value of spring-cloud-azure-sample tag." default = "true" } diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/README.md b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/README.md index bbfc7ea0e..8c3223ea2 100644 --- a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/README.md +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/README.md @@ -1,68 +1,146 @@ -# Sample for Spring JMS with Azure Service Bus Queue Spring Cloud client library for Java +# Sample for Spring JMS with Azure Service Bus Queue Spring Cloud client library for Java -## Key concepts +This sample project demonstrates how to use Spring JMS for Azure Service Bus Queue via Spring Boot Starter `spring-cloud-azure-starter-servicebus-jms`. -This sample project demonstrates how to use Spring JMS for Azure Service Bus Queue via Spring Boot Starter `spring-cloud-azure-starter-servicebus-jms`. -Running this sample will be charged by Azure. You can check the usage and bill at this [link](https://azure.microsoft.com/account/). +## What You Will Build +You will build an application using Spring JMS to send and receive messages for Azure Service Bus Queue. -## Getting started +## What You Need +- [An Azure subscription](https://azure.microsoft.com/free/) +- [Terraform](https://www.terraform.io/) +- [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) +- [JDK8](https://www.oracle.com/java/technologies/downloads/) or later +- Maven +- You can also import the code straight into your IDE: + - [IntelliJ IDEA](https://www.jetbrains.com/idea/download) +## Provision Azure Resources Required to Run This Sample +This sample will create Azure resources using Terraform. If you choose to run it without using Terraform to provision resources, please pay attention to: +> [!IMPORTANT] +> If you choose to use a security principal to authenticate and authorize with Azure Active Directory for accessing an Azure resource +> please refer to [Authorize access with Azure AD](https://microsoft.github.io/spring-cloud-azure/docs/current/reference/html/index.html#authorize-access-with-azure-active-directory) to make sure the security principal has been granted the sufficient permission to access the Azure resource. -### Create Service Bus on Azure +### Authenticate Using the Azure CLI +Terraform must authenticate to Azure to create infrastructure. -1. Go to [Azure portal](https://portal.azure.com/) and create the service by following this [link](https://docs.microsoft.com/azure/service-bus-messaging/service-bus-create-namespace-portal). +In your terminal, use the Azure CLI tool to setup your account permissions locally. -## Examples -### Config the sample +```shell +az login +``` -1. Update [application.properties](https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.properties) - ```properties - # Fill service bus namespace connection string copied from portal - spring.jms.servicebus.connection-string=[servicebus-namespace-connection-string] +Your browser window will open and you will be prompted to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information. You do not need to save this output as it is saved in your system for Terraform to use. - # The idle timeout in milliseconds after which the connection will be failed if the peer sends no AMQP frames - # Default is 1800000 - spring.jms.servicebus.idle-timeout=[idle-timeout] - - #Fill service bus pricing tier according to the one you created. Supported values are premium, standard and basic. - spring.jms.servicebus.pricing-tier=[pricing-tier] - ``` +```shell +You have logged in. Now let us find all the subscriptions to which you have access... -2. Specify your queue name. Update `QUEUE_NAME` in [QueueSendController] and [QueueReceiveController] . - -### How to run -1. Run with Maven - ``` - cd azure-spring-boot-samples/servicebus/spring-cloud-azure-starter-servicebus-jms - mvn spring-boot:run - ``` +[ + { + "cloudName": "AzureCloud", + "homeTenantId": "home-Tenant-Id", + "id": "subscription-id", + "isDefault": true, + "managedByTenants": [], + "name": "Subscription-Name", + "state": "Enabled", + "tenantId": "0envbwi39-TenantId", + "user": { + "name": "your-username@domain.com", + "type": "user" + } + } +] +``` + +If you have more than one subscription, specify the subscription-id you want to use with command below: +```shell +az account set --subscription +``` + +### Provision the Resources + +After login Azure CLI with your account, now you can use the terraform script to create Azure Resources. + +```shell +# In the root directory of the sample +# Initialize your Terraform configuration +terraform -chdir=./terraform init + +# Apply your Terraform Configuration +# Type `yes` at the confirmation prompt to proceed. +terraform -chdir=./terraform apply + +``` + +It may take a few minutes to run the script. After successful running, you will see prompt information like below: + +```shell +azurecaf_name.azurecaf_name_servicebus: Creating... +azurecaf_name.resource_group: Creating... +azurecaf_name.azurecaf_name_servicebus: Creation complete after 0s [id=qikkuohqscnhospw] +azurecaf_name.resource_group: Creation complete after 0s [id=hsgmdikkcuwtxecs] +azurerm_resource_group.main: Creating... +azurerm_resource_group.main: Creation complete after 5s ... +azurerm_servicebus_namespace.servicebus_namespace: Creating... +... +azurerm_servicebus_namespace.servicebus_namespace: Creation complete after ... +azurerm_servicebus_queue.queue: Creating... +azurerm_servicebus_queue.queue: Creation complete ... + +Apply complete! Resources: 5 added, 0 changed, 0 destroyed. + +Outputs: + +... -2. Send a POST request to service bus queue. +``` + +You can go to [Azure portal](https://ms.portal.azure.com/) in your web browser to check the resources you created. + +### Export Output to Your Local Environment +Running the command below to export environment values: + +```shell + source ./terraform/setup_env.sh +``` + +## Run Locally + +In your terminal, run `mvn clean spring-boot:run`. + + +```shell +mvn clean spring-boot:run +``` + +## Verify This Sample + + +1. Send a POST request to service bus queue. ``` - $ curl -X POST localhost:8080/queue?message=hello + curl -d "" http://localhost:8080/queue?message=hello ``` - -3. Verify in your app's logs that a similar message was posted: + +2. Verify in your app's logs that a similar message was posted: ``` Sending message + ... Received message from queue: hello ``` - -4. Delete the resources on [Azure Portal](https://ms.portal.azure.com/) to avoid extra charges. -## Troubleshooting -## Next steps -Please check the following table for reference links of detailed Service Bus usage. -Type | Reference Link ---- | --- -`Queues` | [https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-queues](https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-queues) +## Clean Up Resources +After running the sample, if you don't want to run the sample, remember to destroy the Azure resources you created to avoid unnecessary billing. + +The terraform destroy command terminates resources managed by your Terraform project. +To destroy the resources you created. + +```shell +terraform -chdir=./terraform destroy +``` + -## Contributing - -[QueueSendController]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/java/com/azure/spring/sample/jms/queue/QueueSendController.java -[QueueReceiveController]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/java/com/azure/spring/sample/jms/queue/QueueReceiveController.java diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.properties b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.properties deleted file mode 100644 index d9ee4a90f..000000000 --- a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.properties +++ /dev/null @@ -1,5 +0,0 @@ -spring.jms.servicebus.connection-string=[servicebus-namespace-connection-string] - -spring.jms.servicebus.idle-timeout=[idle-timeout] - -spring.jms.servicebus.pricing-tier=[pricing-tier] diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.yaml b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.yaml new file mode 100644 index 000000000..4e5a8064f --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/src/main/resources/application.yaml @@ -0,0 +1,6 @@ +spring: + jms: + servicebus: + connection-string: ${SERVICEBUS_NAMESPACE_CONNECTION_STRING} + idle-timeout: 1800000 + pricing-tier: ${PRICING_TIER} diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/main.tf b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/main.tf new file mode 100644 index 000000000..2e58db5ab --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/main.tf @@ -0,0 +1,66 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 2.75" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = "1.2.10" + } + } +} + +provider "azurerm" { + features {} +} + +resource "azurecaf_name" "resource_group" { + name = var.application_name + resource_type = "azurerm_resource_group" + random_length = 5 + clean_input = true +} + +resource "azurerm_resource_group" "main" { + name = azurecaf_name.resource_group.result + location = var.location + + tags = { + "terraform" = "true" + "application-name" = var.application_name + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurecaf_name" "azurecaf_name_servicebus" { + name = var.application_name + resource_type = "azurerm_servicebus_namespace" + random_length = 5 + clean_input = true +} + +resource "azurerm_servicebus_namespace" "servicebus_namespace" { + name = azurecaf_name.azurecaf_name_servicebus.result + location = var.location + resource_group_name = azurerm_resource_group.main.name + + sku = var.pricing_tier + zone_redundant = false + + tags = { + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurerm_servicebus_queue" "queue" { + name = "que001" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace.id + + enable_partitioning = false + max_delivery_count = 10 + lock_duration = "PT30S" + max_size_in_megabytes = 1024 + requires_session = false + default_message_ttl = "P14D" +} diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/outputs.tf b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/outputs.tf new file mode 100644 index 000000000..cdd44546f --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/outputs.tf @@ -0,0 +1,10 @@ +output "SERVICEBUS_NAMESPACE_CONNECTION_STRING" { + value = azurerm_servicebus_namespace.servicebus_namespace.default_primary_connection_string + description = "The connection_string of servicebus namespace." + sensitive = true +} + +output "PRICING_TIER" { + value = var.pricing_tier + description = "The pricing tier of Service Bus." +} diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/setup_env.sh b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/setup_env.sh new file mode 100644 index 000000000..7d461707b --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/setup_env.sh @@ -0,0 +1,2 @@ +export SERVICEBUS_NAMESPACE_CONNECTION_STRING=$(terraform -chdir=./terraform output -raw SERVICEBUS_NAMESPACE_CONNECTION_STRING) +export PRICING_TIER=$(terraform -chdir=./terraform output -raw PRICING_TIER) diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/variables.tf b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/variables.tf new file mode 100644 index 000000000..8a7994262 --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-queue/terraform/variables.tf @@ -0,0 +1,23 @@ +variable "application_name" { + type = string + description = "The name of your application." + default = "servicebus-jms-queue" +} + +variable "location" { + type = string + description = "The Azure region where all resources in this example should be created." + default = "eastus" +} + +variable "sample_tag_value" { + type = string + description = "The value of spring-cloud-azure-sample tag." + default = "true" +} + +variable "pricing_tier" { + type = string + description = "The pricing tier of Service Bus." + default = "Standard" +} diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/README.md b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/README.md index 4a7db667a..8755b7db6 100644 --- a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/README.md +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/README.md @@ -1,73 +1,144 @@ -# Sample for Spring JMS with Azure Service Bus Topic Spring Cloud client library for Java -## Key concepts +# Sample for Spring JMS with Azure Service Bus Topic Spring Cloud client library for Java This sample project demonstrates how to use Spring JMS Topic for Azure Service Bus via Spring Boot Starter `spring-cloud-azure-starter-servicebus-jms`. -Running this sample will be charged by Azure. You can check the usage and bill at this [link](https://azure.microsoft.com/account/). +## What You Will Build +You will build an application using Spring JMS to send and receive messages for Azure Service Bus Topic. + +## What You Need + +- [An Azure subscription](https://azure.microsoft.com/free/) +- [Terraform](https://www.terraform.io/) +- [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) +- [JDK8](https://www.oracle.com/java/technologies/downloads/) or later +- Maven +- You can also import the code straight into your IDE: + - [IntelliJ IDEA](https://www.jetbrains.com/idea/download) -## Getting started +## Provision Azure Resources Required to Run This Sample +This sample will create Azure resources using Terraform. If you choose to run it without using Terraform to provision resources, please pay attention to: +> [!IMPORTANT] +> If you choose to use a security principal to authenticate and authorize with Azure Active Directory for accessing an Azure resource +> please refer to [Authorize access with Azure AD](https://microsoft.github.io/spring-cloud-azure/docs/current/reference/html/index.html#authorize-access-with-azure-active-directory) to make sure the security principal has been granted the sufficient permission to access the Azure resource. +### Authenticate Using the Azure CLI +Terraform must authenticate to Azure to create infrastructure. +In your terminal, use the Azure CLI tool to setup your account permissions locally. -### Create Service Bus on Azure -1. Go to [Azure portal](https://portal.azure.com/) and create the service by following this [link](https://docs.microsoft.com/azure/service-bus-messaging/service-bus-create-namespace-portal). +```shell +az login +``` +Your browser window will open and you will be prompted to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information. You do not need to save this output as it is saved in your system for Terraform to use. -## Examples -### Config the sample -1. Update [application.properties](https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.properties) +```shell +You have logged in. Now let us find all the subscriptions to which you have access... - ```properties - # Fill service bus namespace connection string copied from portal - spring.jms.servicebus.connection-string=[servicebus-namespace-connection-string] - - # The JMS client id needs to be specified when using topic and durable subscription - # Default is empty string - spring.jms.servicebus.topic-client-id=[topic-client-id] - - # The idle timeout in milliseconds after which the connection will be failed if the peer sends no AMQP frames - # Default is 1800000 - spring.jms.servicebus.idle-timeout=[idle-timeout] - - #Fill service bus pricing tier according to the one you created. Supported values are premium and standard. - spring.jms.servicebus.pricing-tier=[pricing-tier] - ``` +[ + { + "cloudName": "AzureCloud", + "homeTenantId": "home-Tenant-Id", + "id": "subscription-id", + "isDefault": true, + "managedByTenants": [], + "name": "Subscription-Name", + "state": "Enabled", + "tenantId": "0envbwi39-TenantId", + "user": { + "name": "your-username@domain.com", + "type": "user" + } + } +] +``` -2. Specify your topic name and subscription name. Update `TOPIC_NAME` in [TopicSendController] and [TopicReceiveController], and `SUBSCRIPTION_NAME` in [TopicReceiveController]. +If you have more than one subscription, specify the subscription-id you want to use with command below: +```shell +az account set --subscription +``` -### How to run -1. Run with Maven: - ``` - cd azure-spring-boot-samples/servicebus/spring-cloud-azure-starter-servicebus-jms - mvn spring-boot:run - ``` +### Provision the Resources + +After login Azure CLI with your account, now you can use the terraform script to create Azure Resources. + +```shell +# In the root directory of the sample +# Initialize your Terraform configuration +terraform -chdir=./terraform init + +# Apply your Terraform Configuration +# Type `yes` at the confirmation prompt to proceed. +terraform -chdir=./terraform apply + +``` + +It may take a few minutes to run the script. After successful running, you will see prompt information like below: + +```shell +azurecaf_name.azurecaf_name_servicebus: Creating... +azurecaf_name.resource_group: Creating... +azurecaf_name.resource_group: Creation complete after 0s ... +azurecaf_name.azurecaf_name_servicebus: Creation complete after 0s ... +azurerm_resource_group.main: Creating... +azurerm_resource_group.main: Creation complete ... +azurerm_servicebus_namespace.servicebus_namespace: Creating... +... +azurerm_servicebus_namespace.servicebus_namespace: Creation complete after ... +azurerm_servicebus_topic.application: Creating... +azurerm_servicebus_topic.application: Creation complete ... +azurerm_servicebus_subscription.application: Creating... +azurerm_servicebus_subscription.application: Creation complete ... + +Apply complete! Resources: 6 added, 0 changed, 0 destroyed. + +Outputs: -2. Send a POST request to service bus topic. +... +``` + +You can go to [Azure portal](https://ms.portal.azure.com/) in your web browser to check the resources you created. + +### Export Output to Your Local Environment +Running the command below to export environment values: + +```shell + source ./terraform/setup_env.sh +``` + +## Run Locally + +In your terminal, run `mvn clean spring-boot:run`. + + +```shell +mvn clean spring-boot:run +``` + +## Verify This Sample + +1. Send a POST request to service bus topic. ``` - $ curl -X POST localhost:8080/topic?message=hello + curl http://localhost:8080/topic?message=hello -d "" ``` -3. Verify in your app's logs that a similar message was posted: +2. Verify in your app's logs that a similar message was posted: ``` Sending message + ... Received message from topic: hello ``` - -4. Delete the resources on [Azure Portal](https://ms.portal.azure.com/) to avoid extra charges. -## Troubleshooting -## Next steps +## Clean Up Resources +After running the sample, if you don't want to run the sample, remember to destroy the Azure resources you created to avoid unnecessary billing. + +The terraform destroy command terminates resources managed by your Terraform project. +To destroy the resources you created. -Please check the following table for reference links of detailed Service Bus usage. +```shell +terraform -chdir=./terraform destroy +``` -Type | Reference Link ---- | --- -`Topics` | [https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-topics-subscriptions](https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-topics-subscriptions) -`Subscriptions` | [https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-topics-subscriptions](https://docs.microsoft.com/azure/service-bus-messaging/service-bus-java-how-to-use-topics-subscriptions) -## Contributing - -[TopicSendController]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/java/com/azure/spring/sample/jms/topic/TopicSendController.java -[TopicReceiveController]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/java/com/azure/spring/sample/jms/topic/TopicReceiveController.java diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.properties b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.properties deleted file mode 100644 index c60334621..000000000 --- a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -spring.jms.servicebus.connection-string=[servicebus-namespace-connection-string] - -spring.jms.servicebus.topic-client-id=[topic-client-id] - -spring.jms.servicebus.idle-timeout=[idle-timeout] - -spring.jms.servicebus.pricing-tier=[pricing-tier] diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.yaml b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.yaml new file mode 100644 index 000000000..f618ed8cb --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/src/main/resources/application.yaml @@ -0,0 +1,7 @@ +spring: + jms: + servicebus: + connection-string: ${SERVICEBUS_NAMESPACE_CONNECTION_STRING} + idle-timeout: 1800000 + pricing-tier: ${PRICING_TIER} + topic-client-id: topic-client-id diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/main.tf b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/main.tf new file mode 100644 index 000000000..34cb3673a --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/main.tf @@ -0,0 +1,66 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 2.75" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = "1.2.10" + } + } +} + +provider "azurerm" { + features {} +} + +resource "azurecaf_name" "resource_group" { + name = var.application_name + resource_type = "azurerm_resource_group" + random_length = 5 + clean_input = true +} + +resource "azurerm_resource_group" "main" { + name = azurecaf_name.resource_group.result + location = var.location + + tags = { + "terraform" = "true" + "application-name" = var.application_name + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurecaf_name" "azurecaf_name_servicebus" { + name = var.application_name + resource_type = "azurerm_servicebus_namespace" + random_length = 5 + clean_input = true +} + +resource "azurerm_servicebus_namespace" "servicebus_namespace" { + name = azurecaf_name.azurecaf_name_servicebus.result + location = var.location + resource_group_name = azurerm_resource_group.main.name + + sku = var.pricing_tier + zone_redundant = false + + tags = { + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurerm_servicebus_topic" "application" { + name = "tpc001" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace.id +} + +resource "azurerm_servicebus_subscription" "application" { + name = "sub001" + topic_id = azurerm_servicebus_topic.application.id + + max_delivery_count = 1 +} diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/outputs.tf b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/outputs.tf new file mode 100644 index 000000000..f93e98f79 --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/outputs.tf @@ -0,0 +1,10 @@ +output "CONNECTION_STRING" { + value = azurerm_servicebus_namespace.servicebus_namespace.default_primary_connection_string + description = "The connection_string of servicebus namespace." + sensitive = true +} + +output "PRICING_TIER" { + value = var.pricing_tier + description = "The pricing tier of Service Bus." +} diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/setup_env.sh b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/setup_env.sh new file mode 100644 index 000000000..7af58f7f7 --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/setup_env.sh @@ -0,0 +1,2 @@ +export SERVICEBUS_NAMESPACE_CONNECTION_STRING=$(terraform -chdir=./terraform output -raw CONNECTION_STRING) +export PRICING_TIER=$(terraform -chdir=./terraform output -raw PRICING_TIER) diff --git a/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/variables.tf b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/variables.tf new file mode 100644 index 000000000..0bce33f59 --- /dev/null +++ b/servicebus/spring-cloud-azure-starter-servicebus-jms/servicebus-jms-topic/terraform/variables.tf @@ -0,0 +1,23 @@ +variable "application_name" { + type = string + description = "The name of your application." + default = "servicebus-jms-topic" +} + +variable "location" { + type = string + description = "The Azure region where all resources in this example should be created." + default = "eastus" +} + +variable "sample_tag_value" { + type = string + description = "The value of spring-cloud-azure-sample tag." + default = "true" +} + +variable "pricing_tier" { + type = string + description = "The pricing tier of Service Bus." + default = "Standard" +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/README.md b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/README.md index 490660afa..73ee52345 100644 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/README.md +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/README.md @@ -1,278 +1,160 @@ -# Spring Cloud Azure Stream Binder for Multiple Service Bus Namespaces Code Sample shared library for Java +# Spring Cloud Azure Stream Binder for Multiple Service Bus Namespaces Code Sample shared library for Java -## Key concepts -This code sample demonstrates how to use the Spring Cloud Stream Binder for +This code sample demonstrates how to use the Spring Cloud Stream Binder for multiple Azure Service Bus namespaces. In this sample you will bind to two Service Bus namespaces separately through -a queue binder and a topic binder..The sample app has two operating modes. One way is to expose a Restful API to receive string message, +a queue binder and a topic binder.The sample app has two operating modes. One way is to expose a Restful API to receive string message, another way is to automatically provide string messages. These messages are published to a service bus. The sample will also consume messages from the same service bus. -## Getting started - -Running this sample will be charged by Azure. You can check the usage -and bill at [this link][azure-account]. - - - -### Create Azure resources - -1. Create a queue and a topic in different Service Bus namespaces. - Please see [how to create][create-service-bus]. - -1. **[Optional]** if you want to use service principal, please follow - [create service principal from Azure CLI][create-sp-using-azure-cli] to create one. - -1. **[Optional]** if you want to use managed identity, please follow - [create managed identity][create-managed-identity] to set up managed identity. - -## Examples - -1. Update stream binding related properties in - [application.yaml][application.yaml]. If you choose to use - service principal or managed identity, update the [application-sp.yaml][application-sp.yaml] or - [application-mi.yaml][application-mi.yaml] respectively. - - ```yaml - spring: - cloud: - stream: - #To specify which functional bean to bind to the external destination(s) exposed by the bindings - function: - definition: consume1;supply1;consume2;supply2 - bindings: - consume1-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply1-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - consume2-in-0: - binder: servicebus-2 - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply2-out-0: - binder: servicebus-2 - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - binders: - servicebus-1: - type: servicebus - default-candidate: true - environment: - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_1} - servicebus-2: - type: servicebus - default-candidate: false - environment: - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_2} - servicebus: - bindings: - consume1-in-0: - consumer: - checkpoint-mode: MANUAL - supply1-out-0: - producer: - entity-type: topic - consume2-in-0: - consumer: - checkpoint-mode: MANUAL - supply2-out-0: - producer: - entity-type: queue - poller: - initial-delay: 0 - fixed-delay: 1000 +## What You Will Build +You will build an application using Spring Cloud Stream to send and receive messages for multiple Azure Service Bus namespaces. - ``` +## What You Need -> The **defaultCandidate** configuration item: -Whether the binder configuration is a candidate for being considered a -default binder, or can be used only when explicitly referenced. This -allows adding binder configurations without interfering with the default -processing. +- [An Azure subscription](https://azure.microsoft.com/free/) +- [Terraform](https://www.terraform.io/) +- [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) +- [JDK8](https://www.oracle.com/java/technologies/downloads/) or later +- Maven +- You can also import the code straight into your IDE: + - [IntelliJ IDEA](https://www.jetbrains.com/idea/download) ->[!Important] -> -> When using the Restful API to send messages, the **Active profiles** must contain `manual`. +## Provision Azure Resources Required to Run This Sample +This sample will create Azure resources using Terraform. If you choose to run it without using Terraform to provision resources, please pay attention to: +> [!IMPORTANT] +> If you choose to use a security principal to authenticate and authorize with Azure Active Directory for accessing an Azure resource +> please refer to [Authorize access with Azure AD](https://microsoft.github.io/spring-cloud-azure/docs/current/reference/html/index.html#authorize-access-with-azure-active-directory) to make sure the security principal has been granted the sufficient permission to access the Azure resource. -1. Run the `mvn clean spring-boot:run` in the root of the code sample - to get the app running. +### Authenticate Using the Azure CLI +Terraform must authenticate to Azure to create infrastructure. -1. Send a POST request to test the default binder +In your terminal, use the Azure CLI tool to setup your account permissions locally. - $ curl -X POST http://localhost:8080/messages1?message=hello - -1. Verify in your app’s logs that a similar message was posted: - - [1] New message1 received: 'hello' - [1] Message1 'hello' successfully checkpointed - -1. Send another POST request to test the other binder - - $ curl -X POST http://localhost:8080/messages2?message=hello - -1. Verify in your app’s logs that a similar message was posted: - - [2] New message2 received: 'hello' - [2] Message2 'hello' successfully checkpointed - -6. Delete the resources on [Azure Portal][azure-portal] - to avoid unexpected charges. - -## Enhancement -### Configuration Options - -The binder provides the following configuration options: - -##### Service Bus Producer Properties - -It supports the following configurations with the format of `spring.cloud.stream.servicebus.bindings..producer`. - -**_sync_** - -Whether the producer should act in a synchronous manner with respect to writing messages into a stream. If true, the -producer will wait for a response after a send operation. - -Default: `false` - -**_send-timeout_** - -Effective only if `sync` is set to true. The amount of time to wait for a response after a send operation, in milliseconds. - -Default: `10000` - -##### Service Bus Consumer Properties - -It supports the following configurations with the format of `spring.cloud.stream.servicebus.bindings..consumer`. - -**_checkpoint-mode_** - -The mode in which checkpoints are updated. - -`RECORD`, checkpoints occur after each record successfully processed by user-defined message handler without any exception. - -`MANUAL`, checkpoints occur on demand by the user via the `Checkpointer`. You can get `Checkpointer` by `Message.getHeaders.get(AzureHeaders.CHECKPOINTER)`callback. - -Default: `RECORD` - -**_prefetch-count_** - -Prefetch count of underlying service bus client. - -Default: `1` - -**_maxConcurrentCalls_** - -Controls the max concurrent calls of service bus message handler and the size of fixed thread pool that handles user's business logic - -Default: `1` - -**_maxConcurrentSessions_** - -Controls the maximum number of concurrent sessions to process at any given time. - -Default: `1` - -**_concurrency_** - -When `sessionsEnabled` is true, controls the maximum number of concurrent sessions to process at any given time. -When `sessionsEnabled` is false, controls the max concurrent calls of service bus message handler and the size of fixed thread pool that handles user's business logic. - -Deprecated, replaced with `maxConcurrentSessions` when `sessionsEnabled` is true and `maxConcurrentCalls` when `sessionsEnabled` is false - -Default: `1` - -**_sessionsEnabled_** - -Controls if is a session aware consumer. Set it to `true` if is a queue with sessions enabled. - -Default: `false` - -**_requeueRejected_** - -Controls if is a message that trigger any exception in consumer will be force to DLQ. -Set it to `true` if a message that trigger any exception in consumer will be force to DLQ. -Set it to `false` if a message that trigger any exception in consumer will be re-queued. +```shell +az login +``` -Default: `false` +Your browser window will open and you will be prompted to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information. You do not need to save this output as it is saved in your system for Terraform to use. + +```shell +You have logged in. Now let us find all the subscriptions to which you have access... + +[ + { + "cloudName": "AzureCloud", + "homeTenantId": "home-Tenant-Id", + "id": "subscription-id", + "isDefault": true, + "managedByTenants": [], + "name": "Subscription-Name", + "state": "Enabled", + "tenantId": "0envbwi39-TenantId", + "user": { + "name": "your-username@domain.com", + "type": "user" + } + } +] +``` -**_receiveMode_** +If you have more than one subscription, specify the subscription-id you want to use with command below: +```shell +az account set --subscription +``` -The modes for receiving messages. +### Provision the Resources -`PEEK_LOCK`, received message is not deleted from the queue or subscription, instead it is temporarily locked to the receiver, making it invisible to other receivers. +After login Azure CLI with your account, now you can use the terraform script to create Azure Resources. -`RECEIVE_AND_DELETE`, received message is removed from the queue or subscription and immediately deleted. +```shell +# In the root directory of the sample +# Initialize your Terraform configuration +terraform -chdir=./terraform init -Default: `PEEK_LOCK` +# Apply your Terraform Configuration +# Type `yes` at the confirmation prompt to proceed. +terraform -chdir=./terraform apply -**_enableAutoComplete_** +``` -Enable auto-complete and auto-abandon of received messages. -'enableAutoComplete' is not needed in for RECEIVE_AND_DELETE mode. +It may take a few minutes to run the script. After successful running, you will see prompt information like below: + +```shell +azurecaf_name.servicebus_01: Creating... +azurecaf_name.resource_group: Creating... +... +azurerm_resource_group.main: Creating... +azurerm_resource_group.main: Creation complete after 4s ... +azurerm_servicebus_namespace.servicebus_namespace_02: Creating... +azurerm_servicebus_namespace.servicebus_namespace_01: Creating... +... +azurerm_servicebus_namespace.servicebus_namespace_02: Creation complete ... +azurerm_role_assignment.role_servicebus_data_owner_02: Creating... +azurerm_servicebus_queue.servicebus_namespace_02_queue: Creating... +azurerm_servicebus_namespace.servicebus_namespace_01: Creation complete ... +azurerm_role_assignment.role_servicebus_data_owner_01: Creating... +azurerm_servicebus_topic.servicebus_namespace_01_topic: Creating... +... +azurerm_servicebus_subscription.servicebus_namespace_01_sub: Creating... +... +azurerm_role_assignment.role_servicebus_data_owner_02: Creation complete ... +azurerm_role_assignment.role_servicebus_data_owner_01: Creation complete ... + +Apply complete! Resources: 11 added, 0 changed, 0 destroyed. + +Outputs: + +... +``` -Default: `false` -### Set message headers -The following table illustrates how Spring message headers are mapped to Service Bus message headers and properties. -When creat a message, developers can specify the header or property of a Service Bus message by below constants. +You can go to [Azure portal](https://ms.portal.azure.com/) in your web browser to check the resources you created. -```java - @Autowired -private Sinks.Many> many; +### Export Output to Your Local Environment +Running the command below to export environment values: -@PostMapping("/messages") -public ResponseEntity sendMessage(@RequestParam String message) { - many.emitNext(MessageBuilder.withPayload(message) - .setHeader(SESSION_ID, "group1") - .build(), - Sinks.EmitFailureHandler.FAIL_FAST); - return ResponseEntity.ok("Sent!"); - } +```shell + source ./terraform/setup_env.sh ``` -For some Service Bus headers that can be mapped to multiple Spring header constants, the priority of different Spring headers is listed. - -Service Bus Message Headers and Properties | Spring Message Header Constants | Type | Priority Number (Descending priority) -:---|:---|:---|:--- -ContentType | org.springframework.messaging.MessageHeaders.CONTENT_TYPE | String | N/A -CorrelationId | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.CORRELATION_ID | String | N/A -**MessageId** | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.MESSAGE_ID | String | 1 -**MessageId** | com.azure.spring.messaging.AzureHeaders.RAW_ID | String | 2 -**MessageId** | org.springframework.messaging.MessageHeaders.ID | UUID | 3 -PartitionKey | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.PARTITION_KEY | String | N/A -ReplyTo | org.springframework.messaging.MessageHeaders.REPLY_CHANNEL | String | N/A -ReplyToSessionId | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.REPLY_TO_SESSION_ID | String | N/A -**ScheduledEnqueueTimeUtc** | com.azure.spring.messaging.AzureHeaders.SCHEDULED_ENQUEUE_MESSAGE | Integer | 1 -**ScheduledEnqueueTimeUtc** | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.SCHEDULED_ENQUEUE_TIME | Instant | 2 -SessionID | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.SESSION_ID | String | N/A -TimeToLive | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.TIME_TO_LIVE | Duration | N/A -To | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.TO | String | N/A - -## Troubleshooting +## Run Locally -## Next steps +In your terminal, run `mvn clean spring-boot:run`. -## Contributing +```shell +mvn clean spring-boot:run +``` +## Verify This Sample + +1. Verify in your app’s logs that similar messages were posted: + +```shell +... +Message 'Hello world1, 3' successfully checkpointed +... +... +Message 'Hello world1, 4' successfully checkpointed +... +... +Message 'Hello world2, 3' successfully checkpointed +... +... +Message 'Hello world2, 5' successfully checkpointed +... +... + +Sending message2... +... +New message2 received... - -[azure-account]: https://azure.microsoft.com/account/ -[azure-portal]: https://ms.portal.azure.com/ -[create-service-bus]: https://docs.microsoft.com/azure/service-bus-messaging/service-bus-create-namespace-portal -[create-sp-using-azure-cli]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/create-sp-using-azure-cli.md -[create-managed-identity]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/create-managed-identity.md -[deploy-spring-boot-application-to-app-service]: https://docs.microsoft.com/java/azure/spring-framework/deploy-spring-boot-java-app-with-maven-plugin?toc=%2Fazure%2Fapp-service%2Fcontainers%2Ftoc.json&view=azure-java-stable -[deploy-to-app-service-via-ftp]: https://docs.microsoft.com/azure/app-service/deploy-ftp -[managed-identities]: https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/ -[role-assignment]: https://docs.microsoft.com/azure/role-based-access-control/role-assignments-portal -[application.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application.yaml -[application-mi.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-mi.yaml -[application-sp.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-sp.yaml +``` +## Clean Up Resources +After running the sample, if you don't want to run the sample, remember to destroy the Azure resources you created to avoid unnecessary billing. +The terraform destroy command terminates resources managed by your Terraform project. +To destroy the resources you created. +```shell +terraform -chdir=./terraform destroy +``` diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-mi.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-mi.yaml deleted file mode 100644 index 1a23169f9..000000000 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-mi.yaml +++ /dev/null @@ -1,58 +0,0 @@ -spring: - cloud: - azure: - credential: - managed-identity-client-id: ${AZURE_MANAGED_IDENTITY_CLIENT_ID} - profile: - tenant-id: ${AZURE_TENANT_ID} - stream: - function: - definition: consume1;supply1;consume2;supply2 - bindings: - consume1-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply1-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - consume2-in-0: - binder: servicebus-2 - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply2-out-0: - binder: servicebus-2 - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - binders: - servicebus-1: - type: servicebus - default-candidate: true - environment: - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_1} - servicebus-2: - type: servicebus - default-candidate: false - environment: - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_2} - servicebus: - bindings: - consume1-in-0: - consumer: - checkpoint-mode: MANUAL - supply1-out-0: - producer: - entity-type: topic - consume2-in-0: - consumer: - checkpoint-mode: MANUAL - supply2-out-0: - producer: - entity-type: queue - poller: - initial-delay: 0 - fixed-delay: 1000 \ No newline at end of file diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-sp.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-sp.yaml deleted file mode 100644 index ed24638d3..000000000 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application-sp.yaml +++ /dev/null @@ -1,59 +0,0 @@ -spring: - cloud: - azure: - credential: - client-id: ${AZURE_CLIENT_ID} - client-secret: ${AZURE_CLIENT_SECRET} - profile: - tenant-id: ${AZURE_TENANT_ID} - stream: - function: - definition: consume1;supply1;consume2;supply2 - bindings: - consume1-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply1-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - consume2-in-0: - binder: servicebus-2 - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply2-out-0: - binder: servicebus-2 - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - binders: - servicebus-1: - type: servicebus - default-candidate: true - environment: - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_1} - servicebus-2: - type: servicebus - default-candidate: false - environment: - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_2} - servicebus: - bindings: - consume1-in-0: - consumer: - checkpoint-mode: MANUAL - supply1-out-0: - producer: - entity-type: topic - consume2-in-0: - consumer: - checkpoint-mode: MANUAL - supply2-out-0: - producer: - entity-type: queue - poller: - initial-delay: 0 - fixed-delay: 1000 \ No newline at end of file diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application.yaml index 86b60a00d..e5496a6c5 100644 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application.yaml +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/src/main/resources/application.yaml @@ -24,7 +24,7 @@ spring: cloud: azure: servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_1} + namespace: ${AZURE_SERVICEBUS_NAMESPACE_01} servicebus-2: type: servicebus default-candidate: false @@ -33,7 +33,7 @@ spring: cloud: azure: servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING_2} + namespace: ${AZURE_SERVICEBUS_NAMESPACE_02} servicebus: bindings: consume1-in-0: diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/main.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/main.tf new file mode 100644 index 000000000..adde4d115 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/main.tf @@ -0,0 +1,116 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 2.75" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = "1.2.10" + } + } +} + +provider "azurerm" { + features {} +} + +# resource_group +resource "azurecaf_name" "resource_group" { + name = var.application_name + resource_type = "azurerm_resource_group" + random_length = 5 + clean_input = true +} + +resource "azurerm_resource_group" "main" { + name = azurecaf_name.resource_group.result + location = var.location + + tags = { + "terraform" = "true" + "application-name" = var.application_name + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +data "azurerm_client_config" "client_config" { +} + +# servicebus_namespace_01 with topic and subscription +resource "azurecaf_name" "servicebus_01" { + name = var.application_name + resource_type = "azurerm_servicebus_namespace" + random_length = 5 + clean_input = true +} + +resource "azurerm_servicebus_namespace" "servicebus_namespace_01" { + name = azurecaf_name.servicebus_01.result + location = var.location + resource_group_name = azurerm_resource_group.main.name + + sku = "Standard" + zone_redundant = false + + tags = { + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurerm_servicebus_topic" "servicebus_namespace_01_topic" { + name = "tpc001" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace_01.id +} + +resource "azurerm_servicebus_subscription" "servicebus_namespace_01_sub" { + name = "sub001" + topic_id = azurerm_servicebus_topic.servicebus_namespace_01_topic.id + + max_delivery_count = 1 +} + +resource "azurerm_role_assignment" "role_servicebus_data_owner_01" { + scope = azurerm_servicebus_namespace.servicebus_namespace_01.id + role_definition_name = "Azure Service Bus Data Owner" + principal_id = data.azurerm_client_config.client_config.object_id +} + +# servicebus_namespace_02 with queue +resource "azurecaf_name" "azurecaf_name_servicebus_02" { + name = var.application_name + resource_type = "azurerm_servicebus_namespace" + random_length = 5 + clean_input = true +} + +resource "azurerm_servicebus_namespace" "servicebus_namespace_02" { + name = azurecaf_name.azurecaf_name_servicebus_02.result + location = var.location + resource_group_name = azurerm_resource_group.main.name + + sku = "Standard" + zone_redundant = false + + tags = { + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurerm_servicebus_queue" "servicebus_namespace_02_queue" { + name = "que001" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace_02.id + + enable_partitioning = false + max_delivery_count = 10 + lock_duration = "PT30S" + max_size_in_megabytes = 1024 + requires_session = false + default_message_ttl = "P14D" +} + +resource "azurerm_role_assignment" "role_servicebus_data_owner_02" { + scope = azurerm_servicebus_namespace.servicebus_namespace_02.id + role_definition_name = "Azure Service Bus Data Owner" + principal_id = data.azurerm_client_config.client_config.object_id +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/outputs.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/outputs.tf new file mode 100644 index 000000000..a17aad149 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/outputs.tf @@ -0,0 +1,26 @@ +# =======================namespace_01======================= +output "AZURE_SERVICEBUS_NAMESPACE_01" { + value = azurerm_servicebus_namespace.servicebus_namespace_01.name + description = "The service bus namespace 01." +} + +output "AZURE_SERVICEBUS_TOPIC_NAME" { + value = azurerm_servicebus_topic.servicebus_namespace_01_topic.name + description = "The topic name in service bus namespace 01." +} + +output "AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME" { + value = azurerm_servicebus_subscription.servicebus_namespace_01_sub.name + description = "The topic subscription name in service bus namespace 01." +} + +# =======================namespace_02======================= +output "AZURE_SERVICEBUS_NAMESPACE_02" { + value = azurerm_servicebus_namespace.servicebus_namespace_02.name + description = "The service bus namespace 02." +} + +output "AZURE_SERVICEBUS_QUEUE_NAME" { + value = azurerm_servicebus_queue.servicebus_namespace_02_queue.name + description = "The queue name in service bus namespace 02." +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/setup_env.sh b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/setup_env.sh new file mode 100644 index 000000000..2223b55b2 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/setup_env.sh @@ -0,0 +1,5 @@ +export AZURE_SERVICEBUS_NAMESPACE_01=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_NAMESPACE_01) +export AZURE_SERVICEBUS_NAMESPACE_02=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_NAMESPACE_02) +export AZURE_SERVICEBUS_TOPIC_NAME=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_TOPIC_NAME) +export AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME) +export AZURE_SERVICEBUS_QUEUE_NAME=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_QUEUE_NAME) diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/variables.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/variables.tf new file mode 100644 index 000000000..d2d532a24 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-multibinders/terraform/variables.tf @@ -0,0 +1,17 @@ +variable "application_name" { + type = string + description = "The name of your application." + default = "servicebus-multibinders" +} + +variable "location" { + type = string + description = "The Azure region where all resources in this example should be created." + default = "eastus" +} + +variable "sample_tag_value" { + type = string + description = "The value of spring-cloud-azure-sample tag." + default = "true" +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/README.md b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/README.md index ac19603b7..3f88e0e8b 100644 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/README.md +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/README.md @@ -1,333 +1,153 @@ -# Spring Cloud Azure Stream Binder for Service Bus queue Sample shared library for Java +# Spring Cloud Azure Stream Binder for Service Bus queue Sample shared library for Java -## Key concepts -This code sample demonstrates how to use the Spring Cloud Stream binder for +This code sample demonstrates how to use the Spring Cloud Stream binder for Azure Service Bus queue. The sample app has two operating modes. One way is to expose a Restful API to receive string message, another way is to automatically provide string messages. These messages are published to a service bus queue. The sample will also consume messages from the same service bus queue. -## Getting started - -Running this sample will be charged by Azure. You can check the usage and bill at -[this link][azure-account]. - - - -### Create Azure resources - -We have several ways to config the Spring Cloud Stream Binder for Service -Bus Queue. You can choose anyone of them. - ->[!Important] -> -> When using the Restful API to send messages, the **Active profiles** must contain `manual`. - -#### Method 1: Connection string based usage - -1. Create Azure Service Bus namespace and queue. - Please see [how to create][create-service-bus]. - -1. Update [application.yaml][application.yaml]. - ```yaml - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: queue - poller: - fixed-delay: 1000 - initial-delay: 0 - ``` - -#### Method 2: Service principal based usage - -1. Create a service principal for use in by your app. Please follow - [create service principal from Azure CLI][create-sp-using-azure-cli]. - -1. Create Azure Service Bus namespace and queue. - Please see [how to create][create-service-bus]. - -1. Add Role Assignment for Service Bus. See - [Service principal for Azure resources with Service Bus][role-assignment] - to add role assignment for Service Bus. Assign `Contributor` role for service bus. - -1. Update [application-sp.yaml][application-sp.yaml]. - ```yaml - spring: - cloud: - azure: - credential: - client-id: ${AZURE_CLIENT_ID} - client-secret: ${AZURE_CLIENT_SECRET} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: queue - poller: - fixed-delay: 1000 - initial-delay: 0 - ``` -#### Method 3: MSI credential based usage - -##### Set up managed identity - -Please follow [create managed identity][create-managed-identity] to set up managed identity. - -##### Create other Azure resources - -1. Create Azure Service Bus namespace and queue. - Please see [how to create][create-service-bus]. - -1. Add Role Assignment for Service Bus. See - [Managed identities for Azure resources with Service Bus][role-assignment] - to add role assignment for Service Bus. Assign `Contributor` role for managed identity. - - -##### Update MSI related properties - -1. Update [application-mi.yaml][application-mi.yaml]. - ```yaml - spring: - cloud: - azure: - credential: - managed-identity-client-id: ${AZURE_MANAGED_IDENTITY_CLIENT_ID} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: queue - poller: - fixed-delay: 1000 - initial-delay: 0 - ``` - > We should specify `spring.profiles.active=mi` to run the Spring Boot application. - For App Service, please add a configuration entry for this. -##### Redeploy Application +## What You Will Build +You will build an application using Spring Cloud Stream to send and receive messages for Azure Service Bus Queue. -If you update the `spring.cloud.azure.credential.managed-identity-client-id` -property after deploying the app, or update the role assignment for -services, please try to redeploy the app again. +## What You Need -> You can follow -> [Deploy a Spring Boot JAR file to Azure App Service][deploy-spring-boot-application-to-app-service] -> to deploy this application to App Service +- [An Azure subscription](https://azure.microsoft.com/free/) +- [Terraform](https://www.terraform.io/) +- [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) +- [JDK8](https://www.oracle.com/java/technologies/downloads/) or later +- Maven +- You can also import the code straight into your IDE: + - [IntelliJ IDEA](https://www.jetbrains.com/idea/download) -## Examples +## Provision Azure Resources Required to Run This Sample +This sample will create Azure resources using Terraform. If you choose to run it without using Terraform to provision resources, please pay attention to: +> [!IMPORTANT] +> If you choose to use a security principal to authenticate and authorize with Azure Active Directory for accessing an Azure resource +> please refer to [Authorize access with Azure AD](https://microsoft.github.io/spring-cloud-azure/docs/current/reference/html/index.html#authorize-access-with-azure-active-directory) to make sure the security principal has been granted the sufficient permission to access the Azure resource. -1. Run the `mvn spring-boot:run` in the root of the code sample to get the app running. +### Authenticate Using the Azure CLI +Terraform must authenticate to Azure to create infrastructure. -1. Send a POST request +In your terminal, use the Azure CLI tool to setup your account permissions locally. - $ curl -X POST http://localhost:8080/messages?message=hello - - or when the app runs on App Service or VM - - $ curl -d -X POST https://[your-app-URL]/messages?message=hello - -1. Verify in your app’s logs that a similar message was posted: - - New message received: 'hello' - Message 'hello' successfully checkpointed - -1. Delete the resources on [Azure Portal][azure-portal] to avoid unexpected charges. - -## Enhancement - -### Configuration Options - -The binder provides the following configuration options: - -##### Service Bus Producer Properties - -It supports the following configurations with the format of `spring.cloud.stream.servicebus.bindings..producer`. - -**_sync_** - -Whether the producer should act in a synchronous manner with respect to writing messages into a stream. If true, the -producer will wait for a response after a send operation. - -Default: `false` - -**_send-timeout_** - -Effective only if `sync` is set to true. The amount of time to wait for a response after a send operation, in milliseconds. - -Default: `10000` - -##### Service Bus Consumer Properties - -It supports the following configurations with the format of `spring.cloud.stream.servicebus.bindings..consumer`. - -**_checkpoint-mode_** - -The mode in which checkpoints are updated. - -`RECORD`, checkpoints occur after each record successfully processed by user-defined message handler without any exception. - -`MANUAL`, checkpoints occur on demand by the user via the `Checkpointer`. You can get `Checkpointer` by `Message.getHeaders.get(AzureHeaders.CHECKPOINTER)`callback. - -Default: `RECORD` - -**_prefetch-count_** - -Prefetch count of underlying service bus client. - -Default: `1` - -**_maxConcurrentCalls_** - -Controls the max concurrent calls of service bus message handler and the size of fixed thread pool that handles user's business logic +```shell +az login +``` -Default: `1` +Your browser window will open and you will be prompted to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information. You do not need to save this output as it is saved in your system for Terraform to use. + +```shell +You have logged in. Now let us find all the subscriptions to which you have access... + +[ + { + "cloudName": "AzureCloud", + "homeTenantId": "home-Tenant-Id", + "id": "subscription-id", + "isDefault": true, + "managedByTenants": [], + "name": "Subscription-Name", + "state": "Enabled", + "tenantId": "0envbwi39-TenantId", + "user": { + "name": "your-username@domain.com", + "type": "user" + } + } +] +``` -**_maxConcurrentSessions_** +If you have more than one subscription, specify the subscription-id you want to use with command below: +```shell +az account set --subscription +``` -Controls the maximum number of concurrent sessions to process at any given time. +### Provision the Resources -Default: `1` +After login Azure CLI with your account, now you can use the terraform script to create Azure Resources. -**_concurrency_** +```shell +# In the root directory of the sample +# Initialize your Terraform configuration +terraform -chdir=./terraform init -When `sessionsEnabled` is true, controls the maximum number of concurrent sessions to process at any given time. -When `sessionsEnabled` is false, controls the max concurrent calls of service bus message handler and the size of fixed thread pool that handles user's business logic. +# Apply your Terraform Configuration +# Type `yes` at the confirmation prompt to proceed. +terraform -chdir=./terraform apply -Deprecated, replaced with `maxConcurrentSessions` when `sessionsEnabled` is true and `maxConcurrentCalls` when `sessionsEnabled` is false +``` -Default: `1` +It may take a few minutes to run the script. After successful running, you will see prompt information like below: + +```shell +azurecaf_name.resource_group: Creating... +azurecaf_name.azurecaf_name_servicebus: Creating... +azurecaf_name.azurecaf_name_servicebus: Creation complete ... +azurecaf_name.resource_group: Creation complete ... +azurerm_resource_group.main: Creating... +azurerm_resource_group.main: Creation complete ... +azurerm_servicebus_namespace.servicebus_namespace: Creating... +... +azurerm_servicebus_namespace.servicebus_namespace: Creation complete ... +azurerm_role_assignment.role_servicebus_data_owner: Creating... +azurerm_servicebus_queue.queue: Creating... +azurerm_servicebus_queue.queue: Creation complete ... +... +azurerm_role_assignment.role_servicebus_data_owner: ... + +Apply complete! Resources: 6 added, 0 changed, 0 destroyed. + +Outputs: + +... +``` -**_sessionsEnabled_** +You can go to [Azure portal](https://ms.portal.azure.com/) in your web browser to check the resources you created. -Controls if is a session aware consumer. Set it to `true` if is a queue with sessions enabled. +### Export Output to Your Local Environment +Running the command below to export environment values: -Default: `false` +```shell + source ./terraform/setup_env.sh +``` -**_requeueRejected_** +## Run Locally -Controls if is a message that trigger any exception in consumer will be force to DLQ. -Set it to `true` if a message that trigger any exception in consumer will be force to DLQ. -Set it to `false` if a message that trigger any exception in consumer will be re-queued. +In your terminal, run `mvn clean spring-boot:run`. -Default: `false` -**_receiveMode_** +```shell +mvn clean spring-boot:run +``` -The modes for receiving messages. +## Verify This Sample -`PEEK_LOCK`, received message is not deleted from the queue or subscription, instead it is temporarily locked to the receiver, making it invisible to other receivers. -`RECEIVE_AND_DELETE`, received message is removed from the queue or subscription and immediately deleted. +1. Verify in your app’s logs that similar messages were posted: -Default: `PEEK_LOCK` +```shell +... +New message received: 'Hello world, 2' +... +Message 'Hello world, 2' successfully checkpointed +... +... +New message received: 'Hello world, 3' +... +Message 'Hello world, 3' successfully checkpointed +... +... -**_enableAutoComplete_** +``` -Enable auto-complete and auto-abandon of received messages. -'enableAutoComplete' is not needed in for RECEIVE_AND_DELETE mode. -Default: `false` -### Set Service Bus message headers -The following table illustrates how Spring message headers are mapped to Service Bus message headers and properties. -When creat a message, developers can specify the header or property of a Service Bus message by below constants. +## Clean Up Resources +After running the sample, if you don't want to run the sample, remember to destroy the Azure resources you created to avoid unnecessary billing. -```java - @Autowired -private Sinks.Many> many; +The terraform destroy command terminates resources managed by your Terraform project. +To destroy the resources you created. -@PostMapping("/messages") -public ResponseEntity sendMessage(@RequestParam String message) { - many.emitNext(MessageBuilder.withPayload(message) - .setHeader(SESSION_ID, "group1") - .build(), - Sinks.EmitFailureHandler.FAIL_FAST); - return ResponseEntity.ok("Sent!"); - } +```shell +terraform -chdir=./terraform destroy ``` - -For some Service Bus headers that can be mapped to multiple Spring header constants, the priority of different Spring headers is listed. - -Service Bus Message Headers and Properties | Spring Message Header Constants | Type | Priority Number (Descending priority) -:---|:---|:---|:--- -ContentType | org.springframework.messaging.MessageHeaders.CONTENT_TYPE | String | N/A -CorrelationId | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.CORRELATION_ID | String | N/A -**MessageId** | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.MESSAGE_ID | String | 1 -**MessageId** | com.azure.spring.messaging.AzureHeaders.RAW_ID | String | 2 -**MessageId** | org.springframework.messaging.MessageHeaders.ID | UUID | 3 -PartitionKey | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.PARTITION_KEY | String | N/A -ReplyTo | org.springframework.messaging.MessageHeaders.REPLY_CHANNEL | String | N/A -ReplyToSessionId | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.REPLY_TO_SESSION_ID | String | N/A -**ScheduledEnqueueTimeUtc** | com.azure.spring.messaging.AzureHeaders.SCHEDULED_ENQUEUE_MESSAGE | Integer | 1 -**ScheduledEnqueueTimeUtc** | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.SCHEDULED_ENQUEUE_TIME | Instant | 2 -SessionID | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.SESSION_ID | String | N/A -TimeToLive | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.TIME_TO_LIVE | Duration | N/A -To | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.TO | String | N/A - -## Troubleshooting - -## Next steps - -## Contributing - - - -[azure-account]: https://azure.microsoft.com/account/ -[azure-portal]: https://ms.portal.azure.com/ -[create-service-bus]: https://docs.microsoft.com/azure/service-bus-messaging/service-bus-create-namespace-portal -[create-azure-storage]: https://docs.microsoft.com/azure/storage/ -[create-sp-using-azure-cli]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/create-sp-using-azure-cli.md -[create-managed-identity]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/create-managed-identity.md -[deploy-spring-boot-application-to-app-service]: https://docs.microsoft.com/java/azure/spring-framework/deploy-spring-boot-java-app-with-maven-plugin?toc=%2Fazure%2Fapp-service%2Fcontainers%2Ftoc.json&view=azure-java-stable -[role-assignment]: https://docs.microsoft.com/azure/role-based-access-control/role-assignments-portal -[application-mi.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-mi.yaml -[application-sp.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-sp.yaml -[application.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application.yaml - diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-mi.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-mi.yaml deleted file mode 100644 index 495ffaa05..000000000 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-mi.yaml +++ /dev/null @@ -1,30 +0,0 @@ -spring: - cloud: - azure: - credential: - managed-identity-client-id: ${AZURE_MANAGED_IDENTITY_CLIENT_ID} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: queue - poller: - fixed-delay: 1000 - initial-delay: 0 - - diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-sp.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-sp.yaml deleted file mode 100644 index 033be17ed..000000000 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application-sp.yaml +++ /dev/null @@ -1,29 +0,0 @@ -spring: - cloud: - azure: - credential: - client-id: ${AZURE_CLIENT_ID} - client-secret: ${AZURE_CLIENT_SECRET} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_QUEUE_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: queue - poller: - fixed-delay: 1000 - initial-delay: 0 diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application.yaml index 928e36a4a..b87cc1251 100644 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application.yaml +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/src/main/resources/application.yaml @@ -2,7 +2,7 @@ spring: cloud: azure: servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING} + namespace: ${AZURE_SERVICEBUS_NAMESPACE} stream: function: definition: consume;supply diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/main.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/main.tf new file mode 100644 index 000000000..5bb185364 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/main.tf @@ -0,0 +1,75 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 2.75" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = "1.2.10" + } + } +} + +provider "azurerm" { + features {} +} + +resource "azurecaf_name" "resource_group" { + name = var.application_name + resource_type = "azurerm_resource_group" + random_length = 5 + clean_input = true +} + +resource "azurerm_resource_group" "main" { + name = azurecaf_name.resource_group.result + location = var.location + + tags = { + "terraform" = "true" + "application-name" = var.application_name + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurecaf_name" "azurecaf_name_servicebus" { + name = var.application_name + resource_type = "azurerm_servicebus_namespace" + random_length = 5 + clean_input = true +} + +resource "azurerm_servicebus_namespace" "servicebus_namespace" { + name = azurecaf_name.azurecaf_name_servicebus.result + location = var.location + resource_group_name = azurerm_resource_group.main.name + + sku = "Standard" + zone_redundant = false + + tags = { + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurerm_servicebus_queue" "queue" { + name = "que001" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace.id + + enable_partitioning = false + max_delivery_count = 10 + lock_duration = "PT30S" + max_size_in_megabytes = 1024 + requires_session = false + default_message_ttl = "P14D" +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_role_assignment" "role_servicebus_data_owner" { + scope = azurerm_servicebus_namespace.servicebus_namespace.id + role_definition_name = "Azure Service Bus Data Owner" + principal_id = data.azurerm_client_config.current.object_id +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/outputs.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/outputs.tf new file mode 100644 index 000000000..5209d2437 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/outputs.tf @@ -0,0 +1,9 @@ +output "AZURE_SERVICEBUS_NAMESPACE" { + value = azurerm_servicebus_namespace.servicebus_namespace.name + description = "The name of service bus namespace." +} + +output "AZURE_SERVICEBUS_QUEUE_NAME" { + value = azurerm_servicebus_queue.queue.name + description = "The name of created queue in the service bus namespace." +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/setup_env.sh b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/setup_env.sh new file mode 100644 index 000000000..b79c4fc80 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/setup_env.sh @@ -0,0 +1,2 @@ +export AZURE_SERVICEBUS_NAMESPACE=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_NAMESPACE) +export AZURE_SERVICEBUS_QUEUE_NAME=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_QUEUE_NAME) diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/variables.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/variables.tf new file mode 100644 index 000000000..6e2a1f191 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-queue-binder/terraform/variables.tf @@ -0,0 +1,17 @@ +variable "application_name" { + type = string + description = "The name of your application." + default = "servicebus-queue-binder" +} + +variable "location" { + type = string + description = "The Azure region where all resources in this example should be created." + default = "eastus" +} + +variable "sample_tag_value" { + type = string + description = "The value of spring-cloud-azure-sample tag." + default = "true" +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/README.md b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/README.md index e4f2f9f68..dfef10061 100644 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/README.md +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/README.md @@ -1,336 +1,151 @@ -# Spring Cloud Azure Stream Binder for Service Bus topic Sample shared library for Java +# Spring Cloud Azure Stream Binder for Service Bus topic Sample shared library for Java -## Key concepts This code sample demonstrates how to use the Spring Cloud Stream binder for Azure Service Bus topic. The sample app has two operating modes. One way is to expose a Restful API to receive string message, another way is to automatically provide string messages. These messages are published to a service bus topic. The sample will also consume messages from the same service bus topic. -## Getting started - -Running this sample will be charged by Azure. You can check the usage and bill at -[this link][azure-account]. - - - -### Create Azure resources - -We have several ways to config the Spring Cloud Stream Binder for Azure -Service Bus Topic. You can choose anyone of them. - ->[!Important] -> -> When using the Restful API to send messages, the **Active profiles** must contain `manual`. - -#### Method 1: Connection string based usage - -1. Create Azure Service Bus namespace and topic. - Please see [how to create][create-service-bus]. - -1. Update [application.yaml]. - ```yaml - - spring: - cloud: - azure: - servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: topic - poller: - fixed-delay: 1000 - initial-delay: 0 - ``` - -#### Method 2: Service principal based usage - -1. Create a service principal for use in by your app. Please follow - [create service principal from Azure CLI][create-sp-using-azure-cli]. - -1. Create Azure Service Bus namespace and queue. - Please see [how to create][create-service-bus]. - -1. Add Role Assignment for Service Bus. See - [Service principal for Azure resources with Service Bus][role-assignment] - to add role assignment for Service Bus. Assign `Contributor` role for managed identity. - -1. Update [application-sp.yaml]. - ```yaml - spring: - cloud: - azure: - credential: - client-id: ${AZURE_CLIENT_ID} - client-secret: ${AZURE_CLIENT_SECRET} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: topic - poller: - fixed-delay: 1000 - initial-delay: 0 - ``` - > We should specify `spring.profiles.active=sp` to run the Spring Boot application. - -#### Method 3: MSI credential based usage - -##### Set up managed identity - -Please follow [create managed identity][create-managed-identity] to set up managed identity. - -##### Create other Azure resources - -1. Create Azure Service Bus namespace and queue. - Please see [how to create][create-service-bus]. - -1. Add Role Assignment for Service Bus. See - [Managed identities for Azure resources with Service Bus][role-assignment] - to add role assignment for Service Bus. Assign `Contributor` role for managed identity. - - -##### Update MSI related properties - -1. Update [application-mi.yaml] - ```yaml - spring: - cloud: - azure: - credential: - managed-identity-client-id: ${AZURE_MANAGED_IDENTITY_CLIENT_ID} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: topic - poller: - fixed-delay: 1000 - initial-delay: 0 - ``` - > We should specify `spring.profiles.active=mi` to run the Spring Boot application. - For App Service, please add a configuration entry for this. -##### Redeploy Application +## What You Will Build +You will build an application using Spring Cloud Stream to send and receive messages for Azure Service Bus Topic. -If you update the `spring.cloud.azure.credential.managed-identity-client-id` -property after deploying the app, or update the role assignment for -services, please try to redeploy the app again. +## What You Need -> You can follow -> [Deploy a Spring Boot JAR file to Azure App Service][deploy-spring-boot-application-to-app-service] -> to deploy this application to App Service +- [An Azure subscription](https://azure.microsoft.com/free/) +- [Terraform](https://www.terraform.io/) +- [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) +- [JDK8](https://www.oracle.com/java/technologies/downloads/) or later +- Maven +- You can also import the code straight into your IDE: + - [IntelliJ IDEA](https://www.jetbrains.com/idea/download) -## Examples +## Provision Azure Resources Required to Run This Sample +This sample will create Azure resources using Terraform. If you choose to run it without using Terraform to provision resources, please pay attention to: +> [!IMPORTANT] +> If you choose to use a security principal to authenticate and authorize with Azure Active Directory for accessing an Azure resource +> please refer to [Authorize access with Azure AD](https://microsoft.github.io/spring-cloud-azure/docs/current/reference/html/index.html#authorize-access-with-azure-active-directory) to make sure the security principal has been granted the sufficient permission to access the Azure resource. -1. Run the `mvn spring-boot:run` in the root of the code sample to get the app running. +### Authenticate Using the Azure CLI +Terraform must authenticate to Azure to create infrastructure. -1. Send a POST request +In your terminal, use the Azure CLI tool to setup your account permissions locally. - $ curl -X POST http://localhost:8080/messages?message=hello - - or when the app runs on App Service or VM - - $ curl -d -X POST https://[your-app-URL]/messages?message=hello - -1. Verify in your app’s logs that a similar message was posted: - - New message received: 'hello' - Message 'hello' successfully checkpointed - -1. Delete the resources on [Azure Portal][azure-portal] to avoid unexpected charges. - -## Enhancement -### Configuration Options - -The binder provides the following configuration options: - -##### Service Bus Producer Properties - -It supports the following configurations with the format of `spring.cloud.stream.servicebus.bindings..producer`. - -**_sync_** - -Whether the producer should act in a synchronous manner with respect to writing messages into a stream. If true, the -producer will wait for a response after a send operation. - -Default: `false` - -**_send-timeout_** - -Effective only if `sync` is set to true. The amount of time to wait for a response after a send operation, in milliseconds. - -Default: `10000` - -##### Service Bus Consumer Properties - -It supports the following configurations with the format of `spring.cloud.stream.servicebus.bindings..consumer`. - -**_checkpoint-mode_** - -The mode in which checkpoints are updated. - -`RECORD`, checkpoints occur after each record successfully processed by user-defined message handler without any exception. - -`MANUAL`, checkpoints occur on demand by the user via the `Checkpointer`. You can get `Checkpointer` by `Message.getHeaders.get(AzureHeaders.CHECKPOINTER)`callback. - -Default: `RECORD` - -**_prefetch-count_** - -Prefetch count of underlying service bus client. - -Default: `1` - -**_maxConcurrentCalls_** - -Controls the max concurrent calls of service bus message handler and the size of fixed thread pool that handles user's business logic - -Default: `1` - -**_maxConcurrentSessions_** +```shell +az login +``` -Controls the maximum number of concurrent sessions to process at any given time. +Your browser window will open and you will be prompted to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information. You do not need to save this output as it is saved in your system for Terraform to use. + +```shell +You have logged in. Now let us find all the subscriptions to which you have access... + +[ + { + "cloudName": "AzureCloud", + "homeTenantId": "home-Tenant-Id", + "id": "subscription-id", + "isDefault": true, + "managedByTenants": [], + "name": "Subscription-Name", + "state": "Enabled", + "tenantId": "0envbwi39-TenantId", + "user": { + "name": "your-username@domain.com", + "type": "user" + } + } +] +``` -Default: `1` +If you have more than one subscription, specify the subscription-id you want to use with command below: +```shell +az account set --subscription +``` -**_concurrency_** +### Provision the Resources -When `sessionsEnabled` is true, controls the maximum number of concurrent sessions to process at any given time. -When `sessionsEnabled` is false, controls the max concurrent calls of service bus message handler and the size of fixed thread pool that handles user's business logic. +After login Azure CLI with your account, now you can use the terraform script to create Azure Resources. -Deprecated, replaced with `maxConcurrentSessions` when `sessionsEnabled` is true and `maxConcurrentCalls` when `sessionsEnabled` is false +```shell +# In the root directory of the sample +# Initialize your Terraform configuration +terraform -chdir=./terraform init -Default: `1` +# Apply your Terraform Configuration +# Type `yes` at the confirmation prompt to proceed. +terraform -chdir=./terraform apply -**_sessionsEnabled_** +``` -Controls if is a session aware consumer. Set it to `true` if is a queue with sessions enabled. +It may take a few minutes to run the script. After successful running, you will see prompt information like below: + +```shell +azurecaf_name.resource_group: Creating... +azurecaf_name.azurecaf_name_servicebus: Creating... +azurecaf_name.resource_group: Creation complete ... +azurecaf_name.azurecaf_name_servicebus: Creation complete ... +azurerm_resource_group.main: Creating... +azurerm_resource_group.main: Creation complete ... +azurerm_servicebus_namespace.servicebus_namespace: Creating... +... +azurerm_servicebus_namespace.servicebus_namespace: Creation complete ... +azurerm_role_assignment.role_servicebus_data_owner: Creating... +azurerm_servicebus_topic.servicebus_topic: Creating... +azurerm_servicebus_topic.servicebus_topic: Creation complete ... +azurerm_servicebus_subscription.servicebus_subscription: Creating... +... +azurerm_servicebus_subscription.servicebus_subscription: Creation complete... +... +azurerm_role_assignment.role_servicebus_data_owner: Creation complete ... + +Apply complete! Resources: 7 added, 0 changed, 0 destroyed. + +Outputs: + +... +``` -Default: `false` +You can go to [Azure portal](https://ms.portal.azure.com/) in your web browser to check the resources you created. -**_requeueRejected_** +### Export Output to Your Local Environment +Running the command below to export environment values: -Controls if is a message that trigger any exception in consumer will be force to DLQ. -Set it to `true` if a message that trigger any exception in consumer will be force to DLQ. -Set it to `false` if a message that trigger any exception in consumer will be re-queued. +```shell + source ./terraform/setup_env.sh +``` -Default: `false` +## Run Locally -**_receiveMode_** +In your terminal, run `mvn clean spring-boot:run`. -The modes for receiving messages. -`PEEK_LOCK`, received message is not deleted from the queue or subscription, instead it is temporarily locked to the receiver, making it invisible to other receivers. +```shell +mvn clean spring-boot:run +``` -`RECEIVE_AND_DELETE`, received message is removed from the queue or subscription and immediately deleted. +## Verify This Sample -Default: `PEEK_LOCK` -**_enableAutoComplete_** +1. Verify in your app’s logs that similar messages were posted: -Enable auto-complete and auto-abandon of received messages. -'enableAutoComplete' is not needed in for RECEIVE_AND_DELETE mode. +```shell +... +New message received: 'Hello world, 2' +... +Message 'Hello world, 2' successfully checkpointed +... +New message received: 'Hello world, 3' +... +Message 'Hello world, 3' successfully checkpointed +... +``` -Default: `false` -### Set Service Bus message headers -The following table illustrates how Spring message headers are mapped to Service Bus message headers and properties. -When creat a message, developers can specify the header or property of a Service Bus message by below constants. +## Clean Up Resources +After running the sample, if you don't want to run the sample, remember to destroy the Azure resources you created to avoid unnecessary billing. -```java - @Autowired -private Sinks.Many> many; +The terraform destroy command terminates resources managed by your Terraform project. +To destroy the resources you created. -@PostMapping("/messages") -public ResponseEntity sendMessage(@RequestParam String message) { - many.emitNext(MessageBuilder.withPayload(message) - .setHeader(SESSION_ID, "group1") - .build(), - Sinks.EmitFailureHandler.FAIL_FAST); - return ResponseEntity.ok("Sent!"); - } +```shell +terraform -chdir=./terraform destroy ``` - -For some Service Bus headers that can be mapped to multiple Spring header constants, the priority of different Spring headers is listed. - -Service Bus Message Headers and Properties | Spring Message Header Constants | Type | Priority Number (Descending priority) -:---|:---|:---|:--- -ContentType | org.springframework.messaging.MessageHeaders.CONTENT_TYPE | String | N/A -CorrelationId | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.CORRELATION_ID | String | N/A -**MessageId** | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.MESSAGE_ID | String | 1 -**MessageId** | com.azure.spring.messaging.AzureHeaders.RAW_ID | String | 2 -**MessageId** | org.springframework.messaging.MessageHeaders.ID | UUID | 3 -PartitionKey | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.PARTITION_KEY | String | N/A -ReplyTo | org.springframework.messaging.MessageHeaders.REPLY_CHANNEL | String | N/A -ReplyToSessionId | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.REPLY_TO_SESSION_ID | String | N/A -**ScheduledEnqueueTimeUtc** | com.azure.spring.messaging.AzureHeaders.SCHEDULED_ENQUEUE_MESSAGE | Integer | 1 -**ScheduledEnqueueTimeUtc** | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.SCHEDULED_ENQUEUE_TIME | Instant | 2 -SessionID | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.SESSION_ID | String | N/A -TimeToLive | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.TIME_TO_LIVE | Duration | N/A -To | com.azure.spring.servicebus.support.ServiceBusMessageHeaders.TO | String | N/A - -## Troubleshooting - -## Next steps - -## Contributing - - - -[azure-account]: https://azure.microsoft.com/account/ -[azure-portal]: https://ms.portal.azure.com/ -[create-service-bus]: https://docs.microsoft.com/azure/service-bus-messaging/service-bus-create-namespace-portal -[create-azure-storage]: https://docs.microsoft.com/azure/storage/ -[create-sp-using-azure-cli]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/create-sp-using-azure-cli.md -[create-managed-identity]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/create-managed-identity.md -[deploy-spring-boot-application-to-app-service]: https://docs.microsoft.com/java/azure/spring-framework/deploy-spring-boot-java-app-with-maven-plugin?toc=%2Fazure%2Fapp-service%2Fcontainers%2Ftoc.json&view=azure-java-stable -[role-assignment]: https://docs.microsoft.com/azure/role-based-access-control/role-assignments-portal -[application-mi.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-mi.yaml -[application-sp.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-sp.yaml -[application.yaml]: https://github.com/Azure-Samples/azure-spring-boot-samples/blob/spring-cloud-azure_4.0/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application.yaml diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-mi.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-mi.yaml deleted file mode 100644 index 3cf151c8a..000000000 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-mi.yaml +++ /dev/null @@ -1,29 +0,0 @@ -spring: - cloud: - azure: - credential: - managed-identity-client-id: ${AZURE_MANAGED_IDENTITY_CLIENT_ID} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: topic - poller: - fixed-delay: 1000 - initial-delay: 0 diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-sp.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-sp.yaml deleted file mode 100644 index 6fae93f13..000000000 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application-sp.yaml +++ /dev/null @@ -1,30 +0,0 @@ -spring: - cloud: - azure: - credential: - client-id: ${AZURE_CLIENT_ID} - client-secret: ${AZURE_CLIENT_SECRET} - profile: - tenant-id: ${AZURE_TENANT_ID} - servicebus: - namespace: ${AZURE_SERVICEBUS_NAMESPACE} - stream: - function: - definition: consume;supply - bindings: - consume-in-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - group: ${AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME} - supply-out-0: - destination: ${AZURE_SERVICEBUS_TOPIC_NAME} - servicebus: - bindings: - consume-in-0: - consumer: - checkpoint-mode: MANUAL - supply-out-0: - producer: - entity-type: topic - poller: - fixed-delay: 1000 - initial-delay: 0 diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application.yaml b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application.yaml index e8c0a1a85..e4921de97 100644 --- a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application.yaml +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/src/main/resources/application.yaml @@ -2,7 +2,7 @@ spring: cloud: azure: servicebus: - connection-string: ${AZURE_SERVICEBUS_CONNECTION_STRING} + namespace: ${AZURE_SERVICEBUS_NAMESPACE} stream: function: definition: consume;supply @@ -22,4 +22,4 @@ spring: entity-type: topic poller: fixed-delay: 1000 - initial-delay: 0 \ No newline at end of file + initial-delay: 0 diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/main.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/main.tf new file mode 100644 index 000000000..4f353578b --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/main.tf @@ -0,0 +1,75 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 2.75" + } + azurecaf = { + source = "aztfmod/azurecaf" + version = "1.2.10" + } + } +} + +provider "azurerm" { + features {} +} + +resource "azurecaf_name" "resource_group" { + name = var.application_name + resource_type = "azurerm_resource_group" + random_length = 5 + clean_input = true +} + +resource "azurerm_resource_group" "main" { + name = azurecaf_name.resource_group.result + location = var.location + + tags = { + "terraform" = "true" + "application-name" = var.application_name + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurecaf_name" "azurecaf_name_servicebus" { + name = var.application_name + resource_type = "azurerm_servicebus_namespace" + random_length = 5 + clean_input = true +} + +resource "azurerm_servicebus_namespace" "servicebus_namespace" { + name = azurecaf_name.azurecaf_name_servicebus.result + location = var.location + resource_group_name = azurerm_resource_group.main.name + + sku = "Standard" + zone_redundant = false + + tags = { + "spring-cloud-azure-sample" = var.sample_tag_value + } +} + +resource "azurerm_servicebus_topic" "servicebus_topic" { + name = "tpc001" + namespace_id = azurerm_servicebus_namespace.servicebus_namespace.id +} + +resource "azurerm_servicebus_subscription" "servicebus_subscription" { + name = "sub001" + topic_id = azurerm_servicebus_topic.servicebus_topic.id + + max_delivery_count = 1 +} + +data "azurerm_client_config" "client_config" { +} + +resource "azurerm_role_assignment" "role_servicebus_data_owner" { + scope = azurerm_servicebus_namespace.servicebus_namespace.id + role_definition_name = "Azure Service Bus Data Owner" + principal_id = data.azurerm_client_config.client_config.object_id +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/outputs.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/outputs.tf new file mode 100644 index 000000000..2df7cbc84 --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/outputs.tf @@ -0,0 +1,14 @@ +output "AZURE_SERVICEBUS_NAMESPACE" { + value = azurerm_servicebus_namespace.servicebus_namespace.name + description = "The name of service bus namespace." +} + +output "AZURE_SERVICEBUS_TOPIC_NAME" { + value = azurerm_servicebus_topic.servicebus_topic.name + description = "The name of created topic in the service bus namespace." +} + +output "AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME" { + value = azurerm_servicebus_subscription.servicebus_subscription.name + description = "The name of created subscription in the service bus namespace." +} diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/setup_env.sh b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/setup_env.sh new file mode 100644 index 000000000..c1ec4ae3a --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/setup_env.sh @@ -0,0 +1,3 @@ +export AZURE_SERVICEBUS_NAMESPACE=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_NAMESPACE) +export AZURE_SERVICEBUS_TOPIC_NAME=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_TOPIC_NAME) +export AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME=$(terraform -chdir=./terraform output -raw AZURE_SERVICEBUS_TOPIC_SUBSCRIPTION_NAME) diff --git a/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/variables.tf b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/variables.tf new file mode 100644 index 000000000..af412df1a --- /dev/null +++ b/servicebus/spring-cloud-azure-stream-binder-servicebus/servicebus-topic-binder/terraform/variables.tf @@ -0,0 +1,17 @@ +variable "application_name" { + type = string + description = "The name of your application." + default = "servicebus-topic-binder" +} + +variable "location" { + type = string + description = "The Azure region where all resources in this example should be created." + default = "eastus" +} + +variable "sample_tag_value" { + type = string + description = "The value of spring-cloud-azure-sample tag." + default = "true" +}