-
Notifications
You must be signed in to change notification settings - Fork 3
/
Chapter040-Avdanced-STOMP.html
701 lines (525 loc) · 55.5 KB
/
Chapter040-Avdanced-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
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
<section data-type="chapter" id="ch_advanced_stomp" xmlns="http://www.w3.org/1999/xhtml">
<h1>Advanced STOMP</h1>
<p class="lead">In Chapters 2 and 3, we used STOMP to send and receive messages from a native iOS application and a web application.<a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-type="indexterm" id="STOMPadv"> </a> STOMP provides additional features that we did not use to write these applications. In this chapter, we will take a tour of all the features provided by STOMP.</p>
<p>This chapter covers the latest version of the protocol at the time of this writing (i.e., <a href="http://bit.ly/STOMPspec">STOMP 1.2</a>, which was released on October 22, 2012).</p>
<p>As we present these features, we will show how to use them from either StompKit or stomp.js. The API may vary depending on the platform and language, but the concept remains the same. If you ever need to use STOMP from another language or platform, you will have to adapt to the library API—but the underlying concept will still apply.</p>
<p>Clients and brokers can use these features by sending additional frames (defined in the protocol) or by using message headers. The STOMP protocol defines frames to provide transactions, a combination of frames and headers for message acknowledgment, and headers for heartbeat negotiation.</p>
<p>In this chapter, we will describe the features provided by the STOMP protocol that the client and broker must support in order to be compliant.</p>
<p>STOMP is also extensible, and clients and brokers can support additional features by using headers not defined in the protocol. We will describe these additional features in .</p>
<p>In any case, the features (defined in the protocol or supported only by some brokers) rely on the structure of STOMP frames.</p>
<section data-type="sect1" id="ch_adv_stomp_frame">
<h1>Frame Representation</h1>
<p>All data exchanged between a client and a broker is done using STOMP<a contenteditable="false" data-primary="frames (STOMP)" data-type="indexterm"> </a> frames. <a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="frame representation" data-type="indexterm"> </a>STOMP is modeled on HTTP and its frames follow a similar structure. Each frame is composed of three <a contenteditable="false" data-primary="frames (STOMP)" data-secondary="elements of" data-type="indexterm"> </a>elements (<a data-type="xref" href="ex_stomp_frame">ex_stomp_frame</a>):</p>
<ul>
<li>
<p>A <em>command</em></p>
</li>
<li>
<p>A set of <em>headers</em></p>
</li>
<li>
<p>A <em>payload</em> (optional)</p>
</li>
</ul>
<div data-type="example" id="ex_stomp_frame">
<h5>STOMP Frame Structure</h5>
<pre data-type="programlisting">
COMMAND <a class="co" href="#callout_ch4-co-1" id="co_ch4-co-1"><img alt="1" src="images/callouts/1.png" /></a>
header1:value1 <a class="co" href="#callout_ch4-co-2" id="co_ch4-co-2"><img alt="2" src="images/callouts/2.png" /></a>
header2:value2
<a class="co" href="#callout_ch4-co-3" id="co_ch4-co-3"><img alt="3" src="images/callouts/3.png" /></a>
payload^@ <a class="co" href="#callout_ch4-co-4" id="co_ch4-co-4"><img alt="4" src="images/callouts/4.png" /></a></pre>
<dl class="calloutlist">
<dt><a class="co" href="#co_ch4-co-1" id="callout_ch4-co-1"><img alt="1" src="images/callouts/1.png" /></a></dt>
<dd>
<p>A frame starts with a command string<a contenteditable="false" data-primary="commands in STOMP frames" data-type="indexterm"> </a> followed by an end-of-line (EOL)</p>
</dd>
<dt><a class="co" href="#co_ch4-co-2" id="callout_ch4-co-2"><img alt="2" src="images/callouts/2.png" /></a></dt>
<dd>
<p>Header entries <a contenteditable="false" data-primary="headers" data-secondary="in STOMP frames" data-type="indexterm"> </a>follow the format <code><key>:<value></code> and are ended by EOL</p>
</dd>
<dt><a class="co" href="#co_ch4-co-3" id="callout_ch4-co-3"><img alt="3" src="images/callouts/3.png" /></a></dt>
<dd>
<p>A blank line separates the set of headers from <a contenteditable="false" data-primary="payload" data-secondary="in STOMP frames" data-type="indexterm"> </a>the payload</p>
</dd>
<dt><a class="co" href="#co_ch4-co-4" id="callout_ch4-co-4"><img alt="4" src="images/callouts/4.png" /></a></dt>
<dd>
<p>A frame is <a contenteditable="false" data-primary="NULL octet (^@)" data-type="indexterm"> </a>ended by a <a contenteditable="false" data-primary="^@ (NULL octet)" data-type="indexterm"> </a>NULL octet (represented by <code>^@</code> in ASCII)</p>
</dd>
</dl>
</div>
<p>STOMP is based on<a contenteditable="false" data-primary="text messages in STOMP" data-type="indexterm"> </a> text (using UTF-8 for its default encoding), but it can also transmit binary data in its payload by specifying an alternative encoding.</p>
<p>In Chapters 2 and 3, we were sending JSON data in the messages. We were setting the string representation of the JSON structure in the message body and setting the <code>content-type</code> header to <code>application/json; charset=utf-8</code>.</p>
<p>STOMP uses frames not only to send messages (with the <code>SEND</code> command) and deliver them (with the <code>MESSAGE</code> command) but<a contenteditable="false" data-primary="frames (STOMP)" data-secondary="commands in" data-type="indexterm"> </a> also for all its operation commands (such as <code>CONNECT</code>, <code>DISCONNECT</code>, <code>SUBSCRIBE</code>, etc.).</p>
<section data-type="sect2" id="_headers">
<h2>Headers</h2>
<p>STOMP defines only a handful of headers<a contenteditable="false" data-primary="frames (STOMP)" data-secondary="headers" data-type="indexterm"> </a> for its different frames. </p>
<p>It is also possible to add other headers when sending a frame, as long as it does not collide with the headers already specified in the STOMP protocol.</p>
<p>An application can use headers instead of the message’s payload to transmit data. The fundamental difference between headers and payload is that headers can be read (and modified) by the STOMP brokers.<a contenteditable="false" data-primary="payload" data-secondary="in STOMP frames" data-tertiary="headers versus" data-type="indexterm"> </a> Payload is treated as a black box and the broker never reads or modifies it.</p>
<p>In practice, headers are most often used to specify additional features or constraints to the message processing.</p>
</section>
</section>
<section data-type="sect1" id="_authentication">
<h1>Authentication</h1>
<p>In Chapters 2 and 3, the STOMP clients are <a contenteditable="false" data-primary="authentication" data-secondary="in STOMP" data-type="indexterm"> </a>connected to<a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="authentication" data-type="indexterm"> </a> the broker <em>anonymously</em>. We <a contenteditable="false" data-primary="anonymous connections, STOMP clients to brokers" data-type="indexterm"> </a>do not pass any user credentials that the broker could use to authenticate the user and check whether he or she can send and/or receive messages.</p>
<p>If the STOMP broker is configured to accept secured connections, the client needs to pass <code>login</code> and <code>passcode</code> parameters when it connects to the STOMP broker.</p>
<div data-type="note">
<p>By default, ActiveMQ accepts anonymous connections. <a contenteditable="false" data-primary="ActiveMQ" data-secondary="security settings, changing" data-type="indexterm"> </a>To change its security settings to authenticate users and grant them restricted privileges (so that they can only receive messages from example), consult its <a href="http://bit.ly/activemq-sec">security documentation page</a>.</p>
</div>
<section data-type="sect2" id="__code_stompkit_code_example">
<h2>StompKit Example</h2>
<p>To use an authenticated connection with <code>StompKit</code>, you need<a contenteditable="false" data-primary="authentication" data-secondary="in STOMP" data-tertiary="StompKit example" data-type="indexterm"> </a> to<a contenteditable="false" data-primary="StompKit" data-secondary="authenticated connection (example)" data-type="indexterm"> </a> pass the login and passcode parameters to the headers dictionary when calling the <code>connectWithHeaders:completionHandler</code> method on the <code>STOMPClient</code>. <code>StompKit</code> defines the <code>kHeaderLogin</code> and <code>kHeaderPasscode</code> constants to represent the headers keys:</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)connect
{
NSLog(@"Connecting...");
NSString *login = @"...";
NSString *passcode = @"...";
[self.client connectWithHeaders:@{ @"client-id": self.deviceID,
kHeaderLogin: login,
kHeaderPasscode: passcode }
completionHandler:^(STOMPFrame *connectedFrame,
NSError *error) {
...
}];
}
</pre>
</section>
<section data-type="sect2" id="__code_stomp_js_code_example">
<h2>stomp.js Example</h2>
<p>A stomp.js client can be authenticated by adding the <code>login</code> and <code>passcode</code> headers to the<a contenteditable="false" data-primary="stomp.js" data-secondary="authentication example" data-type="indexterm"> </a> first<a contenteditable="false" data-primary="authentication" data-secondary="in STOMP" data-tertiary="stomp.js example" data-type="indexterm"> </a> parameter of its <code>connect</code> method:</p>
<pre data-code-language="js" data-type="programlisting">
function connect(url) {
// create the STOMP client
client = Stomp.client(url);
...
var connectedCallback = function(frame) {
...
};
var login = ...;
var passcode = ...;
client.connect({
login: login,
passcode: passcode,
}, connectedCallback);
}</pre>
</section>
</section>
<section data-type="sect1" id="_message_acknowledgement">
<h1>Message Acknowledgment</h1>
<p>Message acknowledgment <a contenteditable="false" data-primary="message acknowledgement in STOMP" data-type="indexterm"> </a>is a feature<a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="message acknowledgement" data-type="indexterm"> </a> available to STOMP <em>consumers</em>.</p>
<p>When the broker <a contenteditable="false" data-primary="consumers" data-secondary="message acknowledgement for STOMP consumers" data-type="indexterm"> </a>delivers a message to a consumer, there is a transfer of responsibility between the broker and the consumer to determine which is the <em>owner</em> of the <span class="keep-together">message.</span> The consumer becomes responsible for the message by <em>acknowledging</em> the message.</p>
<p>By default, the STOMP broker will consider the message automatically acknowledged when it is delivered to the consumer.</p>
<p>However, there are cases in which the consumer may prefer to explicitly acknowledge the message. It leaves a window of opportunity to determine whether it can handle the message or not. For example, the client needs to write the message payload in a data store. There may be issues with opening a connection to the data store and the client could choose to acknowledge the message only after having successfully written its body to the data store. In case of failure, it will instead <em>nack</em> the message (explicitly refuse to take ownership of it). When the STOMP broker is informed of this negative acknowledgment, it may then decide to deliver the message to another consumer subscribed to the destination or try again some time later depending on its configuration.</p>
<p>The consumer specifies its type of acknowledgement when it subscribes to a destination.<a contenteditable="false" data-primary="Acknowledgment modes (STOMP)" data-type="indexterm"> </a> STOMP supports<a contenteditable="false" data-primary="auto acknowledgment (STOMP)" data-type="indexterm"> </a> three types of acknowledgements:</p>
<ul>
<li>
<p><code>auto</code> (by default)</p>
</li>
<li>
<p><code>client</code></p>
</li>
<li>
<p><code>client-individual</code></p>
</li>
</ul>
<p>If the client does not specify any type of acknowledgment or if it uses <code>default</code>, it does not need to send any acknowledgment; the STOMP broker will consider the message acknowledged as soon as it is delivered to the client.</p>
<p>If <code>client</code> or <code>client-individual</code> is used, the <a contenteditable="false" data-primary="client acknowledgment (STOMP)" data-type="indexterm"> </a>consumer must send acknowledgments to the server with the <code>message-id</code> of the message that is acknowledged.<a contenteditable="false" data-primary="client-individual acknowledgment (STOMP)" data-type="indexterm"> </a> The <span class="keep-together">difference</span> between <code>client</code> and <code>client-individual</code> is that <code>client</code> will acknowledge the message <em>and all other messages previously delivered to the consumer</em> <code>client-individual</code> will only acknowledge the message and no other messages. The consumer acknowledges a message by sending an <code>ACK</code> frame to the STOMP broker.</p>
<p>If <code>client</code> and <code>client-individual</code> are used, the consumer can explicitly refuse to handle the message by sending a <code>NACK</code> frame, which is a negative acknowledgment.</p>
<section data-type="sect2" id="__code_stompkit_code_example_2">
<h2>StompKit Example</h2>
<p>The message acknowledgment is specified when the <code>STOMPClient</code> subscribes to a destination<a contenteditable="false" data-primary="message acknowledgement in STOMP" data-secondary="StompKit example" data-type="indexterm"> </a> by <a contenteditable="false" data-primary="StompKit" data-secondary="message acknowledgement example" data-type="indexterm"> </a>calling its <code>subscribeTo:headers:messageHandler:</code> method. To specify a <code>client</code> or <code>client-individual</code> acknowledgment, you must set an <code>ack</code> header. <em>StompKit.h</em> defines constants to represent the header name, <code>kHeaderAck</code>, and its accepted values, <code>kAckAuto</code>, <code>kAckClient</code>, and <code>kAckClientIndividual</code>.</p>
<p>The <code>STOMPMessage</code> parameter of the <code>messageHandler</code> has two methods, <code>ack</code> and <code>nack</code>, to acknowledge or nack the message, respectively.</p>
<p>If the <code>ack</code> header is not set or if it set to <code>auto</code>, message acknowledgment is performed by the broker and calling the <code>STOMPMessage</code>'s <code>ack</code> and <code>nack</code> methods will do nothing:</p>
<pre data-code-language="objc" data-type="programlisting">
// use client acknowledgment
[self.client subscribeTo:destination
headers:@{kHeaderAck: kAckClient}
messageHandler:^(STOMPMessage *message) {
// process the message
// ...
// acknowledge it
[message ack];
// or nack it with
// [message nack]
}];</pre>
</section>
<section data-type="sect2" id="__code_stomp_js_code_example_2">
<h2>stomp.js Example</h2>
<p>The <code>client</code> can specify the type of acknowledgment by passing a dictionary with the <code>ack</code> header <a contenteditable="false" data-primary="stomp.js" data-secondary="message acknowledgement example" data-type="indexterm"> </a>as the <a contenteditable="false" data-primary="message acknowledgement in STOMP" data-secondary="stomp.js example" data-type="indexterm"> </a>last parameter of its <code>subscribe</code> message.</p>
<p>The <code>message</code> parameter of the <code>subscribe</code> callback has two methods, <code>ack</code> and <code>nack</code>, to acknowledge or nack the message, respectively. If the acknowledgment type is <code>auto</code> (or if it is not specified at all), these <code>ack</code> and <code>nack</code> methods will do nothing:</p>
<pre data-code-language="js" data-type="programlisting">
client.subscribe(destination,
function(message) {
// process the message
...
// acknowledge it
message.ack();
// or you can nack it by calling message.nack() instead.
},
{"ack": "client"}
);</pre>
<p>There are many use cases where it is not necessary to use explicit acknowledgment.</p>
<p>For example, in the Locations web application, we do not need to acknowledge every message that we receive from the devices with its GPS position. At worst, there may be a problem displaying the position, but we know that other messages will come later to update the device’s position.</p>
<p>Besides, acknowledging every message would have a performance cost. Sending the acknowledgment back to the broker would involve an additional network trip for every message.</p>
<p>The <code>Locations</code> iOS application is also consuming messages from the device’s text queue. These messages may be more important to acknowledge explicitly. We could enhance the application by letting the user confirm that it has read the message’s text and the message would be acknowledged after this confirmation only.</p>
<p>We could also let the user reject it by negatively acknowledging the message. In that case, these <em>nacked</em> messages would be handled back by the STOMP broker. Depending on the broker you use, it may provide additional features to handle these messages. Messages that are nacked multiple times from a destination are commonly sent to a dead letter queue. An administrator can then inspect this dead letter queue to determine what to do with these messages. For example, it can send them to another device, send alerts about the device that rejected them, and so on.</p>
</section>
</section>
<section data-type="sect1" id="_transactions">
<h1>Transactions</h1>
<p>STOMP has basic support for transactions.</p>
<p>Sending a message or acknowledging the consumption of messages can be performed inside a <a contenteditable="false" data-primary="transactions in STOMP" data-type="indexterm"> </a>transaction. <a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="transactions" data-type="indexterm"> </a>This means that the messages and acknowledgments are not processed by the broker when it receives the corresponding frames but when the transaction completes. If the client does not complete the transaction or aborts it, the broker will not process the frames that it received inside the transaction and will just discard them. Transactions ensure that messages and acknowledgment processing are <em>atomic</em>. <em>All</em> transacted messages and acknowledgments will be processed by the broker when the transaction is committed or <em>none</em> will be if the transaction is aborted.</p>
<p>A transaction is started by the client by sending a <code>BEGIN</code> frame to the broker.<a contenteditable="false" data-primary="BEGIN frame (STOMP)" data-type="indexterm"> </a> This frame must have a <code>transaction</code> header whose value is a transaction identifier that must be unique within a STOMP connection.<a contenteditable="false" data-primary="headers" data-secondary="in STOMP frames" data-tertiary="transaction headers" data-type="indexterm"> </a></p>
<p>Sending a message can then be part of this transaction by adding a <code>transaction</code> header to its <code>SEND</code> frames set <a contenteditable="false" data-primary="SEND frames (STOMP), transaction header in" data-type="indexterm"> </a>to the same transaction identifier. If a consumer is subscribed to a STOMP destination with <code>client</code> or <code>client-individual</code> acknowledgment modes, it can also make the message acknowledgment (or nack) inside a transaction by setting the <code>transaction</code> header on the <code>ACK</code> (or <code>NACK</code>) frame.<a contenteditable="false" data-primary="message acknowledgement in STOMP" data-secondary="inside transactions" data-type="indexterm"> </a></p>
<div data-type="note">
<p>By default, STOMP consumers use <code>auto</code> acknowledgment. In that case, the message acknowledgment is performed automatically by the STOMP broker when the message is delivered to the client and the acknowledgment <em>cannot</em> be put inside a transaction.</p>
</div>
<p>To complete this active transaction and allow the broker to process it, the client must send a <code>COMMIT</code> frame with the same <code>transaction</code> header as in the corresponding <code>BEGIN</code> frame that started the transaction.<a contenteditable="false" data-primary="COMMIT frame (STOMP)" data-type="indexterm"> </a> To abort (or roll back) a transaction and discard any messages or acknowledgments inside it, the client must instead send an <code>ABORT</code> frame <a contenteditable="false" data-primary="ABORT frame (STOMP)" data-type="indexterm"> </a>with this <code>transaction</code> header.</p>
<div data-type="warning">
<p>Beginning a transaction is not sufficient to send subsequent messages inside it. If a transaction is begun, the message to send must have its <code>transaction</code> header set to the transaction identifier. Otherwise, the STOMP broker will not consider the message to be part of the transaction and will process it when it receives it instead of waiting for the transaction to complete. If the client decides to abort the transaction, the message will have already been processed by the broker and will not be discarded.</p>
</div>
<p>STOMP does not provide a transaction timeout that would abort the transaction if it is not completed in a timely fashion. The transaction lifecycle (controlled by <code>BEGIN</code> and <code>COMMIT</code>/<code>ABORT</code> frames) is the responsibility of the client. However, the broker will automatically abort any active transaction if the client sends a <code>DISCONNECT</code> frame or if the underlying TCP connection fails.</p>
<section data-type="sect2" id="__code_stompkit_code_example_3">
<h2>StompKit Example</h2>
<p>The <code>STOMPClient</code> can begin a transaction by <a contenteditable="false" data-primary="transactions in STOMP" data-secondary="StompKit example" data-type="indexterm"> </a>calling its <code>begin:</code> method and passing an <code>NSString</code> that will be used to identify the transaction.<a contenteditable="false" data-primary="StompKit" data-secondary="transaction example" data-type="indexterm"> </a> Alternatively, you can call its <code>begin</code> method (without any parameter) and a transaction identifier will be automatically generated. Both <code>begin:</code> and <code>begin</code> methods return a <code>STOMPTransaction</code> object. This object has a <code>identifier</code> property that contains the transaction identifier.</p>
<p>Sending, acknowledging, or nacking a message can then be part of a transaction by adding a <code>transaction</code> header set to the transaction identifier (StompKit.h defines a <code>kHeaderTransaction</code> to represent this <code>transaction</code> header).</p>
<p>Finally, the <code>STOMPTransaction</code> object has two methods, <code>commit</code> and <code>abort</code>, to commit or rollback the transaction, respectively:</p>
<pre data-code-language="objc" data-type="programlisting">
// begin a transaction
STOMPTransaction *transaction = [self.client begin];
// or STOMPTransaction *transaction = [self.client begin:mytxid];
NSLog(@"started transaction %@", transaction.identifier);
// send a message inside a transaction
[self.client sendTo:destination
headers:@{kHeaderTransaction: transaction.identifier}
body:body];
// acknowledge a message inside a transaction
[message ack:@{kHeaderTransaction: transaction.identifier}];
// or nack a message inside a transaction with
// [message nack:@{kHeaderTransaction: transaction.identifier}];
// commit the transaction
[transaction commit];
// or abort it
[transaction abort];</pre>
</section>
<section data-type="sect2" id="__code_stomp_js_code_example_3">
<h2>stomp.js Example</h2>
<p>The API is very similar to StompKit.<a contenteditable="false" data-primary="transactions in STOMP" data-secondary="stomp.js example" data-type="indexterm"> </a> The <code>client</code> object has a <code>begin</code> method that can take a parameter corresponding to <a contenteditable="false" data-primary="stomp.js" data-secondary="transaction example" data-type="indexterm"> </a>the transaction identifier. If there is no parameter, an identifier is automatically generated. The <code>begin</code> method returns a <code>transaction</code> object that has an <code>id</code> property corresponding to the transaction identifier.</p>
<p>Sending, acknowledging, or nacking a message can be part of a transaction by passing a <code>transaction</code> header set to the transaction identifier to these methods.</p>
<p>Finally, committing or aborting a transaction is performed by calling the <code>commit</code> or <code>abort</code> method, respectively, on the <code>transaction</code> object:</p>
<pre data-code-language="js" data-type="programlisting">
// begin a transaction
var tx = client.begin();
// or var tx = client.begin(mytxid);
console.log("started transaction " + tx.id);
// send a message inside a transaction
client.send(destination, {transaction: tx.id}, body);
// acknowledge a message inside a transaction
var subscription = client.subscribe(destination,
function(message) {
// do something with the message
...
// and acknowledge it inside the transaction
message.ack({ transaction: tx.id});
// or nack it inside the transaction
// message.nack({ transaction: tx.id});
},
{ack: 'client'}
);
// commit the transaction
tx.commit();
// or abort it
tx.abort();</pre>
</section>
</section>
<section data-type="sect1" id="_error_handling">
<h1>Error Handling</h1>
<p>Until now, we have used STOMP in a perfect world <a contenteditable="false" data-primary="error handling" data-secondary="in STOMP" data-type="indexterm"> </a>where no unexpected problems happened. <a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="error handling" data-type="indexterm"> </a>Realistically, problems will occur. On mobile devices, the network will be lost and the connection to the STOMP broker will be broken.</p>
<p>STOMP provides basic support to handle errors. The STOMP broker can inform the client that an error occurs by sending an <code>ERROR</code> frame to the client.<a contenteditable="false" data-primary="ERROR frame (STOMP)" data-type="indexterm"> </a> This frame can contain a <code>message</code> header that contains a short description of the error. Most STOMP brokers deliver <code>ERROR</code> frames with a message payload containing more detailed information on the error.</p>
<p>STOMP specifies that after delivering an <code>ERROR</code> frame to the client, the broker must close the connection. This means that STOMP is not resilient to error. If a single <span class="keep-together">error</span> occurs on the server, the broker will close the connection to the client. In addition, there is no guarantee that the client will be able to receive the <code>ERROR</code> frame before the connection is closed.</p>
<p>In practice, this implies that to be able to handle any errors in the client, we should:</p>
<ol>
<li>
<p>Handle <code>ERRORS</code> frames coming from the broker</p>
</li>
<li>
<p>Handle unexpected connection closed events</p>
</li>
</ol>
<section data-type="sect2" id="__code_stompkit_code_example_4">
<h2>StompKit Example</h2>
<p>We will modify the <code>Locations</code> iOS application to handle errors and automatically reconnect to the STOMP broker<a contenteditable="false" data-primary="error handling" data-secondary="in STOMP" data-tertiary="StompKit example" data-type="indexterm"> </a> after a delay.<a contenteditable="false" data-primary="StompKit" data-secondary="error handling example" data-type="indexterm"> </a></p>
<p>The <code>STOMPClient</code> has an <code>errorHandler</code> property that is called if the client encounters any error. Errors can come from the STOMP protocol (when the broker delivers an <code>ERROR</code> frame) or from the underlying network connection (e.g., if the network is lost or if the broker closes the connection before any <code>ERROR</code> frame is delivered).</p>
<p>The <code>errorHandler</code> property is a block with a standard <code>NSSError</code> parameter. If the error is coming from the STOMP broker, the corresponding <code>STOMPFrame</code> is stored in the error’s <code>userInfo</code> dictionary with the key <code>frame</code>.</p>
<p>There are two places where we must handle reconnection:</p>
<ul>
<li>
<p>During the initial connection (e.g., if the broker is not up during the initial reconnect, we will continue to attempt to connect to it until it is up again).</p>
</li>
<li>
<p>When we receive an error from the <code>STOMPClient</code>'s <code>errorHandler</code> property.</p>
</li>
</ul>
<p>In both cases, we will attempt to reconnect by disconnecting first (in the eventual case where the client is already connected), waiting for 10 seconds, and connecting again.<a contenteditable="false" data-primary="reconnecting to STOMP broker" data-secondary="StompKit iOS Locations application (example)" data-type="indexterm"> </a> This code can be encapsulated in a <code>reconnect:</code> method of the <code>MWMViewController</code> implementation:</p>
<pre data-code-language="objc" data-type="programlisting">
#pragma mark - Messaging
- (void)reconnect:(NSError *)error {
NSLog(@"got error %@", error);
STOMPFrame *frame = error.userInfo[@"frame"];
if (frame) {
NSString *message = frame.headers[@"message"];
NSLog(@"error from the STOMP protocol: %@", message);
}
[self disconnect];
sleep(10);
NSLog(@"Reconnecting...");
[self connect];
}</pre>
<p>We then must call this <code>reconnect:</code> method from the client’s <code>errorHandler</code> property and the <code>completionHandler</code> block of its <code>connectWithHeaders:completionHandler:</code> method (both called from the <code>MWMViewController</code> <code>connect</code> method):</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)connect
{
NSLog(@"Connecting...");
__weak typeof(self) weakSelf = self;
self.client.errorHandler = ^(NSError* error) {
[weakSelf reconnect:error];
};
[self.client connectWithHeaders:@{ @"client-id": self.deviceID }
completionHandler:^(STOMPFrame *connectedFrame,
NSError *error) {
if (error) {
NSLog(@"Error during connection: %@", error);
[weakSelf reconnect:error];
} else {
// we are connected to the STOMP broker without
// an error
NSLog(@"Connected");
[self subscribe];
}
}];
// when the method returns, we can not assume that the client is connected
}</pre>
<p>To avoid a retain/release cycle between <code>self</code> and the blocks, we need to create a <em>weak</em> reference to <code>self</code> to use it inside the blocks.</p>
</section>
<section data-type="sect2" id="__code_stomp_js_code_example_4">
<h2>stomp.js Example</h2>
<p>A stomp.js <code>client</code> can specify an <code>errorCallback</code> handler<a contenteditable="false" data-primary="stomp.js" data-secondary="error handling example" data-type="indexterm"> </a> as the last parameter of its <code>connect</code> method.<a contenteditable="false" data-primary="error handling" data-secondary="in STOMP" data-tertiary="stomp.js example" data-type="indexterm"> </a> This handler will be called whenever the client encounters an error (whether coming from the STOMP protocol or the underlying network connection).</p>
<p>We can modify the Locations web application to automatically reconnect when an error occurs.</p>
<p>We will create a <code>reconnect</code> method that disconnects the stomp.js client if it is connected and calls <code>connect</code> again with the<a contenteditable="false" data-primary="reconnecting to STOMP broker" data-secondary="stomp.js Locations web application (example)" data-type="indexterm"> </a> Web socket URL:</p>
<pre data-code-language="js" data-type="programlisting">
function reconnect(url) {
if (client.connected) {
console.log("disconnecting...");
disconnect()
}
console.log("reconnecting");
connect(url);
}</pre>
<p>We then need to create an <code>errorCallback</code> handler that calls this <code>reconnect</code> method and pass it as the last parameter of the <code>client</code>'s <code>connect</code> method:</p>
<pre data-code-language="js" data-type="programlisting">
function connect(url) {
var connectedCallback = function(frame) {
...
};
var errorCallback = function(error) {
client.debug("received error: " + error);
reconnect(url);
};
// create the STOMP client
client = Stomp.client(url);
// and connect to the STOMP broker
client.connect({}, connectedCallback, errorCallback);
}</pre>
<div data-type="warning">
<p>A Web socket can be opened only once. If a problem occurs and the socket is closed, it can no longer be used. This implies that we cannot just call the <code>client</code>'s <code>connect</code> method again as its Web Socket is no longer usable. Instead, we must create a <em>new</em> <code>client</code> that will open a new Web Socket.</p>
</div>
<p>Whenever an error occurs (e.g., if the network connection is broken or the STOMP broker becomes temporarily unavailable), the <code>errorCallback</code> will be called and the client will try to reconnect.</p>
<p>Depending on your application, you may instead decide to report the error to the user and let him know that the client is no longer able to exchange messages with the broker.</p>
</section>
</section>
<section data-type="sect1" id="ch_advanced_stomp_receipts">
<h1>Receipts</h1>
<p>STOMP provides a basic mechanism to let a client know when the broker has received and processed<a contenteditable="false" data-primary="receipts in STOMP" data-type="indexterm"> </a> its frames.<a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="receipts" data-type="indexterm"> </a> This can be used with any STOMP frames. For example, a client can be notified when the broker receives a message that a producer sent (using a <code>SEND</code> frame) or when a consumer subscribes to a destination (with a <code>SUBSCRIBE</code> frame).</p>
<p>To use this mechanism, the frame that is sent to the broker must include a <code>receipt</code> header with any arbitrary value.<a contenteditable="false" data-primary="headers" data-secondary="in STOMP frames" data-tertiary="receipt headers" data-type="indexterm"> </a> After the broker has processed the frame, it will deliver a <code>RECEIPT</code> frame to the client with a <code>receipt-id</code> header corresponding to the <code>receipt</code> header in the frame that has been processed.<a contenteditable="false" data-primary="headers" data-secondary="in STOMP frames" data-tertiary="receipt-id headers" data-type="indexterm"> </a></p>
<p>As an example, we can use <code>receipt</code> to confirm that a consumer has been subscribed successfully to a destination.<a contenteditable="false" data-primary="publish/subscribe messaging model" data-secondary="using receipt to confirm successful subscription" data-type="indexterm"> </a> If the broker cannot successfully create the subscription, it will send back an <code>ERROR</code> frame to the client and close the connection. In practice, this means that a successful subscription is <em>silent</em>: the client is not informed of its success. We can use receipts to have an explicit confirmation of the subscription by adding a <code>receipt</code> header when the client subscribes to a destination. The broker must then deliver a <code>RECEIPT</code> frame that will inform the client that the broker has processed its subscription successfully. If the subscription is unsuccessful, the broker will deliver an <code>ERROR</code> frame that has a <code>receipt-id</code> header corresponding to the <code>RECEIPT</code>'s <code>receipt</code> header to correlate the error.</p>
<p>Another use case for receipts is to make sending a message <em>synchronous</em>.<a contenteditable="false" data-primary="synchronous messaging, using receipts in STOMP" data-type="indexterm"> </a> The client sends a message with a <code>receipt</code> header and blocks until it receives the corresponding <code>RECEIPT</code> frame. This adds reliability (the client is sure that the broker has processed its message) at the cost of performance (the client can do nothing until the <code>RECEIPT</code> frame is received).</p>
<section data-type="sect2" id="__code_stompkit_code_example_5">
<h2>StompKit Example</h2>
<p>A <code>STOMPClient</code> has a <code>receiptHandler</code> property that can be set to handle receipts.<a contenteditable="false" data-primary="StompKit" data-secondary="receipts example" data-type="indexterm"> </a> The <code>receiptHandler</code> is a block that takes a <code>STOMPFrame</code> corresponding to a <code>RECEIPT</code> frame.<a contenteditable="false" data-primary="receipts in STOMP" data-secondary="StompKit example" data-type="indexterm"> </a></p>
<p>Let’s add a receipt for the device text queue’s subscription to the Locations iOS application.</p>
<p>In its <code>subscribe</code> method, we will build a <code>receipt</code> identifier for the subscription receipt and set the <code>client</code>'s <code>receiptHandler</code>. In this block, we just check if the <code>headers</code> of the <code>frame</code> parameter contain a <code>kHeaderReceiptID</code> key with a value that matches the <code>receipt</code> identifier.</p>
<p>To receive such a receipt from the subscription, we need to add a <code>kHeaderReceipt</code> header to the <code>subscribeTo:headers:messageHandler:</code> and set it to the <code>receipt</code> identifier:</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)subscribe
{
// susbscribes to the device text queue:
NSString *destination =
[NSString stringWithFormat:@"/queue/device.%@.text", self.deviceID];
// build a receipt identifier
NSString *receipt = [NSString stringWithFormat:@"%@-%@",
self.deviceID, destination];
// set the client's receiptHandler to handle any receipt delivered by
// the broker
self.client.receiptHandler = ^(STOMPFrame *frame) {
NSString *receiptID = [frame.headers objectForKey:kHeaderReceiptID];
if ([receiptID isEqualToString:receipt]) {
NSLog(@"Subscribed to %@", destination);
}
};
NSLog(@"subscribing to %@", destination);
// pass a receipt header to be informed of the subscription processing
subscription = [self.client subscribeTo:destination
headers:@{kHeaderReceipt: receipt}
messageHandler:^(STOMPMessage *message) {
...
}];
}
</pre>
<p>If the Locations iOS application is run with this code, we see the log that confirms that the client is successfully subscribed to the destination:</p>
<pre data-type="programlisting">
2014-04-21 17:30:39.205 Locations[2384:3903] Subscribing to /queue/device.2262EC25-E9FD-4578-BADE-4E113DE45934.text
2014-04-21 17:30:39.208 Locations[2384:3903] Subscribed to /queue/device.2262EC25-E9FD-4578-BADE-4E113DE45934.text</pre>
<p>Note that the client’s <code>receiptHandler</code> will receive any receipt delivered to the client. If you expect receipts from different STOMP frames, the client will have to handle all of them from a single <code>receiptHandler</code> block.</p>
</section>
<section data-type="sect2" id="__code_stomp_js_code_example_5">
<h2>stomp.js Example</h2>
<p>The <code>stomp.js</code> client has an <code>onreceipt</code> handler <a contenteditable="false" data-primary="receipts in STOMP" data-secondary="stomp.js example" data-type="indexterm"> </a>that can be set to receive receipts.<a contenteditable="false" data-primary="stomp.js" data-secondary="receipts example" data-type="indexterm"> </a> It takes a function with a single <code>frame</code> parameter corresponding to a <code>RECEIPT</code> frame.</p>
<p>To receive a receipt for a subscription, we just need to add a <code>receipt</code> header to the headers passed as the last parameter of the <code>subscribe</code> method:</p>
<pre data-code-language="js" data-type="programlisting">
var destination = "/topic/device.*.location";
var receipt = "receipt_" + destination;
client.onreceipt = function(frame) {
var receiptID = frame.headers['receipt-id'];
if (receipt === receiptID) {
console.log("subscribed to " + destination);
}
}
client.subscribe(destination, function(message) {
...
}, {receipt: receipt});</pre>
<p>If we reload the Locations web application, the browser console will display a log when the receipt confirming the subscription is handled by the client.</p>
<p>All <code>stomp.js</code> method that corresponds to STOMP frames accept a <code>headers</code> parameter that can be used to receive <code>RECEIPT</code> frames from the broker.</p>
</section>
</section>
<section data-type="sect1" id="_heart_beating">
<h1>Heart-Beating</h1>
<p>STOMP offers a mechanism to test the healthiness of a network connection between a STOMP client and a broker<a contenteditable="false" data-primary="heart-beating" data-secondary="in STOMP" data-type="indexterm"> </a> using heart-beating. <a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-tertiary="heart-beating" data-type="indexterm"> </a>In the absence of messages exchanged between the STOMP client and the broker, both can send a <em>heartbeat</em> periodically to inform the other that it is alive but has no activity.</p>
<p>If heart-beating is enabled, this allows the client and the broker to be informed in case of network failures and act accordingly (the client could try to reconnect to the broker, the broker could clean up the resources created on behalf of the client, etc.).</p>
<p>Heart-beating is negotiated between the client and the broker when the client connects to the broker (by sending a <code>CONNECT</code> frame) and the broker accepts the connection (by sending a <code>CONNECTED</code> frame to the client). Both frames accept a <code>heart-beat</code> header whose value <a contenteditable="false" data-primary="headers" data-secondary="in STOMP frames" data-tertiary="heart-beat headers" data-type="indexterm"> </a>contains two integers separated by a comma:</p>
<pre data-type="programlisting">
CONNECT
heart-beat:<cx>,<cy>
CONNECTED:
heart-beat:<sx>,<sy></pre>
<p>Let's take a look at what this code means:</p>
<ul>
<li>
<p><code><cx></code> The smallest number of milliseconds between heartbeats that the client guarantees. If it is set to <code>0</code>, the client will not send any heartbeat at all.</p>
</li>
<li>
<p><code><cy></code> The desired number of milliseconds between heartbeats coming from the broker. If it is set to <code>0</code>, the client does not want to receive any heartbeat.</p>
</li>
<li>
<p><code><sx></code> The smallest number of milliseconds between heartbeats that the broker guarantees. If it is set to <code>0</code>, the broker will not send any heartbeat.</p>
</li>
<li>
<p><code><sy></code> The desired number of milliseconds between heartbeats coming from the client. If it is set to <code>0</code>, the broker does not want to receive any heartbeat.</p>
</li>
</ul>
<p>When the client is successfully connected to the STOMP broker (it has received the <code>CONNECTED</code> frame), it must determine the frequency of the heartbeats to send to the broker and the frequency of heartbeats coming from the broker.</p>
<p>The values that are used to determine the frequency of<a contenteditable="false" data-primary="heart-beating" data-secondary="in STOMP" data-tertiary="values determining frequency of hearbeats" data-type="indexterm"> </a> heartbeats sent to the broker are <code><cx></code> and <code><sy></code>. If <code><cx></code> is <code>0</code> (the client will send no heartbeat) or if <code><sy></code> is <code>0</code> (the broker does not expect any client heartbeats), there will be no client heartbeating at all. This means that the broker will not be able to test the health of the client connection. Otherwise, both server and broker expect to exchange client heartbeats. The frequency is then determined by the maximum value between the value guaranteed by the client, <code><cx></code>, and the value desired by the broker, <code><sy></code>. In other words, the client must send heartbeats at a frequency of at least <code>MAX(cx,sy)</code> milliseconds.</p>
<p>For the heartbeats sent by the broker to the client, the algorithm is the same but uses the <code><cy></code> and <code><sx></code> values.</p>
<p>Let’s take a simple example to illustrate the algorithm. We have a STOMP client that connects to the broker with the <code>heart-beat</code> header set to <code>0,60000</code> (the client will not send any heartbeats, but desires to receive the broker’s heartbeats every minute):</p>
<pre data-type="programlisting">
CONNECT
heart-beat:0,60000
....</pre>
<p>The broker accepts the connection and replies with a <code>CONNECTED</code> frame that contains a <code>heart-beat</code> header set to <code>20000,30000</code> (the broker guarantees to send heartbeats every 20 seconds and desires to receive the client’s hearbeats every 30 seconds):</p>
<pre data-type="programlisting">
CONNECTED
heart-beat:20000,30000
....</pre>
<p>Because the client specified that it will send no heartbeat (<code>0</code> as the first value of the <code>CONNECT</code>'s <code>heart-beat</code> header), client heartbeating is disabled and the broker should not expect any (even though it <em>desired</em> to receive them every 20 seconds).</p>
<p>The client desired to receive the broker heartbeat every minute (<code>60000</code> as the second value of the <code>CONNECT</code>'s <code>heart-beat</code> header). The broker replied that it can guarantee to send them at least every 30 seconds (second value of the <code>CONNECTED</code>'s <code>heart-beat</code> header). In that case, the broker and the client agrees that the broker must send heartbeats every minute (the maximum between 1 minute and 30 seconds). In other words, the broker <em>could</em> send heartbeats every 30 seconds (as it guaranteed in the <code>CONNECTED</code> frame), but the client will only check them every minute.</p>
<div data-type="note">
<p>ActiveMQ supports heart-beating and mirrors the heartbeat values sent by the STOMP client.<a contenteditable="false" data-primary="ActiveMQ" data-secondary="heart-beating support" data-type="indexterm"> </a> This lets the STOMP client be the sole decider of the heart-beating values.</p>
<p>This means that if a client connects with a <code>heart-beat</code> header set to <code><cx>,<sy></code>, the broker will accept the connection with a <code>heart-beat</code> header set to <code><sy>,<cx></code>.</p>
<p>The client guaranteed to send hearbeats every <code><cx></code> milliseconds, so the broker replied that it desires to receive them at this rate. The client desired to receive heartbeats every <code><sy></code> milliseconds, so the broker replied that it guarantees to send its heartbeat at this rate.</p>
</div>
<p>The client should set its <code>heart-beat</code> header according to its usage.<a contenteditable="false" data-primary="heart-beating" data-secondary="in STOMP" data-tertiary="clients, setting heart-beat header" data-type="indexterm"> </a> For example, if an application is sending messages at a regular rate (such as the <code>Locations</code> iOS application), there is no need to send heartbeats to the broker at a similar (or faster) rate. The messages sent are proof enough of the client activity. Likewise, if a client expects to receive messages at a regular rate (such as the Locations web application), there is no need to require the broker to send frequent heartbeats.</p>
<p>However, if the application does not send messages often (the Locations web application will seldom sent text messages to the device’ text topics), it probably should send heartbeats more frequently to inform the broker of its healthiness. Likewise, if the application does not receive messages often (such as the Locations iOS application), it should desire more frequent heartbeats from the broker.</p>
<section data-type="sect2" id="__code_stompkit_code_example_6">
<h2>StompKit Example</h2>
<p>A <code>STOMPClient</code> supports<a contenteditable="false" data-primary="StompKit" data-secondary="heart-beating example" data-type="indexterm"> </a> heart-beating <a contenteditable="false" data-primary="heart-beating" data-secondary="in STOMP" data-tertiary="StompKit example" data-type="indexterm"> </a>by passing the <code>heart-beat</code> header when it connects to the broker using its <code>connectWithHeaders:completionHandler</code> method.</p>
<p>By default, <code>StompKit</code> defines a heartbeat of <code>5000,10000</code> (send heartbeats every 5 seconds and receive them every 10 seconds).</p>
<p>Let’s add heart-beating to the Locations iOS application. The application sends messages often (every time the device GPS position is updated), but receives them less frequently (when a user sends a message from the web application). We will guarantee to send heartbeats every minute (60000ms) and desire to receive them from the broker every 20 seconds (20000ms):</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)connect
{
NSLog(@"Connecting...");
self.client.errorHandler = ^(NSError* error) {
NSLog(@"got error from STOMP: %@", error);
};
// will send a heartbeat at most every minute.
// expect broker's heartbeat at least every 20 seconds.
NSString *heartbeat = @"60000,20000";
[self.client connectWithHeaders:@{ @"client-id": self.deviceID,
kHeaderHeartBeat: heartbeat }
completionHandler:^(STOMPFrame *connectedFrame,
NSError *error) {
...
}];
}</pre>
</section>
<section data-type="sect2" id="__code_stomp_js_code_example_6">
<h2>stomp.js Example</h2>
<p>The <code>STOMP</code> client has a <code>heartbeat</code> property <a contenteditable="false" data-primary="heart-beating" data-secondary="in STOMP" data-tertiary="stomp.js example" data-type="indexterm"> </a>composed of <a contenteditable="false" data-primary="stomp.js" data-secondary="heart-beating example" data-type="indexterm"> </a>two properties:</p>
<ul>
<li>
<p><code>heartbeat.outgoing</code> is the guaranteed frequency of heartbeat it can send to the broker (i.e., <code><cx></code>)</p>
</li>
<li>
<p><code>heartbeat.incoming</code> is the desired frequency of heartbeat coming from the broker (i.e., <code><cy></code>)</p>
</li>
</ul>
<p>By default, stomp.js defines a heartbeat of <code>10000,10000</code> (to send and receive heartbeats every 10 seconds).</p>
<p>These properties must be modified <em>before</em> the <code>connect</code> method is called to take them into account:</p>
<pre data-code-language="js" data-type="programlisting">
// create the STOMP client
client = Stomp.client(url);
// will send a heartbeat at most every 20 seconds
client.heartbeat.outgoing = 20000;
// expects broker's heartbeat at least every minute
client.heartbeat.incoming = 60000;
client.connect({}, function(frame) {
...
});</pre>
</section>
</section>
<section data-type="sect1" id="_summary_4">
<h1>Summary</h1>
<p>In an ideal world, only the basic features of STOMP would be required to use messaging in mobile and web applications. However, to handle errors that will eventually happen under normal use, we need to leverage advanced STOMP features.</p>
<p>In this chapter, we learned to use:</p>
<ul>
<li>
<p><em>Authentication</em> to ensure that only authenticated clients can communicate with the STOMP broker</p>
</li>
<li>
<p><em>Acknowledgment</em> to let the client explicitly accept the delivery of a message</p>
</li>
<li>
<p><em>Transaction</em> to send messages as a single atomic unit of work</p>
</li>
<li>
<p><em>Error handling</em> to face unexpected issues and eventually reconnect to the broker</p>
</li>
<li>
<p><em>Receipt</em> to receive confirmation that a frame has been succesfully processed by the broker</p>
</li>
<li>
<p><em>Heart-beating</em> to ensure that the network connection between the client and broker is healthy (and to ensure that it will kill the connection if that is not the case)<a contenteditable="false" data-primary="STOMP" data-secondary="advanced" data-startref="STOMPadv" data-type="indexterm"> </a></p>
</li>
</ul>
</section>
</section>