blob: 9e31f6777c1c3480f99daf6ac732fb9646d7982f [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smackx;
22
23import org.jivesoftware.smack.*;
24import org.jivesoftware.smack.filter.PacketFilter;
25import org.jivesoftware.smack.filter.PacketIDFilter;
26import org.jivesoftware.smack.filter.PacketTypeFilter;
27import org.jivesoftware.smack.packet.IQ;
28import org.jivesoftware.smack.packet.Packet;
29import org.jivesoftware.smack.packet.PacketExtension;
30import org.jivesoftware.smack.packet.XMPPError;
31import org.jivesoftware.smackx.entitycaps.EntityCapsManager;
32import org.jivesoftware.smackx.packet.DiscoverInfo;
33import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
34import org.jivesoftware.smackx.packet.DiscoverItems;
35import org.jivesoftware.smackx.packet.DataForm;
36
37import java.util.*;
38import java.util.concurrent.ConcurrentHashMap;
39
40/**
41 * Manages discovery of services in XMPP entities. This class provides:
42 * <ol>
43 * <li>A registry of supported features in this XMPP entity.
44 * <li>Automatic response when this XMPP entity is queried for information.
45 * <li>Ability to discover items and information of remote XMPP entities.
46 * <li>Ability to publish publicly available items.
47 * </ol>
48 *
49 * @author Gaston Dombiak
50 */
51public class ServiceDiscoveryManager {
52
53 private static final String DEFAULT_IDENTITY_NAME = "Smack";
54 private static final String DEFAULT_IDENTITY_CATEGORY = "client";
55 private static final String DEFAULT_IDENTITY_TYPE = "pc";
56
57 private static List<DiscoverInfo.Identity> identities = new LinkedList<DiscoverInfo.Identity>();
58
59 private EntityCapsManager capsManager;
60
61 private static Map<Connection, ServiceDiscoveryManager> instances =
62 new ConcurrentHashMap<Connection, ServiceDiscoveryManager>();
63
64 private Connection connection;
65 private final Set<String> features = new HashSet<String>();
66 private DataForm extendedInfo = null;
67 private Map<String, NodeInformationProvider> nodeInformationProviders =
68 new ConcurrentHashMap<String, NodeInformationProvider>();
69
70 // Create a new ServiceDiscoveryManager on every established connection
71 static {
72 Connection.addConnectionCreationListener(new ConnectionCreationListener() {
73 public void connectionCreated(Connection connection) {
74 new ServiceDiscoveryManager(connection);
75 }
76 });
77 identities.add(new Identity(DEFAULT_IDENTITY_CATEGORY, DEFAULT_IDENTITY_NAME, DEFAULT_IDENTITY_TYPE));
78 }
79
80 /**
81 * Creates a new ServiceDiscoveryManager for a given Connection. This means that the
82 * service manager will respond to any service discovery request that the connection may
83 * receive.
84 *
85 * @param connection the connection to which a ServiceDiscoveryManager is going to be created.
86 */
87 public ServiceDiscoveryManager(Connection connection) {
88 this.connection = connection;
89
90 init();
91 }
92
93 /**
94 * Returns the ServiceDiscoveryManager instance associated with a given Connection.
95 *
96 * @param connection the connection used to look for the proper ServiceDiscoveryManager.
97 * @return the ServiceDiscoveryManager associated with a given Connection.
98 */
99 public static ServiceDiscoveryManager getInstanceFor(Connection connection) {
100 return instances.get(connection);
101 }
102
103 /**
104 * Returns the name of the client that will be returned when asked for the client identity
105 * in a disco request. The name could be any value you need to identity this client.
106 *
107 * @return the name of the client that will be returned when asked for the client identity
108 * in a disco request.
109 */
110 public static String getIdentityName() {
111 DiscoverInfo.Identity identity = identities.get(0);
112 if (identity != null) {
113 return identity.getName();
114 } else {
115 return null;
116 }
117 }
118
119 /**
120 * Sets the name of the client that will be returned when asked for the client identity
121 * in a disco request. The name could be any value you need to identity this client.
122 *
123 * @param name the name of the client that will be returned when asked for the client identity
124 * in a disco request.
125 */
126 public static void setIdentityName(String name) {
127 DiscoverInfo.Identity identity = identities.remove(0);
128 identity = new DiscoverInfo.Identity(DEFAULT_IDENTITY_CATEGORY, name, DEFAULT_IDENTITY_TYPE);
129 identities.add(identity);
130 }
131
132 /**
133 * Returns the type of client that will be returned when asked for the client identity in a
134 * disco request. The valid types are defined by the category client. Follow this link to learn
135 * the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
136 *
137 * @return the type of client that will be returned when asked for the client identity in a
138 * disco request.
139 */
140 public static String getIdentityType() {
141 DiscoverInfo.Identity identity = identities.get(0);
142 if (identity != null) {
143 return identity.getType();
144 } else {
145 return null;
146 }
147 }
148
149 /**
150 * Sets the type of client that will be returned when asked for the client identity in a
151 * disco request. The valid types are defined by the category client. Follow this link to learn
152 * the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
153 *
154 * @param type the type of client that will be returned when asked for the client identity in a
155 * disco request.
156 */
157 public static void setIdentityType(String type) {
158 DiscoverInfo.Identity identity = identities.get(0);
159 if (identity != null) {
160 identity.setType(type);
161 } else {
162 identity = new DiscoverInfo.Identity(DEFAULT_IDENTITY_CATEGORY, DEFAULT_IDENTITY_NAME, type);
163 identities.add(identity);
164 }
165 }
166
167 /**
168 * Returns all identities of this client as unmodifiable Collection
169 *
170 * @return
171 */
172 public static List<DiscoverInfo.Identity> getIdentities() {
173 return Collections.unmodifiableList(identities);
174 }
175
176 /**
177 * Initializes the packet listeners of the connection that will answer to any
178 * service discovery request.
179 */
180 private void init() {
181 // Register the new instance and associate it with the connection
182 instances.put(connection, this);
183
184 addFeature(DiscoverInfo.NAMESPACE);
185 addFeature(DiscoverItems.NAMESPACE);
186
187 // Add a listener to the connection that removes the registered instance when
188 // the connection is closed
189 connection.addConnectionListener(new ConnectionListener() {
190 public void connectionClosed() {
191 // Unregister this instance since the connection has been closed
192 instances.remove(connection);
193 }
194
195 public void connectionClosedOnError(Exception e) {
196 // ignore
197 }
198
199 public void reconnectionFailed(Exception e) {
200 // ignore
201 }
202
203 public void reconnectingIn(int seconds) {
204 // ignore
205 }
206
207 public void reconnectionSuccessful() {
208 // ignore
209 }
210 });
211
212 // Listen for disco#items requests and answer with an empty result
213 PacketFilter packetFilter = new PacketTypeFilter(DiscoverItems.class);
214 PacketListener packetListener = new PacketListener() {
215 public void processPacket(Packet packet) {
216 DiscoverItems discoverItems = (DiscoverItems) packet;
217 // Send back the items defined in the client if the request is of type GET
218 if (discoverItems != null && discoverItems.getType() == IQ.Type.GET) {
219 DiscoverItems response = new DiscoverItems();
220 response.setType(IQ.Type.RESULT);
221 response.setTo(discoverItems.getFrom());
222 response.setPacketID(discoverItems.getPacketID());
223 response.setNode(discoverItems.getNode());
224
225 // Add the defined items related to the requested node. Look for
226 // the NodeInformationProvider associated with the requested node.
227 NodeInformationProvider nodeInformationProvider =
228 getNodeInformationProvider(discoverItems.getNode());
229 if (nodeInformationProvider != null) {
230 // Specified node was found, add node items
231 response.addItems(nodeInformationProvider.getNodeItems());
232 // Add packet extensions
233 response.addExtensions(nodeInformationProvider.getNodePacketExtensions());
234 } else if(discoverItems.getNode() != null) {
235 // Return <item-not-found/> error since client doesn't contain
236 // the specified node
237 response.setType(IQ.Type.ERROR);
238 response.setError(new XMPPError(XMPPError.Condition.item_not_found));
239 }
240 connection.sendPacket(response);
241 }
242 }
243 };
244 connection.addPacketListener(packetListener, packetFilter);
245
246 // Listen for disco#info requests and answer the client's supported features
247 // To add a new feature as supported use the #addFeature message
248 packetFilter = new PacketTypeFilter(DiscoverInfo.class);
249 packetListener = new PacketListener() {
250 public void processPacket(Packet packet) {
251 DiscoverInfo discoverInfo = (DiscoverInfo) packet;
252 // Answer the client's supported features if the request is of the GET type
253 if (discoverInfo != null && discoverInfo.getType() == IQ.Type.GET) {
254 DiscoverInfo response = new DiscoverInfo();
255 response.setType(IQ.Type.RESULT);
256 response.setTo(discoverInfo.getFrom());
257 response.setPacketID(discoverInfo.getPacketID());
258 response.setNode(discoverInfo.getNode());
259 // Add the client's identity and features only if "node" is null
260 // and if the request was not send to a node. If Entity Caps are
261 // enabled the client's identity and features are may also added
262 // if the right node is chosen
263 if (discoverInfo.getNode() == null) {
264 addDiscoverInfoTo(response);
265 }
266 else {
267 // Disco#info was sent to a node. Check if we have information of the
268 // specified node
269 NodeInformationProvider nodeInformationProvider =
270 getNodeInformationProvider(discoverInfo.getNode());
271 if (nodeInformationProvider != null) {
272 // Node was found. Add node features
273 response.addFeatures(nodeInformationProvider.getNodeFeatures());
274 // Add node identities
275 response.addIdentities(nodeInformationProvider.getNodeIdentities());
276 // Add packet extensions
277 response.addExtensions(nodeInformationProvider.getNodePacketExtensions());
278 }
279 else {
280 // Return <item-not-found/> error since specified node was not found
281 response.setType(IQ.Type.ERROR);
282 response.setError(new XMPPError(XMPPError.Condition.item_not_found));
283 }
284 }
285 connection.sendPacket(response);
286 }
287 }
288 };
289 connection.addPacketListener(packetListener, packetFilter);
290 }
291
292 /**
293 * Add discover info response data.
294 *
295 * @see <a href="http://xmpp.org/extensions/xep-0030.html#info-basic">XEP-30 Basic Protocol; Example 2</a>
296 *
297 * @param response the discover info response packet
298 */
299 public void addDiscoverInfoTo(DiscoverInfo response) {
300 // First add the identities of the connection
301 response.addIdentities(identities);
302
303 // Add the registered features to the response
304 synchronized (features) {
305 for (Iterator<String> it = getFeatures(); it.hasNext();) {
306 response.addFeature(it.next());
307 }
308 response.addExtension(extendedInfo);
309 }
310 }
311
312 /**
313 * Returns the NodeInformationProvider responsible for providing information
314 * (ie items) related to a given node or <tt>null</null> if none.<p>
315 *
316 * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
317 * NodeInformationProvider will provide information about the rooms where the user has joined.
318 *
319 * @param node the node that contains items associated with an entity not addressable as a JID.
320 * @return the NodeInformationProvider responsible for providing information related
321 * to a given node.
322 */
323 private NodeInformationProvider getNodeInformationProvider(String node) {
324 if (node == null) {
325 return null;
326 }
327 return nodeInformationProviders.get(node);
328 }
329
330 /**
331 * Sets the NodeInformationProvider responsible for providing information
332 * (ie items) related to a given node. Every time this client receives a disco request
333 * regarding the items of a given node, the provider associated to that node will be the
334 * responsible for providing the requested information.<p>
335 *
336 * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
337 * NodeInformationProvider will provide information about the rooms where the user has joined.
338 *
339 * @param node the node whose items will be provided by the NodeInformationProvider.
340 * @param listener the NodeInformationProvider responsible for providing items related
341 * to the node.
342 */
343 public void setNodeInformationProvider(String node, NodeInformationProvider listener) {
344 nodeInformationProviders.put(node, listener);
345 }
346
347 /**
348 * Removes the NodeInformationProvider responsible for providing information
349 * (ie items) related to a given node. This means that no more information will be
350 * available for the specified node.
351 *
352 * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
353 * NodeInformationProvider will provide information about the rooms where the user has joined.
354 *
355 * @param node the node to remove the associated NodeInformationProvider.
356 */
357 public void removeNodeInformationProvider(String node) {
358 nodeInformationProviders.remove(node);
359 }
360
361 /**
362 * Returns the supported features by this XMPP entity.
363 *
364 * @return an Iterator on the supported features by this XMPP entity.
365 */
366 public Iterator<String> getFeatures() {
367 synchronized (features) {
368 return Collections.unmodifiableList(new ArrayList<String>(features)).iterator();
369 }
370 }
371
372 /**
373 * Returns the supported features by this XMPP entity.
374 *
375 * @return a copy of the List on the supported features by this XMPP entity.
376 */
377 public List<String> getFeaturesList() {
378 synchronized (features) {
379 return new LinkedList<String>(features);
380 }
381 }
382
383 /**
384 * Registers that a new feature is supported by this XMPP entity. When this client is
385 * queried for its information the registered features will be answered.<p>
386 *
387 * Since no packet is actually sent to the server it is safe to perform this operation
388 * before logging to the server. In fact, you may want to configure the supported features
389 * before logging to the server so that the information is already available if it is required
390 * upon login.
391 *
392 * @param feature the feature to register as supported.
393 */
394 public void addFeature(String feature) {
395 synchronized (features) {
396 features.add(feature);
397 renewEntityCapsVersion();
398 }
399 }
400
401 /**
402 * Removes the specified feature from the supported features by this XMPP entity.<p>
403 *
404 * Since no packet is actually sent to the server it is safe to perform this operation
405 * before logging to the server.
406 *
407 * @param feature the feature to remove from the supported features.
408 */
409 public void removeFeature(String feature) {
410 synchronized (features) {
411 features.remove(feature);
412 renewEntityCapsVersion();
413 }
414 }
415
416 /**
417 * Returns true if the specified feature is registered in the ServiceDiscoveryManager.
418 *
419 * @param feature the feature to look for.
420 * @return a boolean indicating if the specified featured is registered or not.
421 */
422 public boolean includesFeature(String feature) {
423 synchronized (features) {
424 return features.contains(feature);
425 }
426 }
427
428 /**
429 * Registers extended discovery information of this XMPP entity. When this
430 * client is queried for its information this data form will be returned as
431 * specified by XEP-0128.
432 * <p>
433 *
434 * Since no packet is actually sent to the server it is safe to perform this
435 * operation before logging to the server. In fact, you may want to
436 * configure the extended info before logging to the server so that the
437 * information is already available if it is required upon login.
438 *
439 * @param info
440 * the data form that contains the extend service discovery
441 * information.
442 */
443 public void setExtendedInfo(DataForm info) {
444 extendedInfo = info;
445 renewEntityCapsVersion();
446 }
447
448 /**
449 * Returns the data form that is set as extended information for this Service Discovery instance (XEP-0128)
450 *
451 * @see <a href="http://xmpp.org/extensions/xep-0128.html">XEP-128: Service Discovery Extensions</a>
452 * @return
453 */
454 public DataForm getExtendedInfo() {
455 return extendedInfo;
456 }
457
458 /**
459 * Returns the data form as List of PacketExtensions, or null if no data form is set.
460 * This representation is needed by some classes (e.g. EntityCapsManager, NodeInformationProvider)
461 *
462 * @return
463 */
464 public List<PacketExtension> getExtendedInfoAsList() {
465 List<PacketExtension> res = null;
466 if (extendedInfo != null) {
467 res = new ArrayList<PacketExtension>(1);
468 res.add(extendedInfo);
469 }
470 return res;
471 }
472
473 /**
474 * Removes the data form containing extended service discovery information
475 * from the information returned by this XMPP entity.<p>
476 *
477 * Since no packet is actually sent to the server it is safe to perform this
478 * operation before logging to the server.
479 */
480 public void removeExtendedInfo() {
481 extendedInfo = null;
482 renewEntityCapsVersion();
483 }
484
485 /**
486 * Returns the discovered information of a given XMPP entity addressed by its JID.
487 * Use null as entityID to query the server
488 *
489 * @param entityID the address of the XMPP entity or null.
490 * @return the discovered information.
491 * @throws XMPPException if the operation failed for some reason.
492 */
493 public DiscoverInfo discoverInfo(String entityID) throws XMPPException {
494 if (entityID == null)
495 return discoverInfo(null, null);
496
497 // Check if the have it cached in the Entity Capabilities Manager
498 DiscoverInfo info = EntityCapsManager.getDiscoverInfoByUser(entityID);
499
500 if (info != null) {
501 // We were able to retrieve the information from Entity Caps and
502 // avoided a disco request, hurray!
503 return info;
504 }
505
506 // Try to get the newest node#version if it's known, otherwise null is
507 // returned
508 EntityCapsManager.NodeVerHash nvh = EntityCapsManager.getNodeVerHashByJid(entityID);
509
510 // Discover by requesting the information from the remote entity
511 // Note that wee need to use NodeVer as argument for Node if it exists
512 info = discoverInfo(entityID, nvh != null ? nvh.getNodeVer() : null);
513
514 // If the node version is known, store the new entry.
515 if (nvh != null) {
516 if (EntityCapsManager.verifyDiscoverInfoVersion(nvh.getVer(), nvh.getHash(), info))
517 EntityCapsManager.addDiscoverInfoByNode(nvh.getNodeVer(), info);
518 }
519
520 return info;
521 }
522
523 /**
524 * Returns the discovered information of a given XMPP entity addressed by its JID and
525 * note attribute. Use this message only when trying to query information which is not
526 * directly addressable.
527 *
528 * @see <a href="http://xmpp.org/extensions/xep-0030.html#info-basic">XEP-30 Basic Protocol</a>
529 * @see <a href="http://xmpp.org/extensions/xep-0030.html#info-nodes">XEP-30 Info Nodes</a>
530 *
531 * @param entityID the address of the XMPP entity.
532 * @param node the optional attribute that supplements the 'jid' attribute.
533 * @return the discovered information.
534 * @throws XMPPException if the operation failed for some reason.
535 */
536 public DiscoverInfo discoverInfo(String entityID, String node) throws XMPPException {
537 // Discover the entity's info
538 DiscoverInfo disco = new DiscoverInfo();
539 disco.setType(IQ.Type.GET);
540 disco.setTo(entityID);
541 disco.setNode(node);
542
543 // Create a packet collector to listen for a response.
544 PacketCollector collector =
545 connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
546
547 connection.sendPacket(disco);
548
549 // Wait up to 5 seconds for a result.
550 IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
551 // Stop queuing results
552 collector.cancel();
553 if (result == null) {
554 throw new XMPPException("No response from the server.");
555 }
556 if (result.getType() == IQ.Type.ERROR) {
557 throw new XMPPException(result.getError());
558 }
559 return (DiscoverInfo) result;
560 }
561
562 /**
563 * Returns the discovered items of a given XMPP entity addressed by its JID.
564 *
565 * @param entityID the address of the XMPP entity.
566 * @return the discovered information.
567 * @throws XMPPException if the operation failed for some reason.
568 */
569 public DiscoverItems discoverItems(String entityID) throws XMPPException {
570 return discoverItems(entityID, null);
571 }
572
573 /**
574 * Returns the discovered items of a given XMPP entity addressed by its JID and
575 * note attribute. Use this message only when trying to query information which is not
576 * directly addressable.
577 *
578 * @param entityID the address of the XMPP entity.
579 * @param node the optional attribute that supplements the 'jid' attribute.
580 * @return the discovered items.
581 * @throws XMPPException if the operation failed for some reason.
582 */
583 public DiscoverItems discoverItems(String entityID, String node) throws XMPPException {
584 // Discover the entity's items
585 DiscoverItems disco = new DiscoverItems();
586 disco.setType(IQ.Type.GET);
587 disco.setTo(entityID);
588 disco.setNode(node);
589
590 // Create a packet collector to listen for a response.
591 PacketCollector collector =
592 connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
593
594 connection.sendPacket(disco);
595
596 // Wait up to 5 seconds for a result.
597 IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
598 // Stop queuing results
599 collector.cancel();
600 if (result == null) {
601 throw new XMPPException("No response from the server.");
602 }
603 if (result.getType() == IQ.Type.ERROR) {
604 throw new XMPPException(result.getError());
605 }
606 return (DiscoverItems) result;
607 }
608
609 /**
610 * Returns true if the server supports publishing of items. A client may wish to publish items
611 * to the server so that the server can provide items associated to the client. These items will
612 * be returned by the server whenever the server receives a disco request targeted to the bare
613 * address of the client (i.e. user@host.com).
614 *
615 * @param entityID the address of the XMPP entity.
616 * @return true if the server supports publishing of items.
617 * @throws XMPPException if the operation failed for some reason.
618 */
619 public boolean canPublishItems(String entityID) throws XMPPException {
620 DiscoverInfo info = discoverInfo(entityID);
621 return canPublishItems(info);
622 }
623
624 /**
625 * Returns true if the server supports publishing of items. A client may wish to publish items
626 * to the server so that the server can provide items associated to the client. These items will
627 * be returned by the server whenever the server receives a disco request targeted to the bare
628 * address of the client (i.e. user@host.com).
629 *
630 * @param DiscoverInfo the discover info packet to check.
631 * @return true if the server supports publishing of items.
632 */
633 public static boolean canPublishItems(DiscoverInfo info) {
634 return info.containsFeature("http://jabber.org/protocol/disco#publish");
635 }
636
637 /**
638 * Publishes new items to a parent entity. The item elements to publish MUST have at least
639 * a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
640 * specifies the action being taken for that item. Possible action values are: "update" and
641 * "remove".
642 *
643 * @param entityID the address of the XMPP entity.
644 * @param discoverItems the DiscoveryItems to publish.
645 * @throws XMPPException if the operation failed for some reason.
646 */
647 public void publishItems(String entityID, DiscoverItems discoverItems)
648 throws XMPPException {
649 publishItems(entityID, null, discoverItems);
650 }
651
652 /**
653 * Publishes new items to a parent entity and node. The item elements to publish MUST have at
654 * least a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
655 * specifies the action being taken for that item. Possible action values are: "update" and
656 * "remove".
657 *
658 * @param entityID the address of the XMPP entity.
659 * @param node the attribute that supplements the 'jid' attribute.
660 * @param discoverItems the DiscoveryItems to publish.
661 * @throws XMPPException if the operation failed for some reason.
662 */
663 public void publishItems(String entityID, String node, DiscoverItems discoverItems)
664 throws XMPPException {
665 discoverItems.setType(IQ.Type.SET);
666 discoverItems.setTo(entityID);
667 discoverItems.setNode(node);
668
669 // Create a packet collector to listen for a response.
670 PacketCollector collector =
671 connection.createPacketCollector(new PacketIDFilter(discoverItems.getPacketID()));
672
673 connection.sendPacket(discoverItems);
674
675 // Wait up to 5 seconds for a result.
676 IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
677 // Stop queuing results
678 collector.cancel();
679 if (result == null) {
680 throw new XMPPException("No response from the server.");
681 }
682 if (result.getType() == IQ.Type.ERROR) {
683 throw new XMPPException(result.getError());
684 }
685 }
686
687 /**
688 * Entity Capabilities
689 */
690
691 /**
692 * Loads the ServiceDiscoveryManager with an EntityCapsManger
693 * that speeds up certain lookups
694 * @param manager
695 */
696 public void setEntityCapsManager(EntityCapsManager manager) {
697 capsManager = manager;
698 }
699
700 /**
701 * Updates the Entity Capabilities Verification String
702 * if EntityCaps is enabled
703 */
704 private void renewEntityCapsVersion() {
705 if (capsManager != null && capsManager.entityCapsEnabled())
706 capsManager.updateLocalEntityCaps();
707 }
708}