Angular 11 + ActiveMQ Producer/Consumer + SpringBoot RestAPIs example

In the tutorial, we show how to Producer/Consumer data from ActiveMQ with Angular 11 & 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 11

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

16 thoughts on “Angular 11 + ActiveMQ Producer/Consumer + SpringBoot RestAPIs example”

  1. Every word in this piece of work is very clear and your passion for this topic shines. Please continue your work in this area and I hope to see more from you in the future.

  2. I like Your Article about Catholic Priests and Former Abortionist Confront Congress on Meaning of “Abortion” | Fr. Frank Pavone’s Blog Perfect just what I was searching for! .

  3. The graphic novel Red is a cruel and violent examination of the “price of freedom”, and the havoc that causes in the soul of those who accept to commit atrocities in order to protect the interests from a country.

  4. The the next occasion Someone said a weblog, I hope which it doesnt disappoint me around that one. Get real, Yes, it was my solution to read, but I just thought youd have something interesting to convey. All I hear is a bunch of whining about something you could fix when you werent too busy searching for attention.

  5. 719000 415919My wife style of bogus body art were being quite unsafe. Mother worked with gun first, right after which they your lover snuck no cost upon an tattoo ink ink. I was confident the fact just about every really should not be epidermal, due towards the tattoo ink could be attracted from the entire body. make an own temporary tattoo 102081

  6. Hello there! I know this is kind of off topic but I was wondering if you knew where I could
    get a captcha plugin for my comment form? I’m using the same blog
    platform as yours and I’m having difficulty finding one?
    Thanks a lot!

  7. When I originally commented I seem to have clicked the -Notify
    me when new comments are added- checkbox and from now
    on whenever a comment is added I recieve four emails with the exact same comment.
    Perhaps there is an easy method you are able to remove me from that service?

    Thanks!

  8. Its like you learn my thoughts! You seem to understand so much approximately this,
    like you wrote the ebook in it or something.
    I believe that you just can do with a few percent to force the message house a bit, however instead of that,
    that is wonderful blog. A fantastic read. I’ll definitely be
    back.

  9. I feel this is one of the so much vital information for me.
    And i am satisfied studying your article. But should statement on some general things,
    The web site taste is perfect, the articles is actually nice :
    D. Good task, cheers

  10. I want to to thank you for this fantastic read!! I certainly loved every bit of it.
    I have you book-marked to look at new stuff you post…

Leave a Reply

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