Angular 9 + ActiveMQ Producer/Consumer + SpringBoot RestAPIs example

In the tutorial, we show how to Producer/Consumer data from ActiveMQ with Angular 9 & SpringBoot RestAPIs.

Related posts:
ActiveMQ Producer/Consumer + SpringBoot RestAPIs example
RabbitMq – How to create Spring RabbitMq Publish/Subcribe pattern with SpringBoot
How to use Spring Kafka JsonSerializer (JsonDeserializer) to produce/consume Java Object messages

Related Pages:

Technologies

  • Java 1.8
  • Maven 3.3.9
  • Spring Tool Suite – Version 3.9.4.RELEASE
  • Spring Boot: 2.0.3.RELEASE
  • ActiveMQ
  • Angular 9

Overview

We create a Spring JMS ActiveMQ with JMS Producer & JMS Consumer as below:

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer +springboot-project-structure

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer + activemq-producer-consumer

Then expose RestAPIs to POST/GET data to/from ActiveMQ:

  • @PostMapping(value="/api/task")
  • @GetMapping(value="/api/tasks")

Use Angular Client to submit/get data from ActiveMQ via above RestAPI:

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer +add-new-task

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer +processing-all-task

ActiveMQ state:

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer + activemq-state

Practice

SpringBoot Backend

Setup SpringBoot project

Use SpringToolSuite to create a SpringBoot project with below dependencies:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-broker</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

ActiveMQ Connection Factory

ActiveMqConnectionFactoryConfig ->


package com.ozenero.activemq.config;

import javax.jms.ConnectionFactory;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

@Configuration
public class ActiveMqConnectionFactoryConfig {

	@Value("${gkz.activemq.broker.url}")
	String brokerUrl;
	
	@Value("${gkz.activemq.borker.username}")
	String userName;
	
	@Value("${gkz.activemq.borker.password}")
	String password;

	/*
	 * Initial ConnectionFactory
	 */
    @Bean
    public ConnectionFactory connectionFactory(){
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
        connectionFactory.setBrokerURL(brokerUrl);
        connectionFactory.setUserName(userName);
        connectionFactory.setPassword(password);
        return connectionFactory;
    }
    
	@Bean // Serialize message content to json using TextMessage
	public MessageConverter jacksonJmsMessageConverter() {
	    MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
	    converter.setTargetType(MessageType.TEXT);
	    converter.setTypeIdPropertyName("_type");
	    return converter;
	}
    
    /*
     * Used for Receiving Message
     */
    @Bean
    public JmsListenerContainerFactory jsaFactory(ConnectionFactory connectionFactory,
                                                    DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setMessageConverter(jacksonJmsMessageConverter());
        configurer.configure(factory, connectionFactory);
        return factory;
    }
 
    /*
     * Used for Sending Messages.
     */
    @Bean
    public JmsTemplate jmsTemplate(){
        JmsTemplate template = new JmsTemplate();
        template.setMessageConverter(jacksonJmsMessageConverter());
        template.setConnectionFactory(connectionFactory());
        return template;
    }
}

Add ActiveMQ configuration in application.properties ->


gkz.activemq.broker.url=tcp://localhost:61616
gkz.activemq.borker.username=admin
gkz.activemq.borker.password=admin
gkz.activemq.queue=gkz-queue

Data Model

– Create Task model ->


package com.ozenero.activemq.model;

public class Task {
	private Long id;
	private String name;
	
	public Task(){
	}
	
	public Task(Long id, String name){
		this.id = id;
		this.name = name;
	}
	
	public Long getId() {
		return id;
	}
	
	public void setId(Long id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
}

– Create MessageStorage to storage Task list ->


package com.ozenero.activemq.model;

import java.util.ArrayList;
import java.util.List;

public class MessageStorage {
	private List tasks = new ArrayList();
	
	public void add(Task task) {
		tasks.add(task);
	}
	
	public void clear() {
		tasks.clear();
	}
	
	public List getAll(){
		return tasks;
	}
}

Create a bean for MessageStorage ->


package com.ozenero.activemq.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ozenero.activemq.model.MessageStorage;

@Configuration
public class BeanConfiguration {

  @Bean
  public MessageStorage customerStorage() {
    return new MessageStorage();
  }
}

JMS Producer

JmsProducer send messages to ActiveMQ ->


package com.ozenero.activemq.jms.producer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

import com.ozenero.activemq.model.Task;

@Component
public class JmsProducer {
	@Autowired
	JmsTemplate jmsTemplate;
	
	@Value("${gkz.activemq.queue}")
	String queue;
	
	public void send(Task task){
		jmsTemplate.convertAndSend(queue, task);
	}
}

JMS Consumer

JmsConsumer recieves messages from ActiveMQ ->


package com.ozenero.activemq.jms.consumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import com.ozenero.activemq.model.MessageStorage;
import com.ozenero.activemq.model.Task;

@Component
public class JmsConsumer {
	@Autowired
	private MessageStorage taskStorage;
	
	@JmsListener(destination = "${gkz.activemq.queue}", containerFactory="jsaFactory")
	public void receive(Task task){
		taskStorage.add(task);
	}
}

Rest APIs

RestAPI ->


package com.ozenero.activemq.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.ozenero.activemq.jms.producer.JmsProducer;
import com.ozenero.activemq.model.MessageStorage;
import com.ozenero.activemq.model.Task;

@CrossOrigin(origins = "http://localhost:4200")
@RestController
public class RestAPIs {
	
