Sending message asynchronously (JSM)
Tags: java, spring, jms
Published: 2022-03-10 16:19:42

 JMS is a Java standard that defines a common API for working with message brokers.<br>
First introduced in 2001, JMS has been the go-to approach for asynchronous messaging<br> in Java for a very long time.

### JSM components :

Jms provide multiple entities :
  - Jms provider : broker of messages
  - Jms client : component Jms that can produce or consume a message
  - Message : Data that can exchange between component Jms

### JMS mode comunication :

JMS defines two modes for message delivery:
  - **Point to point** : in this mode a message is sent by a producer and is recieved by a single consumer. The support used for the 
                         implementation of this mode is the Queue.
  - **Publish/subscribe** : in this mode a message is sent by a producer and is reciebed by a single o multiple consumers. The support                   used for the implementation of this mode is the Topic.

Jms support multiple type of message (Text,Object,Stream octet,Map)

In the schema apove I explain how we can use Jms with activemq as broker
  
  ![JMS communication schema](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrdatq578m3vwhm2z4dn.png)


### Setting up JMS:
  - First we have to run a broker in my case I use [activeMq](https://activemq.apache.org/)
    you can download the file zip and after extract the file you can acces to the bin foler and run 
    ```bash 
    $ ./activemq console
    ```
    ![activemq-run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bujgd6c55gymv6g3y3af.png)

    Now we can acces to the web page of activeMq on [localhost](http://127.0.0.1:8161/index.html),
    by default activemq use admin as username and password
    
    ![Iactivemq-web-page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpemloe7ophsge01d590.png)

  - After config the broker we can now create a spring boot application, and add some essiential dependencies to pom.xml for using jms and activemq
  ```xml
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
  ```
<table>
      <tr>
        <td>Property</td>
        <td>Description</td>
      </tr>
    <tbody>
      <tr>
        <td>spring.activemq.broker-url</td>
        <td>The URL of the broker</td>
      </tr>
      <tr>
        <td>spring.activemq.user</td>
        <td>The user for accessing the broker (optional)</td>
      </tr>
      <tr>
        <td>spring.activemq.password</td>
        <td>The password for accessing the broker (optional)</td>
      </tr>
    </tbody>
  </table>

  in the application.yml of spring application we can implement does properties : 
  ```yml
  spring:
    activemq:
    broker-url: tcp://127.0.0.1:8161
    user: admin
    password: admin
  ```  
  - After config the application we can now sending messages by using JmsTemplate that provide by spring.JmsTemplate eliminates a lot    of boiler plate code that would otherwise be required to work with JMS. 
  JmsTemplate has several methods that are useful for sending messages, including the following:
    ```java
      // Send raw messages
      void send(MessageCreator messageCreator) throws JmsException;
      void send(Destination destination, MessageCreator messageCreator) throws JmsException;
      void send(String destinationName, MessageCreator messageCreator) throws JmsException;
      // Send messages converted from objects
      void convertAndSend(Object message) throws JmsException;
      void convertAndSend(Destination destination, Object message) throws JmsException;
      void convertAndSend(String destinationName, Object message) throws JmsException;
    ```
  
  To use jmsTemplate we can just inject a bean of type JmsTemplate 
  ```java
    @Autowired
    private JmsTemplate jmsTemplate;
  ```
  and we can create a function to send message :
  ```java
    public void sendMessage() throws Exception {
          jmsTemplate.convertAndSend("queue.test","Hello world !!");
    }
  ```
  as we can see in the code we use convertAndSend function to send a message to the queue (destination) queue.test
  We can also create Destination by create function in a config class the return bean type Destination
  ```java
    @Bean
    public Destination destination() {
      return new ActiveMQQueue("queue.test");
    }
  ```
  After create the bean we can use it in differante component
   ```java
    @Autowired
    private Destination destination;
    @Autowired
    private JmsTemplate jmsTemplate;
    
    public void sendMessage() throws Exception {
            jmsTemplate.convertAndSend(destination,"Hello world !!");
    }
  ```
  - Now it came the time to consume our message and we can do it by using jmsTemple with specifique functions that provide for us to recieve a message
  ```java
    Message receive() throws JmsException;
    Message receive(Destination destination) throws JmsException;
    Message receive(String destinationName) throws JmsException;
    Object receiveAndConvert() throws JmsException;
    Object receiveAndConvert(Destination destination) throws JmsException;
    Object receiveAndConvert(String destinationName) throws JmsException;
  ```
  The receive() methods receive a raw Message, whereas the receiveAndConvert() methods use a configured message converter to convert messages into domain types.And for each of these, you can specify either a Destination or a String containing the destination name.
  ```java
    @Autowired
    private Destination destination;
    @Autowired
    private JmsTemplate jmsTemplate;
    
    public void consumeMessage(Message message) throws Exception {
            Message message = jmsTemplate.receiveAndConvert(destination);
            //...
    }
  ```
  and we can also use a message listener by annotad a method in a component with @JmsListener.
  ```java
    @Service
    public class Reciever{
      @JmsListener(destination = "queue.test")
      public void reciever(@Payload Message msg) throws JMSException {
          String message = ((TextMessage) msg).getText();
          System.out.println(message);
      }
    }
  ```
  The reciever() method is annotated with JmsListener to “listen” for messages on the "queue.test" destination. It doesn’t deal with JmsTemplate, nor is it explicitly invoked by your application code. Instead, framework code within Spring waits for messages to arrive on the specified destination, and when they arrive, the reciever() method is invoked automatically with the message’s Message payload as a parameter.