1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.mqtt;
17
18 import static io.netty.util.internal.ObjectUtil.checkPositive;
19
20 import io.netty.buffer.ByteBuf;
21 import io.netty.buffer.Unpooled;
22 import io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType;
23 import io.netty.util.CharsetUtil;
24
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28
29 public final class MqttMessageBuilders {
30
31 public static final class PublishBuilder {
32 private String topic;
33 private boolean retained;
34 private MqttQoS qos;
35 private ByteBuf payload;
36 private int messageId;
37 private MqttProperties mqttProperties;
38
39 PublishBuilder() {
40 }
41
42 public PublishBuilder topicName(String topic) {
43 this.topic = topic;
44 return this;
45 }
46
47 public PublishBuilder retained(boolean retained) {
48 this.retained = retained;
49 return this;
50 }
51
52 public PublishBuilder qos(MqttQoS qos) {
53 this.qos = qos;
54 return this;
55 }
56
57 public PublishBuilder payload(ByteBuf payload) {
58 this.payload = payload;
59 return this;
60 }
61
62 public PublishBuilder messageId(int messageId) {
63 this.messageId = messageId;
64 return this;
65 }
66
67 public PublishBuilder properties(MqttProperties properties) {
68 this.mqttProperties = properties;
69 return this;
70 }
71
72 public MqttPublishMessage build() {
73 MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0);
74 MqttPublishVariableHeader mqttVariableHeader =
75 new MqttPublishVariableHeader(topic, messageId, mqttProperties);
76 return new MqttPublishMessage(mqttFixedHeader, mqttVariableHeader, Unpooled.buffer().writeBytes(payload));
77 }
78 }
79
80 public static final class ConnectBuilder {
81
82 private MqttVersion version = MqttVersion.MQTT_3_1_1;
83 private String clientId;
84 private boolean cleanSession;
85 private boolean hasUser;
86 private boolean hasPassword;
87 private int keepAliveSecs;
88 private MqttProperties willProperties = MqttProperties.NO_PROPERTIES;
89 private boolean willFlag;
90 private boolean willRetain;
91 private MqttQoS willQos = MqttQoS.AT_MOST_ONCE;
92 private String willTopic;
93 private byte[] willMessage;
94 private String username;
95 private byte[] password;
96 private MqttProperties properties = MqttProperties.NO_PROPERTIES;
97
98 ConnectBuilder() {
99 }
100
101 public ConnectBuilder protocolVersion(MqttVersion version) {
102 this.version = version;
103 return this;
104 }
105
106 public ConnectBuilder clientId(String clientId) {
107 this.clientId = clientId;
108 return this;
109 }
110
111 public ConnectBuilder cleanSession(boolean cleanSession) {
112 this.cleanSession = cleanSession;
113 return this;
114 }
115
116 public ConnectBuilder keepAlive(int keepAliveSecs) {
117 this.keepAliveSecs = keepAliveSecs;
118 return this;
119 }
120
121 public ConnectBuilder willFlag(boolean willFlag) {
122 this.willFlag = willFlag;
123 return this;
124 }
125
126 public ConnectBuilder willQoS(MqttQoS willQos) {
127 this.willQos = willQos;
128 return this;
129 }
130
131 public ConnectBuilder willTopic(String willTopic) {
132 this.willTopic = willTopic;
133 return this;
134 }
135
136
137
138
139 @Deprecated
140 public ConnectBuilder willMessage(String willMessage) {
141 willMessage(willMessage == null ? null : willMessage.getBytes(CharsetUtil.UTF_8));
142 return this;
143 }
144
145 public ConnectBuilder willMessage(byte[] willMessage) {
146 this.willMessage = willMessage;
147 return this;
148 }
149
150 public ConnectBuilder willRetain(boolean willRetain) {
151 this.willRetain = willRetain;
152 return this;
153 }
154
155 public ConnectBuilder willProperties(MqttProperties willProperties) {
156 this.willProperties = willProperties;
157 return this;
158 }
159
160 public ConnectBuilder hasUser(boolean value) {
161 this.hasUser = value;
162 return this;
163 }
164
165 public ConnectBuilder hasPassword(boolean value) {
166 this.hasPassword = value;
167 return this;
168 }
169
170 public ConnectBuilder username(String username) {
171 this.hasUser = username != null;
172 this.username = username;
173 return this;
174 }
175
176
177
178
179 @Deprecated
180 public ConnectBuilder password(String password) {
181 password(password == null ? null : password.getBytes(CharsetUtil.UTF_8));
182 return this;
183 }
184
185 public ConnectBuilder password(byte[] password) {
186 this.hasPassword = password != null;
187 this.password = password;
188 return this;
189 }
190
191 public ConnectBuilder properties(MqttProperties properties) {
192 this.properties = properties;
193 return this;
194 }
195
196 public MqttConnectMessage build() {
197 MqttFixedHeader mqttFixedHeader =
198 new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
199 MqttConnectVariableHeader mqttConnectVariableHeader =
200 new MqttConnectVariableHeader(
201 version.protocolName(),
202 version.protocolLevel(),
203 hasUser,
204 hasPassword,
205 willRetain,
206 willQos.value(),
207 willFlag,
208 cleanSession,
209 keepAliveSecs,
210 properties);
211 MqttConnectPayload mqttConnectPayload =
212 new MqttConnectPayload(clientId, willProperties, willTopic, willMessage, username, password);
213 return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
214 }
215 }
216
217 public static final class SubscribeBuilder {
218
219 private List<MqttTopicSubscription> subscriptions;
220 private int messageId;
221 private MqttProperties properties;
222
223 SubscribeBuilder() {
224 }
225
226 public SubscribeBuilder addSubscription(MqttQoS qos, String topic) {
227 ensureSubscriptionsExist();
228 subscriptions.add(new MqttTopicSubscription(topic, qos));
229 return this;
230 }
231
232 public SubscribeBuilder addSubscription(String topic, MqttSubscriptionOption option) {
233 ensureSubscriptionsExist();
234 subscriptions.add(new MqttTopicSubscription(topic, option));
235 return this;
236 }
237
238 public SubscribeBuilder messageId(int messageId) {
239 this.messageId = messageId;
240 return this;
241 }
242
243 public SubscribeBuilder properties(MqttProperties properties) {
244 this.properties = properties;
245 return this;
246 }
247
248 public MqttSubscribeMessage build() {
249 MqttFixedHeader mqttFixedHeader =
250 new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
251 MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader =
252 new MqttMessageIdAndPropertiesVariableHeader(messageId, properties);
253 MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(subscriptions);
254 return new MqttSubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
255 }
256
257 private void ensureSubscriptionsExist() {
258 if (subscriptions == null) {
259 subscriptions = new ArrayList<MqttTopicSubscription>(5);
260 }
261 }
262 }
263
264 public static final class UnsubscribeBuilder {
265
266 private List<String> topicFilters;
267 private int messageId;
268 private MqttProperties properties;
269
270 UnsubscribeBuilder() {
271 }
272
273 public UnsubscribeBuilder addTopicFilter(String topic) {
274 if (topicFilters == null) {
275 topicFilters = new ArrayList<String>(5);
276 }
277 topicFilters.add(topic);
278 return this;
279 }
280
281 public UnsubscribeBuilder messageId(int messageId) {
282 this.messageId = messageId;
283 return this;
284 }
285
286 public UnsubscribeBuilder properties(MqttProperties properties) {
287 this.properties = properties;
288 return this;
289 }
290
291 public MqttUnsubscribeMessage build() {
292 MqttFixedHeader mqttFixedHeader =
293 new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
294 MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader =
295 new MqttMessageIdAndPropertiesVariableHeader(messageId, properties);
296 MqttUnsubscribePayload mqttSubscribePayload = new MqttUnsubscribePayload(topicFilters);
297 return new MqttUnsubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
298 }
299 }
300
301 public interface PropertiesInitializer<T> {
302 void apply(T builder);
303 }
304
305 public static final class ConnAckBuilder {
306
307 private MqttConnectReturnCode returnCode;
308 private boolean sessionPresent;
309 private MqttProperties properties = MqttProperties.NO_PROPERTIES;
310 private ConnAckPropertiesBuilder propsBuilder;
311
312 private ConnAckBuilder() {
313 }
314
315 public ConnAckBuilder returnCode(MqttConnectReturnCode returnCode) {
316 this.returnCode = returnCode;
317 return this;
318 }
319
320 public ConnAckBuilder sessionPresent(boolean sessionPresent) {
321 this.sessionPresent = sessionPresent;
322 return this;
323 }
324
325 public ConnAckBuilder properties(MqttProperties properties) {
326 this.properties = properties;
327 return this;
328 }
329
330 public ConnAckBuilder properties(PropertiesInitializer<ConnAckPropertiesBuilder> consumer) {
331 if (propsBuilder == null) {
332 propsBuilder = new ConnAckPropertiesBuilder();
333 }
334 consumer.apply(propsBuilder);
335 return this;
336 }
337
338 public MqttConnAckMessage build() {
339 if (propsBuilder != null) {
340 properties = propsBuilder.build();
341 }
342 MqttFixedHeader mqttFixedHeader =
343 new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
344 MqttConnAckVariableHeader mqttConnAckVariableHeader =
345 new MqttConnAckVariableHeader(returnCode, sessionPresent, properties);
346 return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
347 }
348 }
349
350 public static final class ConnAckPropertiesBuilder {
351 private String clientId;
352 private Long sessionExpiryInterval;
353 private int receiveMaximum;
354 private Byte maximumQos;
355 private boolean retain;
356 private Long maximumPacketSize;
357 private int topicAliasMaximum;
358 private String reasonString;
359 private final MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
360 private Boolean wildcardSubscriptionAvailable;
361 private Boolean subscriptionIdentifiersAvailable;
362 private Boolean sharedSubscriptionAvailable;
363 private Integer serverKeepAlive;
364 private String responseInformation;
365 private String serverReference;
366 private String authenticationMethod;
367 private byte[] authenticationData;
368
369 public MqttProperties build() {
370 final MqttProperties props = new MqttProperties();
371 if (clientId != null) {
372 props.add(new MqttProperties.StringProperty(MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER.value(),
373 clientId));
374 }
375 if (sessionExpiryInterval != null) {
376 props.add(new MqttProperties.IntegerProperty(
377 MqttPropertyType.SESSION_EXPIRY_INTERVAL.value(), sessionExpiryInterval.intValue()));
378 }
379 if (receiveMaximum > 0) {
380 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.RECEIVE_MAXIMUM.value(), receiveMaximum));
381 }
382 if (maximumQos != null) {
383 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.MAXIMUM_QOS.value(), receiveMaximum));
384 }
385 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.RETAIN_AVAILABLE.value(), retain ? 1 : 0));
386 if (maximumPacketSize != null) {
387 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.MAXIMUM_PACKET_SIZE.value(),
388 maximumPacketSize.intValue()));
389 }
390 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.TOPIC_ALIAS_MAXIMUM.value(),
391 topicAliasMaximum));
392 if (reasonString != null) {
393 props.add(new MqttProperties.StringProperty(MqttPropertyType.REASON_STRING.value(), reasonString));
394 }
395 props.add(userProperties);
396 if (wildcardSubscriptionAvailable != null) {
397 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE.value(),
398 wildcardSubscriptionAvailable ? 1 : 0));
399 }
400 if (subscriptionIdentifiersAvailable != null) {
401 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE.value(),
402 subscriptionIdentifiersAvailable ? 1 : 0));
403 }
404 if (sharedSubscriptionAvailable != null) {
405 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SHARED_SUBSCRIPTION_AVAILABLE.value(),
406 sharedSubscriptionAvailable ? 1 : 0));
407 }
408 if (serverKeepAlive != null) {
409 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SERVER_KEEP_ALIVE.value(),
410 serverKeepAlive));
411 }
412 if (responseInformation != null) {
413 props.add(new MqttProperties.StringProperty(MqttPropertyType.RESPONSE_INFORMATION.value(),
414 responseInformation));
415 }
416 if (serverReference != null) {
417 props.add(new MqttProperties.StringProperty(MqttPropertyType.SERVER_REFERENCE.value(),
418 serverReference));
419 }
420 if (authenticationMethod != null) {
421 props.add(new MqttProperties.StringProperty(MqttPropertyType.AUTHENTICATION_METHOD.value(),
422 authenticationMethod));
423 }
424 if (authenticationData != null) {
425 props.add(new MqttProperties.BinaryProperty(MqttPropertyType.AUTHENTICATION_DATA.value(),
426 authenticationData));
427 }
428
429 return props;
430 }
431
432 public ConnAckPropertiesBuilder sessionExpiryInterval(long seconds) {
433 this.sessionExpiryInterval = seconds;
434 return this;
435 }
436
437 public ConnAckPropertiesBuilder receiveMaximum(int value) {
438 this.receiveMaximum = checkPositive(value, "value");
439 return this;
440 }
441
442 public ConnAckPropertiesBuilder maximumQos(byte value) {
443 if (value != 0 && value != 1) {
444 throw new IllegalArgumentException("maximum QoS property could be 0 or 1");
445 }
446 this.maximumQos = value;
447 return this;
448 }
449
450 public ConnAckPropertiesBuilder retainAvailable(boolean retain) {
451 this.retain = retain;
452 return this;
453 }
454
455 public ConnAckPropertiesBuilder maximumPacketSize(long size) {
456 this.maximumPacketSize = checkPositive(size, "size");
457 return this;
458 }
459
460 public ConnAckPropertiesBuilder assignedClientId(String clientId) {
461 this.clientId = clientId;
462 return this;
463 }
464
465 public ConnAckPropertiesBuilder topicAliasMaximum(int value) {
466 this.topicAliasMaximum = value;
467 return this;
468 }
469
470 public ConnAckPropertiesBuilder reasonString(String reason) {
471 this.reasonString = reason;
472 return this;
473 }
474
475 public ConnAckPropertiesBuilder userProperty(String name, String value) {
476 userProperties.add(name, value);
477 return this;
478 }
479
480 public ConnAckPropertiesBuilder wildcardSubscriptionAvailable(boolean value) {
481 this.wildcardSubscriptionAvailable = value;
482 return this;
483 }
484
485 public ConnAckPropertiesBuilder subscriptionIdentifiersAvailable(boolean value) {
486 this.subscriptionIdentifiersAvailable = value;
487 return this;
488 }
489
490 public ConnAckPropertiesBuilder sharedSubscriptionAvailable(boolean value) {
491 this.sharedSubscriptionAvailable = value;
492 return this;
493 }
494
495 public ConnAckPropertiesBuilder serverKeepAlive(int seconds) {
496 this.serverKeepAlive = seconds;
497 return this;
498 }
499
500 public ConnAckPropertiesBuilder responseInformation(String value) {
501 this.responseInformation = value;
502 return this;
503 }
504
505 public ConnAckPropertiesBuilder serverReference(String host) {
506 this.serverReference = host;
507 return this;
508 }
509
510 public ConnAckPropertiesBuilder authenticationMethod(String methodName) {
511 this.authenticationMethod = methodName;
512 return this;
513 }
514
515 public ConnAckPropertiesBuilder authenticationData(byte[] rawData) {
516 this.authenticationData = rawData.clone();
517 return this;
518 }
519 }
520
521 public static final class PubAckBuilder {
522
523 private int packetId;
524 private byte reasonCode;
525 private MqttProperties properties;
526
527 PubAckBuilder() {
528 }
529
530 public PubAckBuilder reasonCode(byte reasonCode) {
531 this.reasonCode = reasonCode;
532 return this;
533 }
534
535 public PubAckBuilder packetId(int packetId) {
536 this.packetId = packetId;
537 return this;
538 }
539
540
541
542
543 @Deprecated
544 public PubAckBuilder packetId(short packetId) {
545 return packetId(packetId & 0xFFFF);
546 }
547
548 public PubAckBuilder properties(MqttProperties properties) {
549 this.properties = properties;
550 return this;
551 }
552
553 public MqttMessage build() {
554 MqttFixedHeader mqttFixedHeader =
555 new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
556 MqttPubReplyMessageVariableHeader mqttPubAckVariableHeader =
557 new MqttPubReplyMessageVariableHeader(packetId, reasonCode, properties);
558 return new MqttMessage(mqttFixedHeader, mqttPubAckVariableHeader);
559 }
560 }
561
562 public static final class SubAckBuilder {
563
564 private int packetId;
565 private MqttProperties properties;
566 private final List<MqttQoS> grantedQoses = new ArrayList<MqttQoS>();
567
568 SubAckBuilder() {
569 }
570
571 public SubAckBuilder packetId(int packetId) {
572 this.packetId = packetId;
573 return this;
574 }
575
576
577
578
579 @Deprecated
580 public SubAckBuilder packetId(short packetId) {
581 return packetId(packetId & 0xFFFF);
582 }
583
584 public SubAckBuilder properties(MqttProperties properties) {
585 this.properties = properties;
586 return this;
587 }
588
589 public SubAckBuilder addGrantedQos(MqttQoS qos) {
590 this.grantedQoses.add(qos);
591 return this;
592 }
593
594 public SubAckBuilder addGrantedQoses(MqttQoS... qoses) {
595 this.grantedQoses.addAll(Arrays.asList(qoses));
596 return this;
597 }
598
599 public MqttSubAckMessage build() {
600 MqttFixedHeader mqttFixedHeader =
601 new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
602 MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader =
603 new MqttMessageIdAndPropertiesVariableHeader(packetId, properties);
604
605
606 int[] grantedQoses = new int[this.grantedQoses.size()];
607 int i = 0;
608 for (MqttQoS grantedQos : this.grantedQoses) {
609 grantedQoses[i++] = grantedQos.value();
610 }
611
612 MqttSubAckPayload subAckPayload = new MqttSubAckPayload(grantedQoses);
613 return new MqttSubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload);
614 }
615 }
616
617 public static final class UnsubAckBuilder {
618
619 private int packetId;
620 private MqttProperties properties;
621 private final List<Short> reasonCodes = new ArrayList<Short>();
622
623 UnsubAckBuilder() {
624 }
625
626 public UnsubAckBuilder packetId(int packetId) {
627 this.packetId = packetId;
628 return this;
629 }
630
631
632
633
634 @Deprecated
635 public UnsubAckBuilder packetId(short packetId) {
636 return packetId(packetId & 0xFFFF);
637 }
638
639 public UnsubAckBuilder properties(MqttProperties properties) {
640 this.properties = properties;
641 return this;
642 }
643
644 public UnsubAckBuilder addReasonCode(short reasonCode) {
645 this.reasonCodes.add(reasonCode);
646 return this;
647 }
648
649 public UnsubAckBuilder addReasonCodes(Short... reasonCodes) {
650 this.reasonCodes.addAll(Arrays.asList(reasonCodes));
651 return this;
652 }
653
654 public MqttUnsubAckMessage build() {
655 MqttFixedHeader mqttFixedHeader =
656 new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
657 MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader =
658 new MqttMessageIdAndPropertiesVariableHeader(packetId, properties);
659
660 MqttUnsubAckPayload subAckPayload = new MqttUnsubAckPayload(reasonCodes);
661 return new MqttUnsubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload);
662 }
663 }
664
665 public static final class DisconnectBuilder {
666
667 private MqttProperties properties;
668 private byte reasonCode;
669
670 DisconnectBuilder() {
671 }
672
673 public DisconnectBuilder properties(MqttProperties properties) {
674 this.properties = properties;
675 return this;
676 }
677
678 public DisconnectBuilder reasonCode(byte reasonCode) {
679 this.reasonCode = reasonCode;
680 return this;
681 }
682
683 public MqttMessage build() {
684 MqttFixedHeader mqttFixedHeader =
685 new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
686 MqttReasonCodeAndPropertiesVariableHeader mqttDisconnectVariableHeader =
687 new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
688
689 return new MqttMessage(mqttFixedHeader, mqttDisconnectVariableHeader);
690 }
691 }
692
693 public static final class AuthBuilder {
694
695 private MqttProperties properties;
696 private byte reasonCode;
697
698 AuthBuilder() {
699 }
700
701 public AuthBuilder properties(MqttProperties properties) {
702 this.properties = properties;
703 return this;
704 }
705
706 public AuthBuilder reasonCode(byte reasonCode) {
707 this.reasonCode = reasonCode;
708 return this;
709 }
710
711 public MqttMessage build() {
712 MqttFixedHeader mqttFixedHeader =
713 new MqttFixedHeader(MqttMessageType.AUTH, false, MqttQoS.AT_MOST_ONCE, false, 0);
714 MqttReasonCodeAndPropertiesVariableHeader mqttAuthVariableHeader =
715 new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
716
717 return new MqttMessage(mqttFixedHeader, mqttAuthVariableHeader);
718 }
719 }
720
721 public static ConnectBuilder connect() {
722 return new ConnectBuilder();
723 }
724
725 public static ConnAckBuilder connAck() {
726 return new ConnAckBuilder();
727 }
728
729 public static PublishBuilder publish() {
730 return new PublishBuilder();
731 }
732
733 public static SubscribeBuilder subscribe() {
734 return new SubscribeBuilder();
735 }
736
737 public static UnsubscribeBuilder unsubscribe() {
738 return new UnsubscribeBuilder();
739 }
740
741 public static PubAckBuilder pubAck() {
742 return new PubAckBuilder();
743 }
744
745 public static SubAckBuilder subAck() {
746 return new SubAckBuilder();
747 }
748
749 public static UnsubAckBuilder unsubAck() {
750 return new UnsubAckBuilder();
751 }
752
753 public static DisconnectBuilder disconnect() {
754 return new DisconnectBuilder();
755 }
756
757 public static AuthBuilder auth() {
758 return new AuthBuilder();
759 }
760
761 private MqttMessageBuilders() {
762 }
763 }