Monday, October 22, 2018

Angular Message Queue for Asynchronous Processing (ngx-message-queue)

ngx-message-queue

A message queue library for Angular 2+ application components to communication each other. ngx-message-queue library supports message headers and message payload. It also supports message selectors for subscribers to subscribe to only messages with specific message header via msgSelector.
Selectors are a way of attaching a filter to a subscription to perform content based routing. Selectors are typically apply to message headers, If no headers are attached to the message then all the messages will be delivered to subscriber by default. If subscriber is not configuring the msgSelector then all the messages will be delivered to the subscribers i.e. no filters are applied.
Multiple conditions are allowed in the msgSelector.

ngx-message-queue was previously knowns as ng2-message-queue.
Example of a message selectors
LogLevel=fatal

age > 13

age>=13 & gender=M

Filter expression can be grouped using parenthesis

age<=5 & ((gender=M & state=NJ) | gender=F)
Supported Operators
=, !=, >, <, >=, <=, &, |
Message header supports only JSON object. Multiple header properties can be posted with single JSON object
{
"id": "1234567890",
"age": "20"
}

Message payload can be of any type (string, integer, object, JSON etc)

Index

Install

npm install ngx-message-queue

Usage

Import into Angular 2+ application (typescript)

ngx-message-queue is implemented as Angular 2+ injectable service name NgxMessageQueue.
For apps using angular version 6+
NgxMessageQueue is Injectable as root so no additional decalaration is required in any module.
For apps using angular version prior to 6
Add NgxMessageQueue into module providers.
import { NgxMessageQueue } from 'ngx-message-queue';

@NgModule({
 providers: [NgxMessageQueue]
})
Each component to use NgxMessageQueue
import { NgxMessageQueue } from 'ngx-message-queue';

export class MyComponent {

 constructor(private messageQueue: NgxMessageQueue) { }

}

API

createQueue(name: string): boolean
createQueue will create queue name.
Return false if queue name exist.
this.messageQueue.createQueue('myQueue');
deleteQueue(name: string): boolean
deleteQueue will delete queue name.
Return false if queue name does not exist.
this.messageQueue.deleteQueue('myQueue');
getQueueNames(): string[]
getQueueNames will return all queue name in string array.
let qNames: string[] = this.messageQueue.getQueueNames();
getSubscribers(): string[]
getSubscribers will return all subscribers id in string array.
let ids: string[] = this.messageQueue.getSubscribers();
publish(name: string, headers: any, message: any, lazy = true): boolean
publish will put message into queue name. It will also put headers into queue if any. headers are optional but it is best way to process/route/filter the messages quickly without parsing the message payload.
If lazy = true(default), queue name will be created automatically if not exist yet.
Return true if successful.
Return false if any of following is true:
  • lazy = false, and queue name does not exist.
  • name is undefined.
  • message is undefined.
// lazy mode
message = 'This is a test message';
this.mq.publish('myQueue', {}, message);
subscribe(name: string, msgSelector: string, callback: (any, any) => void, lazy = true): string
subscribe will subscribe for the message posted on the queue name. Whenever queue name receives a new message, callback will be invoked. The callback will return both headers and message payload.
Subscribers can subscribe to only certain messages within the same queue. Let us say you have multiple subscribers listening for log messages with different logging level (debug, info, warn, fatal etc). If you want to configure high priority subscriber who listens for logs with 'fatal' level then use msgSelector as loglevel=fatal
this.mq.subscribe('myQueue', 'loglevel=fatal', (headers, message) => this.handleMessage(headers, message));
If lazy = true(default), queue name will be created automatically if not exist yet.
Return subscription id if successful.
Return empty string if any of following is true:
  • lazy = false, and queue name does not exist.
  • name is undefined.
  • callback is undefined.
Either use Lambda(fat arrow) in typescript to pass in callback or bind this to another variable in javascript, else this scope will be lost.
Lambda(fat arrow)
ngOnInit() {
 this.mq.subscribe('testqueue', '', (headers, message) => this.handleMessage(headers, message));
}

handleMessage(headers, message) {
 console.log(message);
}
unsubscribe(id: string): boolean
unsubscribe will cancel subscription using id.
unsubscribe will return false if id is undefined or id is not found in subscription list.
id: string;

this.messageQueue.unsubscribe(this.id);

Example

Sunday, September 23, 2018

Angular App Integration with Spring Boot

Developing angular app is easy but deploying it in cloud environment has its own challenges. Spring boot application helps to overcome many of the cloud deployment challenges. Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run". 

