This guide walks you through the process of building a simple msgs.js client that exchanges messages with a Spring STOMP messaging service.

What you’ll build

You will build a msgs.js client that consumes a Spring-based messaging service that uses the STOMP protocol over WebSocket. Specifically, the client will exchange messages with the service created in Using WebSocket to build an interactive web application.

The msgs.js client will be accessed by opening the index.html file in your browser, and will exchanges messages with the Spring messaging service accepting requests at:

stomp.guides.spring.io/hello

The client will send a JSON representation of a name entered into the UI:

{"name":"Bob"}

The server will publish a greeting response, which all connected clients will render into their UI:

{"content":"Hello, Bob!"}

What you’ll need

  • About 15 minutes

  • A favorite text editor

  • A modern web browser

  • An internet connection

  • Node.js and git pre-installed

  • Bower installed as a global node.js package

Create bower configuration files

Bower is a JavaScript package manager. To install packages needed to build the msgs.js client, first, create a bower control file, .bowerrc. This file tells bower where to put the JavaScript dependencies. The .bowerrc file should be located at the root of the project ({project_id}/initial) and formatted as JSON:

.bowerrc

{
	"directory": "public/lib"
}

From a command prompt at the root of the project, run bower init. This will create a bower.json file that describes the JavaScript packages required by the project. Bower will ask for several bits of information such as a project name, license, etc. If in doubt, just press Enter to accept the defaults.

Next, use bower to install msgs.js, sockjs, which provides WebSocket fallback support in older browsers, a stomp protocol handler for WebSocket, and an AMD module loader such as curl.js. From the command prompt, type:

bower install --save msgs#dev curl#~0.8 sockjs#~0.3 stomp-websocket#~2.0

Bower will install msgs.js, curl.js, sockjs, and stomp-websocket into the directory we listed in .bowerrc. Since we specified the --save option, bower will store the package information in the bower.json file.

Bower should discover that msgs.js depends on when.js and install a compatible version.

When done, the bower.json file should have a "dependencies" object property that lists "msgs", "curl", "sockjs" and "stomp-websocket" as property names and their semantic version (semver) information as values:

bower.json

{
  "name": "draft-messaging-stomp-msgsjs",
  "version": "0.0.1",
  "authors": [
    "Brian Cavalier <[email protected]>"
  ],
  "license": "http://www.apache.org/licenses/LICENSE-2.0",
  "ignore": [
    "**/.*",
    "node_modules",
    "public/lib",
    "test",
    "tests"
  ],
  "dependencies": {
    "msgs": "dev",
    "curl": "~0.8",
    "sockjs": "~0.3",
    "stomp-websocket": "~2.0"
  }
}

Create a message bus factory module

First, create a module that creates a msgs.js message bus, and configures it to use STOMP over a SockJS socket.

public/hello/connectMessageBus.js

define(function(require) {

	var msgs = require('msgs');
	var SockJS = require('sockjs');

	require('msgs/adapters/webSocket');
	require('msgs/channels/bridges/stomp');

	function connectMessageBus(messageServiceUrl, onConnect) {
		var bus = msgs.bus();
		var socket = new SockJS(messageServiceUrl);

		socket.addEventListener('open', function () {
			var bridge = bus.stompWebSocketBridge('remote', socket, { ack: 'client' });

			bridge.controlBus.on('connected', onConnect);
			bridge.controlBus.on('error', function (error) {
				console.error('STOMP protocol error ' + error);
			});
		});

		return bus;
	}

	return connectMessageBus;
});

The connectMessageBus function uses SockJS to create a socket connection to the provided message service URL. SockJS provides the same API as WebSocket. It will use native WebSocket in environments that support it, and will fall back to other communication mechanisms in environments where native WebSocket is not available.

Once the socket has connected, a STOMP bridge is installed into the message bus. Since STOMP has it’s own connection handshake protocol on top of the socket protocol, the code arranges for the caller to be notified (via the onConnect callback) once the STOMP handshake has succeeded and thus the message bus is ready to be used.

Create a main module

Next, create a main module that uses the connectMessageBus module to create a message bus, and sets up the desired user interactions using standard DOM event handling.

public/hello/main.js

