aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smackx/ServiceDiscoveryManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smackx/ServiceDiscoveryManager.java')
-rw-r--r--src/org/jivesoftware/smackx/ServiceDiscoveryManager.java708
1 files changed, 708 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smackx/ServiceDiscoveryManager.java b/src/org/jivesoftware/smackx/ServiceDiscoveryManager.java
new file mode 100644
index 0000000..9e31f67
--- /dev/null
+++ b/src/org/jivesoftware/smackx/ServiceDiscoveryManager.java
@@ -0,0 +1,708 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jivesoftware.smackx;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.PacketFilter;
+import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.filter.PacketTypeFilter;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smackx.entitycaps.EntityCapsManager;
+import org.jivesoftware.smackx.packet.DiscoverInfo;
+import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
+import org.jivesoftware.smackx.packet.DiscoverItems;
+import org.jivesoftware.smackx.packet.DataForm;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Manages discovery of services in XMPP entities. This class provides:
+ * <ol>
+ * <li>A registry of supported features in this XMPP entity.
+ * <li>Automatic response when this XMPP entity is queried for information.
+ * <li>Ability to discover items and information of remote XMPP entities.
+ * <li>Ability to publish publicly available items.
+ * </ol>
+ *
+ * @author Gaston Dombiak
+ */
+public class ServiceDiscoveryManager {
+
+ private static final String DEFAULT_IDENTITY_NAME = "Smack";
+ private static final String DEFAULT_IDENTITY_CATEGORY = "client";
+ private static final String DEFAULT_IDENTITY_TYPE = "pc";
+
+ private static List<DiscoverInfo.Identity> identities = new LinkedList<DiscoverInfo.Identity>();
+
+ private EntityCapsManager capsManager;
+
+ private static Map<Connection, ServiceDiscoveryManager> instances =
+ new ConcurrentHashMap<Connection, ServiceDiscoveryManager>();
+
+ private Connection connection;
+ private final Set<String> features = new HashSet<String>();
+ private DataForm extendedInfo = null;
+ private Map<String, NodeInformationProvider> nodeInformationProviders =
+ new ConcurrentHashMap<String, NodeInformationProvider>();
+
+ // Create a new ServiceDiscoveryManager on every established connection
+ static {
+ Connection.addConnectionCreationListener(new ConnectionCreationListener() {
+ public void connectionCreated(Connection connection) {
+ new ServiceDiscoveryManager(connection);
+ }
+ });
+ identities.add(new Identity(DEFAULT_IDENTITY_CATEGORY, DEFAULT_IDENTITY_NAME, DEFAULT_IDENTITY_TYPE));
+ }
+
+ /**
+ * Creates a new ServiceDiscoveryManager for a given Connection. This means that the
+ * service manager will respond to any service discovery request that the connection may
+ * receive.
+ *
+ * @param connection the connection to which a ServiceDiscoveryManager is going to be created.
+ */
+ public ServiceDiscoveryManager(Connection connection) {
+ this.connection = connection;
+
+ init();
+ }
+
+ /**
+ * Returns the ServiceDiscoveryManager instance associated with a given Connection.
+ *
+ * @param connection the connection used to look for the proper ServiceDiscoveryManager.
+ * @return the ServiceDiscoveryManager associated with a given Connection.
+ */
+ public static ServiceDiscoveryManager getInstanceFor(Connection connection) {
+ return instances.get(connection);
+ }
+
+ /**
+ * Returns the name of the client that will be returned when asked for the client identity
+ * in a disco request. The name could be any value you need to identity this client.
+ *
+ * @return the name of the client that will be returned when asked for the client identity
+ * in a disco request.
+ */
+ public static String getIdentityName() {
+ DiscoverInfo.Identity identity = identities.get(0);
+ if (identity != null) {
+ return identity.getName();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the name of the client that will be returned when asked for the client identity
+ * in a disco request. The name could be any value you need to identity this client.
+ *
+ * @param name the name of the client that will be returned when asked for the client identity
+ * in a disco request.
+ */
+ public static void setIdentityName(String name) {
+ DiscoverInfo.Identity identity = identities.remove(0);
+ identity = new DiscoverInfo.Identity(DEFAULT_IDENTITY_CATEGORY, name, DEFAULT_IDENTITY_TYPE);
+ identities.add(identity);
+ }
+
+ /**
+ * Returns the type of client that will be returned when asked for the client identity in a
+ * disco request. The valid types are defined by the category client. Follow this link to learn
+ * the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
+ *
+ * @return the type of client that will be returned when asked for the client identity in a
+ * disco request.
+ */
+ public static String getIdentityType() {
+ DiscoverInfo.Identity identity = identities.get(0);
+ if (identity != null) {
+ return identity.getType();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the type of client that will be returned when asked for the client identity in a
+ * disco request. The valid types are defined by the category client. Follow this link to learn
+ * the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
+ *
+ * @param type the type of client that will be returned when asked for the client identity in a
+ * disco request.
+ */
+ public static void setIdentityType(String type) {
+ DiscoverInfo.Identity identity = identities.get(0);
+ if (identity != null) {
+ identity.setType(type);
+ } else {
+ identity = new DiscoverInfo.Identity(DEFAULT_IDENTITY_CATEGORY, DEFAULT_IDENTITY_NAME, type);
+ identities.add(identity);
+ }
+ }
+
+ /**
+ * Returns all identities of this client as unmodifiable Collection
+ *
+ * @return
+ */
+ public static List<DiscoverInfo.Identity> getIdentities() {
+ return Collections.unmodifiableList(identities);
+ }
+
+ /**
+ * Initializes the packet listeners of the connection that will answer to any
+ * service discovery request.
+ */
+ private void init() {
+ // Register the new instance and associate it with the connection
+ instances.put(connection, this);
+
+ addFeature(DiscoverInfo.NAMESPACE);
+ addFeature(DiscoverItems.NAMESPACE);
+
+ // Add a listener to the connection that removes the registered instance when
+ // the connection is closed
+ connection.addConnectionListener(new ConnectionListener() {
+ public void connectionClosed() {
+ // Unregister this instance since the connection has been closed
+ instances.remove(connection);
+ }
+
+ public void connectionClosedOnError(Exception e) {
+ // ignore
+ }
+
+ public void reconnectionFailed(Exception e) {
+ // ignore
+ }
+
+ public void reconnectingIn(int seconds) {
+ // ignore
+ }
+
+ public void reconnectionSuccessful() {
+ // ignore
+ }
+ });
+
+ // Listen for disco#items requests and answer with an empty result
+ PacketFilter packetFilter = new PacketTypeFilter(DiscoverItems.class);
+ PacketListener packetListener = new PacketListener() {
+ public void processPacket(Packet packet) {
+ DiscoverItems discoverItems = (DiscoverItems) packet;
+ // Send back the items defined in the client if the request is of type GET
+ if (discoverItems != null && discoverItems.getType() == IQ.Type.GET) {
+ DiscoverItems response = new DiscoverItems();
+ response.setType(IQ.Type.RESULT);
+ response.setTo(discoverItems.getFrom());
+ response.setPacketID(discoverItems.getPacketID());
+ response.setNode(discoverItems.getNode());
+
+ // Add the defined items related to the requested node. Look for
+ // the NodeInformationProvider associated with the requested node.
+ NodeInformationProvider nodeInformationProvider =
+ getNodeInformationProvider(discoverItems.getNode());
+ if (nodeInformationProvider != null) {
+ // Specified node was found, add node items
+ response.addItems(nodeInformationProvider.getNodeItems());
+ // Add packet extensions
+ response.addExtensions(nodeInformationProvider.getNodePacketExtensions());
+ } else if(discoverItems.getNode() != null) {
+ // Return <item-not-found/> error since client doesn't contain
+ // the specified node
+ response.setType(IQ.Type.ERROR);
+ response.setError(new XMPPError(XMPPError.Condition.item_not_found));
+ }
+ connection.sendPacket(response);
+ }
+ }
+ };
+ connection.addPacketListener(packetListener, packetFilter);
+
+ // Listen for disco#info requests and answer the client's supported features
+ // To add a new feature as supported use the #addFeature message
+ packetFilter = new PacketTypeFilter(DiscoverInfo.class);
+ packetListener = new PacketListener() {
+ public void processPacket(Packet packet) {
+ DiscoverInfo discoverInfo = (DiscoverInfo) packet;
+ // Answer the client's supported features if the request is of the GET type
+ if (discoverInfo != null && discoverInfo.getType() == IQ.Type.GET) {
+ DiscoverInfo response = new DiscoverInfo();
+ response.setType(IQ.Type.RESULT);
+ response.setTo(discoverInfo.getFrom());
+ response.setPacketID(discoverInfo.getPacketID());
+ response.setNode(discoverInfo.getNode());
+ // Add the client's identity and features only if "node" is null
+ // and if the request was not send to a node. If Entity Caps are
+ // enabled the client's identity and features are may also added
+ // if the right node is chosen
+ if (discoverInfo.getNode() == null) {
+ addDiscoverInfoTo(response);
+ }
+ else {
+ // Disco#info was sent to a node. Check if we have information of the
+ // specified node
+ NodeInformationProvider nodeInformationProvider =
+ getNodeInformationProvider(discoverInfo.getNode());
+ if (nodeInformationProvider != null) {
+ // Node was found. Add node features
+ response.addFeatures(nodeInformationProvider.getNodeFeatures());
+ // Add node identities
+ response.addIdentities(nodeInformationProvider.getNodeIdentities());
+ // Add packet extensions
+ response.addExtensions(nodeInformationProvider.getNodePacketExtensions());
+ }
+ else {
+ // Return <item-not-found/> error since specified node was not found
+ response.setType(IQ.Type.ERROR);
+ response.setError(new XMPPError(XMPPError.Condition.item_not_found));
+ }
+ }
+ connection.sendPacket(response);
+ }
+ }
+ };
+ connection.addPacketListener(packetListener, packetFilter);
+ }
+
+ /**
+ * Add discover info response data.
+ *
+ * @see <a href="http://xmpp.org/extensions/xep-0030.html#info-basic">XEP-30 Basic Protocol; Example 2</a>
+ *
+ * @param response the discover info response packet
+ */
+ public void addDiscoverInfoTo(DiscoverInfo response) {
+ // First add the identities of the connection
+ response.addIdentities(identities);
+
+ // Add the registered features to the response
+ synchronized (features) {
+ for (Iterator<String> it = getFeatures(); it.hasNext();) {
+ response.addFeature(it.next());
+ }
+ response.addExtension(extendedInfo);
+ }
+ }
+
+ /**
+ * Returns the NodeInformationProvider responsible for providing information
+ * (ie items) related to a given node or <tt>null</null> if none.<p>
+ *
+ * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
+ * NodeInformationProvider will provide information about the rooms where the user has joined.
+ *
+ * @param node the node that contains items associated with an entity not addressable as a JID.
+ * @return the NodeInformationProvider responsible for providing information related
+ * to a given node.
+ */
+ private NodeInformationProvider getNodeInformationProvider(String node) {
+ if (node == null) {
+ return null;
+ }
+ return nodeInformationProviders.get(node);
+ }
+
+ /**
+ * Sets the NodeInformationProvider responsible for providing information
+ * (ie items) related to a given node. Every time this client receives a disco request
+ * regarding the items of a given node, the provider associated to that node will be the
+ * responsible for providing the requested information.<p>
+ *
+ * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
+ * NodeInformationProvider will provide information about the rooms where the user has joined.
+ *
+ * @param node the node whose items will be provided by the NodeInformationProvider.
+ * @param listener the NodeInformationProvider responsible for providing items related
+ * to the node.
+ */
+ public void setNodeInformationProvider(String node, NodeInformationProvider listener) {
+ nodeInformationProviders.put(node, listener);
+ }
+
+ /**
+ * Removes the NodeInformationProvider responsible for providing information
+ * (ie items) related to a given node. This means that no more information will be
+ * available for the specified node.
+ *
+ * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
+ * NodeInformationProvider will provide information about the rooms where the user has joined.
+ *
+ * @param node the node to remove the associated NodeInformationProvider.
+ */
+ public void removeNodeInformationProvider(String node) {
+ nodeInformationProviders.remove(node);
+ }
+
+ /**
+ * Returns the supported features by this XMPP entity.
+ *
+ * @return an Iterator on the supported features by this XMPP entity.
+ */
+ public Iterator<String> getFeatures() {
+ synchronized (features) {
+ return Collections.unmodifiableList(new ArrayList<String>(features)).iterator();
+ }
+ }
+
+ /**
+ * Returns the supported features by this XMPP entity.
+ *
+ * @return a copy of the List on the supported features by this XMPP entity.
+ */
+ public List<String> getFeaturesList() {
+ synchronized (features) {
+ return new LinkedList<String>(features);
+ }
+ }
+
+ /**
+ * Registers that a new feature is supported by this XMPP entity. When this client is
+ * queried for its information the registered features will be answered.<p>
+ *
+ * Since no packet is actually sent to the server it is safe to perform this operation
+ * before logging to the server. In fact, you may want to configure the supported features
+ * before logging to the server so that the information is already available if it is required
+ * upon login.
+ *
+ * @param feature the feature to register as supported.
+ */
+ public void addFeature(String feature) {
+ synchronized (features) {
+ features.add(feature);
+ renewEntityCapsVersion();
+ }
+ }
+
+ /**
+ * Removes the specified feature from the supported features by this XMPP entity.<p>
+ *
+ * Since no packet is actually sent to the server it is safe to perform this operation
+ * before logging to the server.
+ *
+ * @param feature the feature to remove from the supported features.
+ */
+ public void removeFeature(String feature) {
+ synchronized (features) {
+ features.remove(feature);
+ renewEntityCapsVersion();
+ }
+ }
+
+ /**
+ * Returns true if the specified feature is registered in the ServiceDiscoveryManager.
+ *
+ * @param feature the feature to look for.
+ * @return a boolean indicating if the specified featured is registered or not.
+ */
+ public boolean includesFeature(String feature) {
+ synchronized (features) {
+ return features.contains(feature);
+ }
+ }
+
+ /**
+ * Registers extended discovery information of this XMPP entity. When this
+ * client is queried for its information this data form will be returned as
+ * specified by XEP-0128.
+ * <p>
+ *
+ * Since no packet is actually sent to the server it is safe to perform this
+ * operation before logging to the server. In fact, you may want to
+ * configure the extended info before logging to the server so that the
+ * information is already available if it is required upon login.
+ *
+ * @param info
+ * the data form that contains the extend service discovery
+ * information.
+ */
+ public void setExtendedInfo(DataForm info) {
+ extendedInfo = info;
+ renewEntityCapsVersion();
+ }
+
+ /**
+ * Returns the data form that is set as extended information for this Service Discovery instance (XEP-0128)
+ *
+ * @see <a href="http://xmpp.org/extensions/xep-0128.html">XEP-128: Service Discovery Extensions</a>
+ * @return
+ */
+ public DataForm getExtendedInfo() {
+ return extendedInfo;
+ }
+
+ /**
+ * Returns the data form as List of PacketExtensions, or null if no data form is set.
+ * This representation is needed by some classes (e.g. EntityCapsManager, NodeInformationProvider)
+ *
+ * @return
+ */
+ public List<PacketExtension> getExtendedInfoAsList() {
+ List<PacketExtension> res = null;
+ if (extendedInfo != null) {
+ res = new ArrayList<PacketExtension>(1);
+ res.add(extendedInfo);
+ }
+ return res;
+ }
+
+ /**
+ * Removes the data form containing extended service discovery information
+ * from the information returned by this XMPP entity.<p>
+ *
+ * Since no packet is actually sent to the server it is safe to perform this
+ * operation before logging to the server.
+ */
+ public void removeExtendedInfo() {
+ extendedInfo = null;
+ renewEntityCapsVersion();
+ }
+
+ /**
+ * Returns the discovered information of a given XMPP entity addressed by its JID.
+ * Use null as entityID to query the server
+ *
+ * @param entityID the address of the XMPP entity or null.
+ * @return the discovered information.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public DiscoverInfo discoverInfo(String entityID) throws XMPPException {
+ if (entityID == null)
+ return discoverInfo(null, null);
+
+ // Check if the have it cached in the Entity Capabilities Manager
+ DiscoverInfo info = EntityCapsManager.getDiscoverInfoByUser(entityID);
+
+ if (info != null) {
+ // We were able to retrieve the information from Entity Caps and
+ // avoided a disco request, hurray!
+ return info;
+ }
+
+ // Try to get the newest node#version if it's known, otherwise null is
+ // returned
+ EntityCapsManager.NodeVerHash nvh = EntityCapsManager.getNodeVerHashByJid(entityID);
+
+ // Discover by requesting the information from the remote entity
+ // Note that wee need to use NodeVer as argument for Node if it exists
+ info = discoverInfo(entityID, nvh != null ? nvh.getNodeVer() : null);
+
+ // If the node version is known, store the new entry.
+ if (nvh != null) {
+ if (EntityCapsManager.verifyDiscoverInfoVersion(nvh.getVer(), nvh.getHash(), info))
+ EntityCapsManager.addDiscoverInfoByNode(nvh.getNodeVer(), info);
+ }
+
+ return info;
+ }
+
+ /**
+ * Returns the discovered information of a given XMPP entity addressed by its JID and
+ * note attribute. Use this message only when trying to query information which is not
+ * directly addressable.
+ *
+ * @see <a href="http://xmpp.org/extensions/xep-0030.html#info-basic">XEP-30 Basic Protocol</a>
+ * @see <a href="http://xmpp.org/extensions/xep-0030.html#info-nodes">XEP-30 Info Nodes</a>
+ *
+ * @param entityID the address of the XMPP entity.
+ * @param node the optional attribute that supplements the 'jid' attribute.
+ * @return the discovered information.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public DiscoverInfo discoverInfo(String entityID, String node) throws XMPPException {
+ // Discover the entity's info
+ DiscoverInfo disco = new DiscoverInfo();
+ disco.setType(IQ.Type.GET);
+ disco.setTo(entityID);
+ disco.setNode(node);
+
+ // Create a packet collector to listen for a response.
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
+
+ connection.sendPacket(disco);
+
+ // Wait up to 5 seconds for a result.
+ IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+ // Stop queuing results
+ collector.cancel();
+ if (result == null) {
+ throw new XMPPException("No response from the server.");
+ }
+ if (result.getType() == IQ.Type.ERROR) {
+ throw new XMPPException(result.getError());
+ }
+ return (DiscoverInfo) result;
+ }
+
+ /**
+ * Returns the discovered items of a given XMPP entity addressed by its JID.
+ *
+ * @param entityID the address of the XMPP entity.
+ * @return the discovered information.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public DiscoverItems discoverItems(String entityID) throws XMPPException {
+ return discoverItems(entityID, null);
+ }
+
+ /**
+ * Returns the discovered items of a given XMPP entity addressed by its JID and
+ * note attribute. Use this message only when trying to query information which is not
+ * directly addressable.
+ *
+ * @param entityID the address of the XMPP entity.
+ * @param node the optional attribute that supplements the 'jid' attribute.
+ * @return the discovered items.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public DiscoverItems discoverItems(String entityID, String node) throws XMPPException {
+ // Discover the entity's items
+ DiscoverItems disco = new DiscoverItems();
+ disco.setType(IQ.Type.GET);
+ disco.setTo(entityID);
+ disco.setNode(node);
+
+ // Create a packet collector to listen for a response.
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
+
+ connection.sendPacket(disco);
+
+ // Wait up to 5 seconds for a result.
+ IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+ // Stop queuing results
+ collector.cancel();
+ if (result == null) {
+ throw new XMPPException("No response from the server.");
+ }
+ if (result.getType() == IQ.Type.ERROR) {
+ throw new XMPPException(result.getError());
+ }
+ return (DiscoverItems) result;
+ }
+
+ /**
+ * Returns true if the server supports publishing of items. A client may wish to publish items
+ * to the server so that the server can provide items associated to the client. These items will
+ * be returned by the server whenever the server receives a disco request targeted to the bare
+ * address of the client (i.e. user@host.com).
+ *
+ * @param entityID the address of the XMPP entity.
+ * @return true if the server supports publishing of items.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public boolean canPublishItems(String entityID) throws XMPPException {
+ DiscoverInfo info = discoverInfo(entityID);
+ return canPublishItems(info);
+ }
+
+ /**
+ * Returns true if the server supports publishing of items. A client may wish to publish items
+ * to the server so that the server can provide items associated to the client. These items will
+ * be returned by the server whenever the server receives a disco request targeted to the bare
+ * address of the client (i.e. user@host.com).
+ *
+ * @param DiscoverInfo the discover info packet to check.
+ * @return true if the server supports publishing of items.
+ */
+ public static boolean canPublishItems(DiscoverInfo info) {
+ return info.containsFeature("http://jabber.org/protocol/disco#publish");
+ }
+
+ /**
+ * Publishes new items to a parent entity. The item elements to publish MUST have at least
+ * a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
+ * specifies the action being taken for that item. Possible action values are: "update" and
+ * "remove".
+ *
+ * @param entityID the address of the XMPP entity.
+ * @param discoverItems the DiscoveryItems to publish.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public void publishItems(String entityID, DiscoverItems discoverItems)
+ throws XMPPException {
+ publishItems(entityID, null, discoverItems);
+ }
+
+ /**
+ * Publishes new items to a parent entity and node. The item elements to publish MUST have at
+ * least a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
+ * specifies the action being taken for that item. Possible action values are: "update" and
+ * "remove".
+ *
+ * @param entityID the address of the XMPP entity.
+ * @param node the attribute that supplements the 'jid' attribute.
+ * @param discoverItems the DiscoveryItems to publish.
+ * @throws XMPPException if the operation failed for some reason.
+ */
+ public void publishItems(String entityID, String node, DiscoverItems discoverItems)
+ throws XMPPException {
+ discoverItems.setType(IQ.Type.SET);
+ discoverItems.setTo(entityID);
+ discoverItems.setNode(node);
+
+ // Create a packet collector to listen for a response.
+ PacketCollector collector =
+ connection.createPacketCollector(new PacketIDFilter(discoverItems.getPacketID()));
+
+ connection.sendPacket(discoverItems);
+
+ // Wait up to 5 seconds for a result.
+ IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+ // Stop queuing results
+ collector.cancel();
+ if (result == null) {
+ throw new XMPPException("No response from the server.");
+ }
+ if (result.getType() == IQ.Type.ERROR) {
+ throw new XMPPException(result.getError());
+ }
+ }
+
+ /**
+ * Entity Capabilities
+ */
+
+ /**
+ * Loads the ServiceDiscoveryManager with an EntityCapsManger
+ * that speeds up certain lookups
+ * @param manager
+ */
+ public void setEntityCapsManager(EntityCapsManager manager) {
+ capsManager = manager;
+ }
+
+ /**
+ * Updates the Entity Capabilities Verification String
+ * if EntityCaps is enabled
+ */
+ private void renewEntityCapsVersion() {
+ if (capsManager != null && capsManager.entityCapsEnabled())
+ capsManager.updateLocalEntityCaps();
+ }
+}