Angular apps can be used as static resources in a web application which uses Spring Boot. In this post I'll show you how to marry Angular App and Sprint Boot web application.

The technology stack required for building angular app are Node.js and Angular CLI. In the example I am using Gradle to build & package the web application.

Node.js is used to install the required packages and dependencies for the angular application.

Angular CLI is used to generate the boilerplate code for angular app using command line interface.

Gradle hepls to build, package the application and automates the deployments.

I am not going to explaing much in details about these tools or frameworks.

Project Structure


The anugular app source files are placed in webapp folder(src\main\webapp) within a spring boot application. The startup Application.java class is annotated with @SpringBootApplcation is placed in src\main\java\com\jsp\jsonformatter\angularboot\.

  1 
package com.jsp.jsonformatter.angularboot;
  2 
 
  3 
import org.springframework.boot.SpringApplication;
  4 
import org.springframework.boot.autoconfigure.SpringBootApplication;
  5 
 
  6 
@SpringBootApplication
  7 
public class Application {
  8 
 
  9 
    public static void main(String[] args) {
 10 
        SpringApplication.run(Application.class, args);
 11 
    }
 12 
}

Angular build configuration

The anugular application can be build using ng build and it can be executed & tested using ng serve  You need to install the required packages and dependencies using npm install The ng build or ng serve uses the angular-cli.json or angular.json to build & package the angular application.



   1 
{
   2 
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
   3 
  "version": 1,
   4 
  "newProjectRoot": "projects",
   5 
  "projects": {
   6 
    "webapp": {
   7 
      "root": "",
   8 
      "sourceRoot": "src",
   9 
      "projectType": "application",
  10 
      "prefix": "app",
  11 
      "schematics": {},
  12 
      "architect": {
  13 
        "build": {
  14 
          "builder": "@angular-devkit/build-angular:browser",
  15 
          "options": {
  16 
            "outputPath": "dist/static",
  17 
            "index": "src/index.html",
  18 
            "main": "src/main.ts",
  19 
            "polyfills": "src/polyfills.ts",
  20 
            "tsConfig": "src/tsconfig.app.json",
  21 
            "assets": [
  22 
              "src/favicon.ico",
  23 
              "src/assets"
  24 
            ],
  25 
            "styles": [
  26 
              "src/styles.scss"
  27 
            ],
  28 
            "scripts": []
  29 
          },
  30 
          "configurations": {
  31 
            "production": {
  32 
              "fileReplacements": [
  33 
                {
  34 
                  "replace": "src/environments/environment.ts",
  35 
                  "with": "src/environments/environment.prod.ts"
  36 
                }
  37 
              ],
  38 
              "optimization": true,
  39 
              "outputHashing": "all",
  40 
              "sourceMap": false,
  41 
              "extractCss": true,
  42 
              "namedChunks": false,
  43 
              "aot": true,
  44 
              "extractLicenses": true,
  45 
              "vendorChunk": false,
  46 
              "buildOptimizer": true
  47 
            }
  48 
          }
  49 
        },
  50 
        "serve": {
  51 
          "builder": "@angular-devkit/build-angular:dev-server",
  52 
          "options": {
  53 
            "browserTarget": "webapp:build"
  54 
          },
  55 
          "configurations": {
  56 
            "production": {
  57 
              "browserTarget": "webapp:build:production"
  58 
            }
  59 
          }
  60 
        },
  61 
        "extract-i18n": {
  62 
          "builder": "@angular-devkit/build-angular:extract-i18n",
  63 
          "options": {
  64 
            "browserTarget": "webapp:build"
  65 
          }
  66 
        },
  67 
        "test": {
  68 
          "builder": "@angular-devkit/build-angular:karma",
  69 
          "options": {
  70 
            "main": "src/test.ts",
  71 
            "polyfills": "src/polyfills.ts",
  72 
            "tsConfig": "src/tsconfig.spec.json",
  73 
            "karmaConfig": "src/karma.conf.js",
  74 
            "styles": [
  75 
              "src/styles.css"
  76 
            ],
  77 
            "scripts": [],
  78 
            "assets": [
  79 
              "src/favicon.ico",
  80 
              "src/assets"
  81 
            ]
  82 
          }
  83 
        },
  84 
        "lint": {
  85 
          "builder": "@angular-devkit/build-angular:tslint",
  86 
          "options": {
  87 
            "tsConfig": [
  88 
              "src/tsconfig.app.json",
  89 
              "src/tsconfig.spec.json"
  90 
            ],
  91 
            "exclude": [
  92 
              "**/node_modules/**"
  93 
            ]
  94 
          }
  95 
        }
  96 
      }
  97 
    },
  98 
    "webapp-e2e": {
  99 
      "root": "e2e/",
 100 
      "projectType": "application",
 101 
      "architect": {
 102 
        "e2e": {
 103 
          "builder": "@angular-devkit/build-angular:protractor",
 104 
          "options": {
 105 
            "protractorConfig": "e2e/protractor.conf.js",
 106 
            "devServerTarget": "webapp:serve"
 107 
          },
 108 
          "configurations": {
 109 
            "production": {
 110 
              "devServerTarget": "webapp:serve:production"
 111 
            }
 112 
          }
 113 
        },
 114 
        "lint": {
 115 
          "builder": "@angular-devkit/build-angular:tslint",
 116 
          "options": {
 117 
            "tsConfig": "e2e/tsconfig.e2e.json",
 118 
            "exclude": [
 119 
              "**/node_modules/**"
 120 
            ]
 121 
          }
 122 
        }
 123 
      }
 124 
    }
 125 
  },
 126 
  "defaultProject": "webapp"
 127 
}