	@Autowired
	JmsProducer jmsProducer;
	
	@Autowired
	private MessageStorage taskStorage;
	
	@PostMapping(value="/api/task")
	public Task postCustomer(@RequestBody Task task){
		jmsProducer.send(task);
		return task;
	}
	
	@GetMapping(value="/api/tasks")
	public List getAll(){
		List tasks = new ArrayList(taskStorage.getAll());
		taskStorage.clear();
		return tasks;
	}
}

Angular Frontend

Setup Angular Project

Setup guide:

  • Create Angular project with commandline: ng new angular6-client
  • Generate Task model with commandline ng generate class Task
  • Generate TaskService with commandline ng generate service Task
  • Generate TaskComponent with commandline ng generate component Task

Then install Bootstrap by commandline ->

>npm install bootstrap jquery --save

Configure installed Bootstrap & JQuery in angular.json file ->


...
 
"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.min.js",
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
]
 
...

Data Model

Task ->


export class Task {
    id: number;
    name: string;
}

Implement HttpClient Service

TaskService ->


import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Task } from './task';
 
const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
 
@Injectable({
  providedIn: 'root'
})
export class   {
  constructor( 
    private http: HttpClient
  ) { }
 
  getTasks (): Observable {
    return this.http.get("http://localhost:8080/api/tasks")
  }
  
  addTask (task: Task): Observable {
    return this.http.post("http://localhost:8080/api/task", task, httpOptions);
  }
}

Implement ActiveMQ Component

activemq-task.component.html ->

<div [hidden]="submitted">
    <h3>Add Task</h3>
    <form #addTaskForm="ngForm">

      <div class="form-group">
        <label for="id">Id</label>
        <input type="number" class="form-control" id="id" 
        placeholder="Enter Id"
        required
        [(ngModel)]="task.id" name="id" #id="ngModel">
        <div [hidden]="id.valid || id.pristine"
              class="alert alert-danger">
            Id is required
        </div>
      </div>
 
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" placeholder="Enter Task Name" 
        required
        [(ngModel)]="task.name" name="name" #name="ngModel">
        <div [hidden]="name.valid || name.pristine"
             class="alert alert-danger">
            Name is required
        </div>
      </div>  
      <button class="btn btn-dark" (click)="addTask()" [disabled]="!addTaskForm.form.valid">Add Task</button>
    </form>
</div>
 
<div [hidden]="!submitted">
  <p>Submitted Successfully! -> <span class="badge badge-dark">Task's Info -> Id: {{task.id}}, Name: {{task.name}}</span></p>
	<div class="btn-group btn-group-sm">
    <button class="btn btn-secondary" (click)="newTask(); addTaskForm.reset();">New Task</button>
    <button class="btn btn-secondary" (click)="processTasks();">Process Tasks</button>
  </div>
  <div [hidden]="!processing">
      <br>
      <h5>Processing</h5>
      <ul>
        <li *ngFor="let task of tasks">
          Task id = {{task.id}}, name = {{task.name}}
        </li>
      </ul>
  </div>
</div>

activemq-task.component.ts ->


import { Component, OnInit } from '@angular/core';
import { Task } from '../task';
import { TaskService } from '../task.service';

@Component({
  selector: 'app-activemq-task',
  templateUrl: './activemq-task.component.html',
  styleUrls: ['./activemq-task.component.css']
})
export class ActivemqTaskComponent{

  task = new Task();
  tasks: Task[];
  submitted = false;
  processing = false;

  constructor(private taskService: TaskService) { }

  newTask(): void {
    this.submitted = false;
    this.processing = false;
    this.task = new Task();
  }
 
  addTask() {
    this.submitted = true;
    this.taskService.addTask(this.task)
    .subscribe();
  }

  processTasks(){
    this.processing = true;
    return this.taskService.getTasks()
                .subscribe(
                  tasks => {
                    console.log(tasks);
                    this.tasks = tasks;
                  }
                );
  }
}

– Create style file assets/forms.css ->


.ng-valid[required], .ng-valid.required  {
    border-left: 5px solid rgba(32, 77, 32, 0.623);
}
 
.ng-invalid:not(form)  {
    border-left: 5px solid rgb(148, 27, 27);
}

– Add above style file assets/forms.css to index.html file ->

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Angular6Client</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="assets/forms.css">
</head>
<body>
  <app-root></app-root>
</body>
</html>

– Add app-activemq-task selector to app.component.html file ->

<div class="container">
  <div class="row">
    <div class="col-sm-4"> 
      <app-activemq-task></app-activemq-task>
   </div>
  </div>
</div>

Run & Check Results

– Build & Run SpringBoot project with commandlines {mvn clean install, mvn spring-boot:run}.
– Run the Angular project as commandline: ng serve

Add Task ->

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer +add-new-task

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer +submit-successfully

Get Tasks ->

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer +processing-all-task

ActiveMQ state ->

Angular-6-Spring-Boot-RestAPI-ActiveMQ-Producer-Consumer + activemq-state

SourceCode

SpringBootRestAPIsActiveMQ
Angular6-Client

One thought on “Angular 9 + ActiveMQ Producer/Consumer + SpringBoot RestAPIs example”

Leave a Reply

Your email address will not be published. Required fields are marked *