Add the message producers and listeners
You will next add the code required to send and receive messages to the visits service. The message-emulator will send a PetClinicMessageRequest to the visits-requests queue. The visits service will need to listen to this queue and each time a VisitRequest message is submitted, it will create a new Visit for the pet ID referenced in the message. The visits service will also send back a VisitResponse as a confirmation to the visits-confirmations queue. This is the queue the message-emulator is listening to.
Step by step guidance
-
In the
spring-petclinic-visits-servicedirectory, create a newsrc/main/java/org/springframework/samples/petclinic/visits/entitiessubdirectory and add aVisitRequest.javaclass file containing the following code:package org.springframework.samples.petclinic.visits.entities; import java.io.Serializable; import java.util.Date; public class VisitRequest implements Serializable { private static final long serialVersionUID = -249974321255677286L; private Integer requestId; private Integer petId; private String message; public VisitRequest() { } public Integer getRequestId() { return requestId; } public void setRequestId(Integer id) { this.requestId = id; } public Integer getPetId() { return petId; } public void setPetId(Integer petId) { this.petId = petId; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } -
In the same directory, add a
VisitResponse.javaclass containing the following code:package org.springframework.samples.petclinic.visits.entities; public class VisitResponse { Integer requestId; Boolean confirmed; String reason; public VisitResponse() { } public VisitResponse(Integer requestId, Boolean confirmed, String reason) { this.requestId = requestId; this.confirmed = confirmed; this.reason = reason; } public Boolean getConfirmed() { return confirmed; } public void setConfirmed(Boolean confirmed) { this.confirmed = confirmed; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public Integer getRequestId() { return requestId; } public void setRequestId(Integer requestId) { this.requestId = requestId; } } -
In the
spring-petclinic-visits-servicedirectory, create a newsrc/main/java/org/springframework/samples/petclinic/visits/configsubdirectory and add aMessagingConfig.javaclass file containing the following code:package org.springframework.samples.petclinic.visits.config; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MessageConverter; import org.springframework.samples.petclinic.visits.entities.VisitRequest; import org.springframework.samples.petclinic.visits.entities.VisitResponse; @Configuration public class MessagingConfig { @Bean public MessageConverter jackson2Converter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); Map<String, Class<?>> typeMappings = new HashMap<String, Class<?>>(); typeMappings.put("visitRequest", VisitRequest.class); typeMappings.put("visitResponse", VisitResponse.class); converter.setTypeIdMappings(typeMappings); converter.setTypeIdPropertyName("messageType"); return converter; } } -
In the same directory, add a
QueueConfig.javaclass file containing the following code:package org.springframework.samples.petclinic.visits.config; import org.springframework.beans.factory.annotation.Value; public class QueueConfig { @Value("${spring.jms.queue.visits-requests:visits-requests}") private String visitsRequestsQueue; public String getVisitsRequestsQueue() { return visitsRequestsQueue; } } -
In the
spring-petclinic-visits-servicedirectory, create a newsrc/main/java/org/springframework/samples/petclinic/visits/servicesubdirectory and add aVisitsReceiver.javaclass file containing the following code:package org.springframework.samples.petclinic.visits.service; import java.util.Date; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.jms.annotation.JmsListener; import org.springframework.jms.core.JmsTemplate; import org.springframework.samples.petclinic.visits.entities.VisitRequest; import org.springframework.samples.petclinic.visits.entities.VisitResponse; import org.springframework.samples.petclinic.visits.model.Visit; import org.springframework.samples.petclinic.visits.model.VisitRepository; import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Component @Slf4j @RequiredArgsConstructor public class VisitsReceiver { private final VisitRepository visitsRepository; private final JmsTemplate jmsTemplate; @JmsListener(destination = "visits-requests") void receiveVisitRequests(VisitRequest visitRequest) { log.info("Received message: {}", visitRequest.getMessage()); try { Visit visit = new Visit(null, new Date(), visitRequest.getMessage(), visitRequest.getPetId()); visitsRepository.save(visit); jmsTemplate.convertAndSend("visits-confirmations", new VisitResponse(visitRequest.getRequestId(), true, "Your visit request has been accepted")); } catch (Exception ex) { log.error("Error saving visit: {}", ex.getMessage()); jmsTemplate.convertAndSend("visits-confirmations", new VisitResponse(visitRequest.getRequestId(), false, ex.getMessage())); } } }This
VisitsReceiverservice is listening to thevisits-requestsqueue. Each time a message is present on the queue, it will dequeue this message and save a newVisitin the database. In the next step, you will verify it by having it sent a confirmation message to thevisits-confirmationsqueue. -
Rebuild the spring-petclinic-visits-service microservice
cd .. mvn clean package -DskipTests -rf :spring-petclinic-visits-service -
Navigate to the
staging-acrdirectory, copy the jar file of the visit-service and rebuild the container.cd staging-acr rm spring-petclinic-visits-service-$VERSION.jar cp ../spring-petclinic-visits-service/target/spring-petclinic-visits-service-$VERSION.jar spring-petclinic-visits-service-$VERSION.jar docker build -t $MYACR.azurecr.io/spring-petclinic-visits-service:$VERSION \ --build-arg ARTIFACT_NAME=spring-petclinic-visits-service-$VERSION.jar \ --build-arg APP_PORT=8080 \ --build-arg AI_JAR=ai.jar \ . docker push $MYACR.azurecr.io/spring-petclinic-visits-service:$VERSION -
Navigate to the kubernetes folder and update the
spring-petclinic-visits-service.ymlfile so it also contains an environment variable for theSPRING_JMS_SERVICEBUS_CONNECTIONSTRING. Add the below at the bottom of the existing environment variables below line 39 and before theimagePullPolicy.- name: SPRING_JMS_SERVICEBUS_CONNECTIONSTRING valueFrom: secretKeyRef: name: sbsecret key: sbconn -
In the same file, below the environment variable you just added and again right abobe the
imagePullPolicy, add avolumeMountssection.volumeMounts: - name: secrets-store01-inline mountPath: "/mnt/secrets-store" readOnly: true -
In the same file, below the
serviceAccountName: workload-identity-saon line 18, add avolumessection:volumes: - name: secrets-store01-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "azure-kvname-user-msi"The resulting spring-petclinic-visits-service.yml file should look like this. Also double check that the secretKeyRef name and key of the existing environment variables and the volumes and volumeMounts names, are the same as what you set previously when you configured Key Vault integration.
apiVersion: apps/v1 kind: Deployment metadata: labels: app: visits-service name: visits-service spec: replicas: 1 selector: matchLabels: app: visits-service template: metadata: labels: app: visits-service azure.workload.identity/use: "true" spec: serviceAccountName: workload-identity-sa volumes: - name: secrets-store01-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "azure-kvname-user-msi" containers: - image: <your-registry-name>.azurecr.io/spring-petclinic-visits-service:2.7.6 name: visits-service env: - name: "CONFIG_SERVER_URL" valueFrom: configMapKeyRef: name: config-server key: CONFIG_SERVER_URL - name: "APPLICATIONINSIGHTS_CONNECTION_STRING" valueFrom: configMapKeyRef: name: config-server key: APPLICATIONINSIGHTS_CONNECTION_STRING - name: "APPLICATIONINSIGHTS_CONFIGURATION_CONTENT" value: >- { "role": { "name": "visits-service" } } - name: SPRING_JMS_SERVICEBUS_CONNECTIONSTRING valueFrom: secretKeyRef: name: sbsecret key: sbconn volumeMounts: - name: secrets-store01-inline mountPath: "/mnt/secrets-store" readOnly: true imagePullPolicy: Always livenessProbe: failureThreshold: 3 httpGet: path: /actuator/health port: 8080 scheme: HTTP initialDelaySeconds: 180 successThreshold: 1 readinessProbe: failureThreshold: 3 httpGet: path: /actuator/health port: 8080 scheme: HTTP initialDelaySeconds: 10 successThreshold: 1 ports: - containerPort: 8080 name: http protocol: TCP - containerPort: 9779 name: prometheus protocol: TCP - containerPort: 8778 name: jolokia protocol: TCP securityContext: privileged: false --- apiVersion: v1 kind: Service metadata: labels: app: visits-service name: visits-service spec: ports: - port: 8080 protocol: TCP targetPort: 8080 selector: app: visits-service type: ClusterIP -
Reapply the yaml definition on the AKS cluster.
cd ../kubernetes kubectl apply -f spring-petclinic-visits-service.yml -
Double check that the visits-service started up correctly.
kubectl get pods -w -
To validate the resulting functionality, in the Azure Portal, navigate back to the page of the
visits-requestsqueue of the Service Bus namespace you deployed earlier in this lab. -
On the Overview page of the
visits-requestsqueue, verify that there are no active messages. -
In the web browser window, open another tab and navigate to the public endpoint of the
api-gatewayservice. -
On the Welcome to Petclinic page, select Owners and, in the drop-down menu, select All.
-
In the list of owners, select the first entry (George Franklin).
-
On the Owner Information page, in the Pets and Visits section, verify the presence of an entry representing the message you submitted earlier in this lab.