-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathChapter030-Web-Messaging-STOMP.html
588 lines (448 loc) · 34.5 KB
/
Chapter030-Web-Messaging-STOMP.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
<section data-type="chapter" id="ch_web_stomp" xmlns="http://www.w3.org/1999/xhtml">
<h1>Web Messaging with STOMP</h1>
<p class="lead">In this chapter, we will write a web application that sends and receives messages using the STOMP protocol <a contenteditable="false" data-primary="web messaging with STOMP" data-type="indexterm" id="webmessSTOMP"> </a>over HTML5 Web Sockets.<a contenteditable="false" data-primary="STOMP" data-secondary="web messaging with" data-type="indexterm" id="STOMPwebmess"> </a></p>
<p>In <a data-type="xref" href="#ch_introduction_stomp_example">#ch_introduction_stomp_example</a>, we described the Locations application. In this chapter, we will write the web application (shown in <a data-type="xref" href="img_web_stomp_1">img_web_stomp_1</a>) that consumes the device’s GPS position to display it on a map and send text messages to the device.</p>
<figure id="img_web_stomp_1"><img alt="Diagram of the +Locations+ Web application" src="images/mawm_0301.png" />
<figcaption>Diagram of the Locations web application</figcaption>
</figure>
<div data-type="note">
<p>Throughout the chapter, we will show all the code required to run the application.</p>
<p>The whole application code can be retrieved from the <a href="http://bit.ly/mwm-code">GitHub repository</a> in the <em>stomp/web/</em> directory.</p>
</div>
<section data-type="sect1" id="_html5_web_sockets">
<h1>HTML5 Web Sockets</h1>
<p>Web Sockets is a recent protocol that provides a bidirectional and full-duplex communication channel over a single TCP <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="HTML 5 Web Sockets" data-type="indexterm"> </a>connection between the web browser and the web<a contenteditable="false" data-primary="HTML 5 Web Sockets" data-see="Web Sockets" data-type="indexterm"> </a> server.<a contenteditable="false" data-primary="Web Sockets" data-type="indexterm"> </a> Before Web Sockets, communication was only one way: the browser initiated communication by sending an HTTP request to the web server, which replied with an HTTP response. The server could not send data to the browser without the browser having first sent a request. Some techniques exist to provide a bidirectional HTTP (such as HTTP long polling and HTTP streaming), but they have significant issues, as described in <a href="http://bit.ly/rfc-6202">RFC-6202</a>.</p>
<p>Thanks to HTML5 Web Sockets, it is now possible to have two-way communication between the browser and the server. The server can send data to the browser without waiting for a request. This enables us to deliver messages from the web server to the browser.</p>
<p>We are no longer limited to pull-based messages where the browser needs to regularly contact the server to know whether there are any available messages to consume. This preserves bandwidth (the browser no longer needs to send periodic query requests) and battery on mobile devices.</p>
<div data-type="note">
<p>Web Sockets have many use cases in addition to messaging. In some simple cases, sending data on Web Sockets without using a messaging protocol could be enough.</p>
<p>For further information, the book <a class="orm:hideurl " href="http://bit.ly/hpbn-book"><em>High Performance Browser Networking</em></a> contains a thorough introduction to Web Sockets.</p>
</div>
</section>
<section data-type="sect1" id="_stomp_js_stomp_over_web_sockets">
<h1>stomp.js, STOMP over Web Sockets</h1>
<p>If your web browser supports Web Sockets (and all modern browsers do), we can then use a STOMP <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="STOMP over Web Sockets, using stomp.js" data-type="indexterm"> </a>client that leverages them to send and receive STOMP messages directly from it.<a contenteditable="false" data-primary="Web Sockets" data-secondary="STOMP over" data-type="indexterm"> </a></p>
<p>stomp.js is a small JavaScript library (8 kibibytes for its minified version) that provides a full-featured STOMP 1.2 client.<a contenteditable="false" data-primary="stomp.js" data-type="indexterm"> </a></p>
<p>It can run in multiple JavaScript platforms. Its main target is the web browser (using Web Sockets as its transport protocol), but it can also be used on other JavaScript platforms such as <a href="http://nodejs.org">node.js</a> (using TCP sockets).</p>
<div data-type="warning">
<p>A STOMP client running in a web browser is similar to another STOMP client running on another platform with one exception: STOMP messages are transported by Web Sockets and not regular TCP sockets. The significant difference between them is that a Web Socket is created by sending an HTTP request with an <code>Upgrade: websocket</code> header. The STOMP broker must be able to handle such an HTTP request and upgrade the connection from HTTP to the STOMP protocol.</p>
<p>Web Sockets support is not part of the STOMP protocol. The most commonly used STOMP brokers—such as ActiveMQ (which is used in this book), HornetQ, and RabbitMQ—support it, but you will have to consult your broker documentation to check if it does.</p>
</div>
</section>
<section data-type="sect1" id="_bootstrap_the_code_locations_html_code_web_application">
<h1>Bootstrap the Locations Web Application</h1>
<p>As we explained in the first chapter, this web application will be used to display the locations of the devices and send them text messages.<a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="bootstrapping Locations web application (example)" data-type="indexterm"> </a></p>
<p>It will be a very simple one-page web application that can be run from a web server serving static pages. It does not require any server-side runtime, as all the code will be executed inside the web browser using JavaScript.</p>
<p>As shown in <a data-type="xref" href="ex_web_stomp_1">ex_web_stomp_1</a>, let’s bootstrap the web application by creating a <em>locations.html</em> page.</p>
<div data-type="example" id="ex_web_stomp_1">
<h5>Template for the locations.html web application</h5>
<pre data-code-language="html" data-type="programlisting">
<!DOCTYPE html>
<html lang="en">
<head>
<meta content="width=device-width" name="viewport">
<meta charset="utf-8">
<title>Locations - STOMP Example</title>
</head>
<body>
<h1>Locations - STOMP Example</h1>
<form id='connect_form'>
<fieldset>
<label>WebSocket URL</label>
<input name=url id='connect_url'
value='ws://localhost:61614' type="text">
<button id='connect_button' type="submit">Connect</button>
<button type="button"
id='disconnect_button' disabled>Disconnect</button>
</fieldset>
</form>
<form id="text_form" style="display: none;">
<fieldset>
<legend>Send Text</legend>
Device: <select id="deviceID"></select>
<br>
Text: <input id='text' size=64
placeholder="type the text to sent to the device" type="text">
<br>
<button id='text_submit' type="submit">Send</button>
</fieldset>
</form>
<hr>
<div id="map-canvas" style="height:512px; width:100%; padding:0; margin:0">
</div>
<footer>&copy; 2014 <a href="http://mobile-web-messaging.net">Mobile &amp;
Web Messaging</a></footer>
<!-- Scripts placed at the end of the document so the pages load faster -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.1/stomp.js"></script>
<script>
$(document).ready(function() {
// We will put all the JavaScript code in this is called
// when the document is ready
};
</script>
</body>
</html>
</pre>
</div>
</section>
<section data-type="sect1" id="_create_a_stomp_client_with_code_stomp_js_code">
<h1>Create a Stomp Client with stomp.js</h1>
<p>The STOMP client will be used both for consuming messages (from the device’s location destination) <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="creating a STOMP client with stomp.js" data-type="indexterm"> </a>and<a contenteditable="false" data-primary="stomp.js" data-secondary="creating a STOMP client with" data-type="indexterm"> </a> producing them (to the device’s text destination).</p>
<p>We will declare the <code>client</code> variable to use it throughout our script:</p>
<pre data-code-language="js" data-type="programlisting">
$(document).ready(function() {
var client; // keep a reference on the Stomp client that we create
}</pre>
<p>When the user clicks on the <code>Connect</code> button, the URL is retrieved from the <code>connect_url</code> input element and passed to the <code>connect</code> method. In this method, we will create the <code>client</code> and connect to the STOMP broker:</p>
<pre data-code-language="js" data-type="programlisting">
// handles the connect_form
$('#connect_form').submit(function() {
var url = $("#connect_url").val();
connect(url);
return false;
});</pre>
</section>
<section data-type="sect1" id="_connect_to_a_stomp_broker_2">
<h1>Connect to a STOMP Broker</h1>
<p>In the <code>connect</code> method, we create the STOMP client <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="connecting to a STOMP broker" data-type="indexterm"> </a>by calling <code>Stomp.client(url)</code>. <a contenteditable="false" data-primary="brokers" data-secondary="connecting to a STOMP broker" data-type="indexterm"> </a>When we have the <code>client</code> object, we call its <code>connect</code> method to connect to the STOMP broker. This method takes at least two arguments:</p>
<ul>
<li>
<p>A set of headers containing additional metadata that the STOMP broker can use during the connection process (in our case, we have no such header, so we will pass an empty JavaScript literal object <code>{}</code>)</p>
</li>
<li>
<p>A <code>connectedCallback</code> function that is called back when the client is successfully connected to the STOMP broker)</p>
</li>
</ul>
<pre data-code-language="js" data-type="programlisting">
// connection to the STOMP broker
// and subscription to the device's position destinations
//
// the url paramater is the Web Socket URL of the STOMP broker
function connect(url) {
// creates the callback that is called when the client
// is successfully connected to the STOMP broker
var connectedCallback = function(frame) {
...
};
// create the STOMP client
client = Stomp.client(url);
// and connect to the STOMP broker
client.connect({}, connectedCallback);
}</pre>
<p>The <code>connectedCallback</code> is called when the client is successfully connected to the STOMP broker. At this stage, we can clean up the user interface by hiding the <code>Connect</code> button, and showing the <code>Disconnect</code> button and the form to send text messages to the devices:</p>
<pre data-code-language="js" data-type="programlisting">
var connectedCallback = function(frame) {
client.debug("connected to Stomp");
// disable the connect button
$("#connect_button").prop("disabled",true);
// enable the disconnect button
$("#disconnect_button").prop("disabled",false);
// show the form to send text to the devices
$("#text_form").show();
// ...
};</pre>
<p>If we want to be notified when the connection is <em>unsuccesful</em>, we can pass an additional argument to the <code>connect</code> method: a function that is called back if the connection is <em>not</em> successful.</p>
<pre data-code-language="js" data-type="programlisting">
client.connect({}, connectedCallback,
function(frame) {
// this function is executed if the connection to the STOMP broker fails
});</pre>
</section>
<section data-type="sect1" id="_receive_stomp_messages_2">
<h1>Receive STOMP Messages</h1>
<p>When the client is connected successfully to the STOMP broker, it can subscribe to a destination <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="receiving STOMP messages" data-type="indexterm"> </a>using the <code>subscribe</code> method,<a contenteditable="false" data-primary="messages" data-secondary="receiving STOMP messages" data-tertiary="Locations web application (example)" data-type="indexterm"> </a> which takes two parameters:</p>
<ul>
<li>
<p>The name of the destination</p>
</li>
<li>
<p>A function that is called back every time the broker delivers a message to the <span class="keep-together">client:</span></p>
</li>
</ul>
<pre data-code-language="js" data-type="programlisting">
client.subscribe(destination, function(message) {
// this function is executed every time a message is consumed
});</pre>
<p>The <code>message</code> parameter that is passed to the subscription callback corresponds to a STOMP message and has three properties:</p>
<dl>
<dt><code>command</code></dt>
<dd>The command of the STOMP frame (when a message is received, it will always be <code>MESSAGE</code>)</dd>
<dt><code>headers</code></dt>
<dd>A JavaScript object containing all the frame headers; it can be empty if the message has no headers</dd>
<dt><code>body</code></dt>
<dd>A string representing the message’s payload; it can be <code>null</code> if the message has no payload</dd>
</dl>
<section data-type="sect2" id="_subscribe_to_a_wildcard_destination">
<h2>Subscribe to a Wildcard Destination</h2>
<p>This web application wants to receive the location of <em>any</em> devices that broadcast it. This means <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="receiving STOMP messages" data-tertiary="subscribing to wildcard destination" data-type="indexterm"> </a>that we must <a contenteditable="false" data-primary="destinations" data-secondary="subscribing to a wildcard STOMP destination" data-type="indexterm"> </a>subscribe to the <code>/topic/device.XXX.location</code> for every device where <code>XXX</code> is the device identifier.</p>
<p>There are two different ways to achieve this. The first way is to know beforehand all the device IDs and subscribe to their topics one after the other. We can use the same subscription callback for all of them. However, that implies that the web application must now have a way to know this list. For example, it could be a web service that returns such a list.</p>
<p>The pseudocode for it would look like:</p>
<pre data-code-language="js" data-type="programlisting">
var listURL = "...";
var deviceIDs = fetch(listURL);
var callback = function(message) {
// we use the same callback for every subscription
}
for (deviceID in deviceIDs) {
var destination = "/topic/truck." + deviceID + ".location";
client.subscribe(destination, callback);
}</pre>
<p>But what happens if another device starts broadcasting its location <em>after</em> the web application fetches the list of device IDs? The web application will not subscribe to its topic and will never display it on the map. We would have to periodically fetch the list of device IDs and check whether there are new ones or if some devices have been removed.</p>
<p>Fortunately, the flexibility of the STOMP protocol comes in handy to manage this in a simpler fashion. The STOMP 1.2 protocol defines very loosely the notion of <span class="keep-together">destination:</span></p>
<blockquote data-type="epigraph">
<p>A STOMP server is modelled as a set of destinations to which messages can be sent. The STOMP protocol treats destinations as opaque string and their syntax is server implementation specific. Additionally STOMP does not define what the delivery semantics of destinations should be. The delivery, or “message exchange”, semantics of destinations can vary from server to server and even from destination to destination. This allows servers to be creative with the semantics that they can support with STOMP.</p>
</blockquote>
<p>Until now, we have used <em>simple</em> destinations (e.g., <code>/topic/device.2262EC25-E9FD-4578-BADE-4E113DE45934.location</code> or <code>/queue/device.2262EC25-E9FD-4578-BADE-4E113DE45934.text</code>) that are straightforward to understand.</p>
<p>We will now use a feature<a contenteditable="false" data-primary="wildcard destinations" data-type="indexterm"> </a> from our STOMP broker, <a contenteditable="false" data-primary="ActiveMQ" data-secondary="wildcard destinations" data-type="indexterm"> </a>ActiveMQ, that allows us to use <a href="http://bit.ly/activemq"><em>wildcard</em> destinations</a>.</p>
<ul>
<li>
<p><code>.</code> is used to separate names in a path</p>
</li>
<li>
<p><code>*</code> is used to match any name in a path</p>
</li>
<li>
<p><code>></code> is used to recursively match any destination starting from this name</p>
</li>
</ul>
<p>With our example using ActiveMQ, we can use this notation to subscribe to any device location topic by using the <code>/topic/device.*.location</code> wildcard destination (where <code>*</code> stands for <em>any device identifier</em>).</p>
<p>The subscription code becomes simpler:</p>
<pre data-code-language="js" data-type="programlisting">
// we use a wildcard destination to register to any
// destination that matches this pattern.
var destination = "/topic/device.*.location";
client.subscribe(destination, function(message) {
// this function is called every time a message is received
});</pre>
<div data-type="note">
<p>The semantics of STOMP destinations are specific to the STOMP broker so you have to check its documentation to determine if wildcard destinations or similar concepts are supported. If it does not, you have to revert to the first idea to fetch the list of devices and subscribe to each of the destinations, or use another STOMP broker that supports this feature.</p>
</div>
<p>Because we no longer know <em>a priori</em> which device location we are receiving, we need a new way to determine which device broadcast it. There are two pieces of information we can use. When a consumer receives a STOMP message, the message always has a <code>destination</code> header that corresponds to the <em>actual</em> destination from which the client consumed.</p>
<p>We are subscribing to the wildcard address <code>/topic/device.*.location</code>, so the actual destination from which we consume will be stored in the received message’s headers in <code>message.headers["destination"]</code>. In my case, the value of this header would be <code>/topic/device.2262EC25-E9FD-4578-BADE-4E113DE45934.location</code>. However, we would then have to parse this <code>destination</code> to extract the device ID from it and write brittle code for that.</p>
<p>If we look back at <a data-type="xref" href="#ch_introduction_stomp_example_message">#ch_introduction_stomp_example_message</a>, the message representation for the location also contains the device ID in the <code>deviceID</code> property:</p>
<pre data-code-language="js" data-type="programlisting">
{
"deviceID": "BBB",
"lat": 48.8581,
"lng": 2.2946,
"ts": "2013-09-23T08:43Z"
}</pre>
<p>The message is <em>self-contained</em> and defines all the interesting information that a consumer might need. When we receive such a location message, we know which device is sending it by simply looking at the <code>deviceID</code> property from the JSON object created from the message body:</p>
<pre data-code-language="js" data-type="programlisting">
var destination = "/topic/device.*.location";
client.subscribe(destination, function(message) {
// this function is called every time a message is received
// create an object from the JSON string contained in the message body
var payload = JSON.parse(message.body);
var deviceID = payload.deviceID;
//...
});</pre>
<p>When we receive the position of a device, the last step we need to make is to display its position on a map. We will wrap this code in a <code>show</code> method that is called from the subscription callback. The callback will pass three parameters to this method: the device identifier, its latitude, and its longitude.</p>
<p>The code to connect to the STOMP broker and subscribe to the wildcard destination is shown here:</p>
<pre data-code-language="js" data-type="programlisting">
// connection to the STOMP broker
// and subscription to the device's position destinations
//
// the url paramater is the Web Socket URL of the STOMP broker
function connect(url) {
// creates the callback that is called when the client
// is successfully connected to the STOMP broker
var connectedCallback = function(frame) {
client.debug("connected to Stomp");
// disable the connect button
$("#connect_button").prop("disabled",true);
// enable the disconnect button
$("#disconnect_button").prop("disabled",false);
// show the form to send text to the devices
$("#text_form").show();
// after the client is connected, subscribe to
// the device's location destinations
// we use a wildcard destination to register to any
// destination that matches this pattern
var destination = "/topic/device.*.location";
client.subscribe(destination, function(message) {
// this function is called every time a message is received
// create an object from the JSON string contained in the message body
var payload = JSON.parse(message.body);
var deviceID = payload.deviceID;
if (!$("#deviceID option[value='" + deviceID + "']").length) {
// if the device ID is not already in the list of devices we can send
// orders to, we add it.
$('#deviceID').append($('<option>', {value:deviceID}).text(deviceID));
}
// show the device location on the map
show(deviceID, payload.lat, payload.lng);
});
};
// create the STOMP client
client = Stomp.client(url);
// and connect to the STOMP broker
client.connect({}, connectedCallback);
}</pre>
</section>
</section>
<section data-type="sect1" id="_draw_the_device_locations_on_a_map">
<h1>Draw the Device Locations on a Map</h1>
<p>The web application is now receiving the GPS coordinates of any devices that send them.<a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="drawing device locations on a map" data-type="indexterm"> </a> We could just display them as text like we did for the iOS application in <a data-type="xref" href="#ch_mobile_stomp_display_position">#ch_mobile_stomp_display_position</a>, but we can make it prettier by drawing them on a map using the Google Maps API.<a contenteditable="false" data-primary="Google Maps API" data-type="indexterm"> </a></p>
<p>In the template in <a data-type="xref" href="#ex_web_stomp_1">#ex_web_stomp_1</a>, we already added the scripts to use Google Maps API. We now need to create the map and initialize it:</p>
<pre data-code-language="js" data-type="programlisting">
$(document).ready(function() {
// Google map and the trackers to follow the trucks
var map, trackers = {};
function initialize() {
var mapOptions = {
zoom: 2,
center: new google.maps.LatLng(30,0),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map($("#map-canvas").get(0), mapOptions);
}
// initialize the Google map
google.maps.event.addDomListener(window, 'load', initialize);</pre>
<p>With this initialization code, the map will be drawn in the <code>map_canvas</code> <code>div</code> element and we can reference it using the <code>map</code> variable.</p>
<p>The <code>trackers</code> variable is a map whose keys are the device identifiers and the values are a tracker with the latest location of the device on the map.</p>
<p>We called a <code>show()</code> method in the subscription handler. We can now implement it to display the device on the map using its coordinates:</p>
<pre data-code-language="js" data-type="programlisting">
// show the device at the given latitude and longitude
function show(deviceID, lat, lng) {
var position = new google.maps.LatLng(lat, lng);
// lazy instantiation of the map
if (!map) {
create_map(position);
}
if (trackers[deviceID]) {
// the tracker is known; we just need to update its position
trackers[deviceID].marker.setPosition(position);
} else {
// there is no tracker for this device yet; let's create it
var marker = new google.maps.Marker({
position: position,
map: map,
title: deviceID + " is here"});
var infowindow = new google.maps.InfoWindow({
content: "Device " + deviceID
});
var tracker = {
marker: marker
};
// add it to the trackers
trackers[deviceID] = tracker;
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(map, marker);
});
}
}</pre>
<p>If we now open this <em>locations.html</em> file in a web browser, we will see a map of the whole world<a contenteditable="false" data-primary="Google Maps API" data-secondary="map for Locations STOMP application (example)" data-type="indexterm"> </a> displayed (<a data-type="xref" href="img_web_stomp_125">img_web_stomp_125</a>).</p>
<p>If we click on the Connect button, markers will appear on the map for each device that broadcasts its coordinates.</p>
<p>In my case, I am using the iOS simulator to run the mobile application developed in . I use the Location tool to simulate a freeway drive (as explained in <a data-type="xref" href="#ch_mobile_stomp_location_simulator">#ch_mobile_stomp_location_simulator</a>). See <a data-type="xref" href="img_web_stomp_2">img_web_stomp_2</a>.</p>
<p>The position of the device is updated every time the web application receives a STOMP message from the device’s position destination and we see it move on the map.</p>
<p>At this stage, the web application receives STOMP messages to display the position of the devices. We now need to write the code to send texts to the devices.</p>
<figure id="img_web_stomp_125"><img alt="The Locations Web application" src="images/mawm_0302.png" />
<figcaption>The Locations web application</figcaption>
</figure>
<figure id="img_web_stomp_2"><img alt="Simulate a freeway drive" src="images/mawm_0303.png" />
<figcaption>Simulating a freeway drive</figcaption>
</figure>
</section>
<section data-type="sect1" id="_send_stomp_messages_2">
<h1>Send STOMP Messages</h1>
<p>The STOMP client can send messages to the broker using its <code>send</code> method, <a contenteditable="false" data-primary="messages" data-secondary="sending STOMP messages" data-tertiary="Locations web application (example)" data-type="indexterm"> </a>which takes <a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="sending STOMP messages" data-type="indexterm"> </a>three parameters:</p>
<dl>
<dt><code>destination</code></dt>
<dd>The name of the destination</dd>
<dt><code>headers</code></dt>
<dd>A JavaScript object containing any additional headers</dd>
<dt><code>body</code></dt>
<dd>A string corresponding to the message payload</dd>
</dl>
<p>Both <code>headers</code> and <code>body</code> are optional and can be omitted. However, if you want to set the message body, you must also specify the headers (using an empty JavaScript literal if you have no header to set):</p>
<pre data-code-language="js" data-type="programlisting">
client.send(destination, {}, body);</pre>
<p>As we described in <a data-type="xref" href="#ch_introduction_stomp_example_topology">#ch_introduction_stomp_example_topology</a>, we use a queue to send orders to a given device, and the destination for this is named <span class="keep-together"><code>/queue/device.XXX.text</code></span>.</p>
<p>The text is sent in the STOMP message body as a plain-text string:</p>
<pre data-type="programlisting">
Hello, where are you?</pre>
<p>We must respect this message format, as it is the format expected by the iOS application to handle the texts and display them (we wrote this code in <a data-type="xref" href="#ch_mobile_stomp_subscribe">#ch_mobile_stomp_subscribe</a>).</p>
<p>We added an HTML <code><form></code> element with the id <code>text_form</code> to send a text message. The device identifier is taken from the selected option in the <code><select></code> element identified by <code>deviceID</code>. The text itself is taken from the <code><input></code> element identified by <code>text</code>.</p>
<p>When we know the <code>deviceID</code> and the <code>order</code>, we have all we need to send an order to this device. The destination for the order will be built using the <code>device</code>.</p>
<p>Piecing everything together, the code to send a STOMP message looks like:</p>
<pre data-code-language="js" data-type="programlisting">
// send a text to a device
$('#text_form').submit(function() {
var deviceID = $("#deviceID").val();
var text = $("#text").val();
// use the device's queue orders as the destination
var destination = "/queue/device." + deviceID + ".text";
// text is sent as a plain-text string
client.send(destination, {}, text);
// reset the text input field
$("#text").val("");
return false;
});</pre>
<p>If we reload the <em>locations.html</em> file after adding this code, we can now send any text message to a device by selecting it in the list in the Send Text form.</p>
<p>As illustrated in , let's type "Hello, where are you?" and click on the Send button.</p>
<figure id="img_web_stomp_3"><img alt="Send a text message to a device" src="images/mawm_0304.png" />
<figcaption>Send a text message to a device</figcaption>
</figure>
<p>The message is sent when you click on the Send button. The Locations iOS application is subscribed to this destination, so it will receive the message and display it in its table (<a data-type="xref" href="img_web_stomp_4">img_web_stomp_4</a>).</p>
<figure id="img_web_stomp_4"><img alt="The Locations iOS application received the text" src="images/mawm_0305.png" />
<figcaption>The Locations iOS application received the text</figcaption>
</figure>
</section>
<section data-type="sect1" id="_disconnect_from_the_stomp_broker">
<h1>Disconnect from the STOMP Broker</h1>
<p><em>locations.html</em> connects to the STOMP broker<a contenteditable="false" data-primary="web messaging with STOMP" data-secondary="disconnecting from a STOMP broker" data-type="indexterm"> </a> when the user clicks on the Connect button.<a contenteditable="false" data-primary="brokers" data-secondary="disconnecting from a STOMP broker" data-type="indexterm"> </a> The user can then disconnect from the broker by clicking on the Disconnect button.</p>
<p>To disconnect from the STOMP broker, we call the <code>disconnect</code> method on the <code>client</code> and pass a callback that is called after the disconnection succeeds.</p>
<p>In this callback, we clean up the trackers that are displayed on the map so that nothing is shown after the user is disconnected. We also revert the user interface by hiding the Disconnect button and the text form and showing the Connect button:</p>
<pre data-code-language="js" data-type="programlisting">
function disconnect() {
// disconnect from the broker
client.disconnect(function() {
// when we are successfully disconnected, clear out all the trackers from
// the map
for (var tracker in trackers) {
trackers[tracker].marker.setMap(null);
}
trackers = {};
});
$("#deviceID").empty();
$("#connect_button").prop("disabled",false);
$("#disconnect_button").prop("disabled",true);
$("#text_form").hide();
}</pre>
<p>Finally, we need to call this disconnect method when the <code>Disconnect</code> button is clicked:</p>
<pre data-code-language="js" data-type="programlisting">
$('#disconnect_button').click(function() {
disconnect();
return false;
});</pre>
</section>
<section data-type="sect1" id="_summary_3">
<h1>Summary</h1>
<p>In this chapter, we learned to use stomp.js to send and receive STOMP messages from a web application.</p>
<p>Whether you are using StompKit in an iOS application or stomp.js in a web application, the steps are always the same.</p>
<p>To send a message, the application must do the following:</p>
<ol>
<li>
<p>Connect to the STOMP broker.</p>
</li>
<li>
<p>Send the message to the destination.</p>
</li>
</ol>
<p>To consume a message, the application must do the following:</p>
<ol>
<li>
<p>Connect to the STOMP broker.</p>
</li>
<li>
<p>Subscribe to the destination and pass a callback that is called every time a message is received.</p>
</li>
</ol>
<p>At the end of this chapter, we have a very simple application that works. If you have access to several iPhone devices, you can see that the web application will display the location of all the devices running the iOS application.</p>
<p>In the next chapter, we will learn about more advanced features of STOMP. We did not present them earlier because they were not required to write this simple application. However, it is likely that you may need some of these features if your applications are more complex than this simple example.<br />
<a contenteditable="false" data-primary="web messaging with STOMP" data-startref="webmessSTOMP" data-type="indexterm"> </a><a contenteditable="false" data-primary="STOMP" data-secondary="web messaging with" data-startref="STOMPwebmess" data-type="indexterm"> </a></p>
</section>
</section>