define(function(require) {

	var ready = require('curl/domReady');
	var connectMessageBus = require('./connectMessageBus');

	var bus, sendName, form, connectButton, disconnectButton, responseContainer;

	ready(function() {
		form = document.querySelector('form');
		connectButton = document.querySelector('[data-connect]');
		disconnectButton = document.querySelector('[data-disconnect]');
		responseContainer = document.querySelector('[data-response]');

		form.addEventListener('submit', parseFormAndSend);
		connectButton.addEventListener('click', connect);
		disconnectButton.addEventListener('click', disconnect);
	});

	function connect() {
		bus = connectMessageBus('//stomp.guides.spring.io/hello', function() {
			setConnected(true);

			sendName = bus.inboundAdapter('remote!/app/hello', JSON.stringify);

			bus.on('remote!/queue/greetings', function(greeting) {
				addGreeting(JSON.parse(greeting));
			});
		});
	}

	function disconnect() {
		bus.destroy();
		setConnected(false);
	}

	function parseFormAndSend(e) {
		e.preventDefault();

		var name = e.target.elements.name.value;
		sendName({ name: name });
	}

	function setConnected(connected) {
		connectButton.disabled = !!connected;
		disconnectButton.disabled = form.elements.send.disabled = !connected;
		responseContainer.innerHTML = '';
	}

	function addGreeting(greeting) {
		form.reset();
		responseContainer.innerHTML += '<p>' + greeting.content + '</p>';
	}
});

Once the DOM is ready, event handlers for the connect and disconnect buttons, and the name entry form are added. The connect button handler calls createMessageBus to create the msgs.js bus and connect it to the Spring messaging service. Once connected, the handler updates the UI state. It then creates a msgs.js inbound adapter, sendName: a function that can be used to send data into the message bus.

It then registers a callback to receive all messages from the Spring message service’s queue/greetings queue. When a message is received, the callback parses it as JSON, and calls the addGreeting function to update the UI.

In a real application, you’ll want to use data binding or templating, rather than DOM manipulation as shown here.

When the user types his/her name and clicks the send button (or presses return/enter) to submit the form, the form submit event handler simply extracts the name from the name input, and uses the sendName function (the inbound adapter created above) to place it onto the message bus.

When the server receives the name, it will format a greeting, place it onto the greeting queue, and all connected clients will be able to receive it.

Create an AMD boot script

Next, create the AMD boot script, run.js:

public/run.js

var curl;
(function () {

    curl.config({
        main: 'hello',
		paths: {
			sockjs: 'lib/sockjs/sockjs'
		},
        packages: {
            // Your application's packages
            hello: { location: 'hello' },
            // Third-party packages
            curl: { location: 'lib/curl/src/curl' },
            msgs: { location: 'lib/msgs', main: 'msgs' },
            stomp: { location: 'lib/stomp-websocket', main: 'dist/stomp' },
            when: { location: 'lib/when', main: 'when' }
        }
    });

}());

This script configures the AMD loader: curl.config(). The main configuration property tells curl.js where to find the application’s main module, which will be fetched and evaluated automatically. The packages config object tells curl.js where to find modules in our application’s packages or in third-party packages.

Create the application page

Finally, create an index.html file and add the following HTML:

public/index.html

<!doctype html>
<html>
<head>
    <title>Hello msgs.js</title>
    <script data-curl-run="run.js" src="lib/curl/src/curl.js"></script>
</head>
<body>
    <div>
        <button data-connect>Connect</button>
        <button data-disconnect disabled>Disconnect</button>
    </div>
    <form>
        <label>What is your name?<input type="text" name="name" /></label>
        <input type="submit" value="Send" name="send" disabled/>
    </form>
    <div data-response></div>
</body>
</html>

The script element loads curl.js and then loads an application boot script named "run.js". The boot script initializes and configures an AMD module environment and then starts the client-side application code.

Run the client

To run the client, you’ll need to serve it from a web server to your browser. The Spring Boot CLI (Command Line Interface) includes an embedded Tomcat server, which offers a simple approach to serving web content. See Building an Application with Spring Boot for more information about installing and using the CLI.

In order to serve static content from Spring Boot’s embedded Tomcat server, you’ll also need to create a minimal amount of web application code so that Spring Boot knows to start Tomcat. The following app.groovy script is sufficient for letting Spring Boot know that you want to run Tomcat:

app.groovy

@Controller class JsApp { }

You can now run the app using the Spring Boot CLI:

spring run app.groovy

Once the app starts, open http://localhost:8080 and click the "Connect" button.

Upon opening a connection, you are asked for your name. Enter your name and click "Send". Your name is sent to the server as a JSON message over STOMP. After a 3-second simulated delay, the server sends a message back with a "Hello" greeting that is displayed on the page. At this point, you can send another name, or you can click the "Disconnect" button to close the connection.

Greeting message receive from Spring messaging service is rendered into the DOM

Summary

Congratulations! You’ve just developed a msgs.js client that exchanges messages with a Spring STOMP-based messaging service.