Skip to main content Link Menu Expand (external link) Document Search Copy Copied

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

  1. In the spring-petclinic-visits-service directory, create a new src/main/java/org/springframework/samples/petclinic/visits/entities subdirectory and add a VisitRequest.java class 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;
        }
    }
    
  2. In the same directory, add a VisitResponse.java class 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;
        }
    }
    
  3. In the spring-petclinic-visits-service directory, create a new src/main/java/org/springframework/samples/petclinic/visits/config subdirectory and add a MessagingConfig.java class 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;
        }
    }
    
  4. In the same directory, add a QueueConfig.java class 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;
        }   
    }
    
  5. In the spring-petclinic-visits-service directory, create a new src/main/java/org/springframework/samples/petclinic/visits/service subdirectory and add a VisitsReceiver.java class 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 VisitsReceiver service is listening to the visits-requests queue. Each time a message is present on the queue, it will dequeue this message and save a new Visit in the database. In the next step, you will verify it by having it sent a confirmation message to the visits-confirmations queue.

  6. Rebuild the spring-petclinic-visits-service microservice

    cd ..
    mvn clean package -DskipTests -rf :spring-petclinic-visits-service
    
  7. Navigate to the staging-acr directory, 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
    
  8. Navigate to the kubernetes folder and update the spring-petclinic-visits-service.yml file so it also contains an environment variable for the SPRING_JMS_SERVICEBUS_CONNECTIONSTRING. Add the below at the bottom of the existing environment variables below line 39 and before the imagePullPolicy.

            - name: SPRING_JMS_SERVICEBUS_CONNECTIONSTRING
              valueFrom:
                secretKeyRef:
                  name: sbsecret
                  key: sbconn
    
  9. In the same file, below the environment variable you just added and again right abobe the imagePullPolicy, add a volumeMounts section.

            volumeMounts:
            - name: secrets-store01-inline
              mountPath: "/mnt/secrets-store"
              readOnly: true
    
  10. In the same file, below the serviceAccountName: workload-identity-sa on line 18, add a volumes section:

          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
    
  11. Reapply the yaml definition on the AKS cluster.

    cd ../kubernetes
    kubectl apply -f spring-petclinic-visits-service.yml
    
  12. Double check that the visits-service started up correctly.

    kubectl get pods -w
    
  13. To validate the resulting functionality, in the Azure Portal, navigate back to the page of the visits-requests queue of the Service Bus namespace you deployed earlier in this lab.

  14. On the Overview page of the visits-requests queue, verify that there are no active messages.

  15. In the web browser window, open another tab and navigate to the public endpoint of the api-gateway service.

  16. On the Welcome to Petclinic page, select Owners and, in the drop-down menu, select All.

  17. In the list of owners, select the first entry (George Franklin).

  18. 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.