Take a closer look at output path property. The outputPath is defined as dist\static. This is important as the angular app build output will be used as static resource by the Spring Boot applicaiton.

Building Together (Angular + Spring Boot App)

The Gradle build script is used to build the Spring Boot app and it will take care of building Angular app as static resource to the Spring Boot App

Take a closer at the installAngular and buildAngular task in Gradle script. The installAngular task installs the required packages & dependencies defined in package.json. It uses the npm install command to install the required pacakges.

The buildAngular task uses the "ng build" angular cli command to build the angular project. The output of angular build will be placed in dist\static folder.

  1 
buildscript {
  2 
    ext {
  3 
        springBootVersion = '1.5.4.RELEASE'
  4 
    }
  5 
    repositories {
  6 
        mavenCentral()
  7 
    }
  8 
    dependencies {
  9 
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
 10 
    }
 11 
}
 12 
 
 13 
apply plugin: 'java'
 14 
apply plugin: 'eclipse'
 15 
apply plugin: 'org.springframework.boot'
 16 
 
 17 
version = '0.0.1-SNAPSHOT'
 18 
sourceCompatibility = 1.8
 19 
 
 20 
repositories {
 21 
    mavenCentral()
 22 
}
 23 
 
 24 
 
 25 
dependencies {
 26 
    compile('org.springframework.boot:spring-boot-starter-web')
 27 
    testCompile('org.springframework.boot:spring-boot-starter-test')
 28 
}
 29 
 
 30 
def webappDir = "$projectDir/src/main/webapp"
 31 
sourceSets {
 32 
    main {
 33 
        resources {
 34 
            srcDirs = ["$webappDir/dist", "$projectDir/src/main/resources"]
 35 
        }
 36 
    }
 37 
}
 38 
 
 39 
processResources {
 40 
    dependsOn "buildAngular"
 41 
}
 42 
 
 43 
task buildAngular(type:Exec) {
 44 
    // installAngular should be run prior to this task
 45 
    dependsOn "installAngular"
 46 
    workingDir "$webappDir"
 47 
    inputs.dir "$webappDir"
 48 
    // Add task to the standard build group
 49 
    group = BasePlugin.BUILD_GROUP
 50 
    // ng doesn't exist as a file in windows -> ng.cmd
 51 
    if (System.getProperty("os.name").toUpperCase().contains("WINDOWS")){
 52 
        commandLine "ng.cmd", "build", " --prod --aot"
 53 
    } else {
 54 
        commandLine "ng", "build"
 55 
    }
 56 
}
 57 
 
 58 
task installAngular(type:Exec) {
 59 
    workingDir "$webappDir"
 60 
    inputs.dir "$webappDir"
 61 
    group = BasePlugin.BUILD_GROUP
 62 
    if (System.getProperty("os.name").toUpperCase().contains("WINDOWS")){
 63 
        commandLine "npm.cmd", "install"
 64 
    } else {
 65 
        commandLine "npm", "install"
 66 
    }
 67 
}
The buildAngular task is added as processResouces in the gradle script and the installAngular task is added as dependency to buildAngular task. The gradle build script has been modified to include the angular app's dist directory in soruceSets.

Commands to build & run

Execute the following command to build the app

gradle build

Execute the following command to run the app

gradle bootRun

:) Happy Building :)


Live JSON Formatter Demo



Download Source