From 7b3331dda066a31cda5de4dddf945eebfec4294f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 25 Jul 2014 20:49:11 +0200 Subject: [PATCH] Add API to find services for a given feature It's a common pattern to look for all services hosted under the users service that provide a given feature. Let's provide an API for that and use that API in the methods that benefit from it. --- .../address/MultipleRecipientManager.java | 54 ++----------- .../smackx/disco/ServiceDiscoveryManager.java | 75 +++++++++++++++++++ .../smackx/muc/MultiUserChat.java | 12 +-- .../smackx/search/UserSearch.java | 18 +++-- .../smackx/search/UserSearchManager.java | 29 +------ 5 files changed, 95 insertions(+), 93 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java index 34b923a24..bac9c7c16 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java @@ -25,18 +25,13 @@ import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.util.Cache; import org.jivesoftware.smackx.address.packet.MultipleAddresses; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.packet.DiscoverItems; import org.jxmpp.util.XmppStringUtils; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; /** * A MultipleRecipientManager allows to send packets to multiple recipients by making use of @@ -47,14 +42,6 @@ import java.util.logging.Logger; */ public class MultipleRecipientManager { - private static final Logger LOGGER = Logger.getLogger(MultipleRecipientManager.class.getName()); - - /** - * Create a cache to hold the 100 most recently accessed elements for a period of - * 24 hours. - */ - private static Cache services = new Cache(100, 24 * 60 * 60 * 1000); - /** * Sends the specified packet to the list of specified recipients using the * specified connection. If the server has support for XEP-33 then only one @@ -295,43 +282,12 @@ public class MultipleRecipientManager { * @throws NotConnectedException */ private static String getMultipleRecipienServiceAddress(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException { - String serviceName = connection.getServiceName(); - String serviceAddress = (String) services.get(serviceName); - if (serviceAddress == null) { - ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); - // Send the disco packet to the server itself - DiscoverInfo info = sdm.discoverInfo(serviceName); - // Check if the server supports XEP-33 - if (info.containsFeature(MultipleAddresses.NAMESPACE)) { - serviceAddress = serviceName; - } - else { - // Get the disco items and send the disco packet to each server item - DiscoverItems items = sdm.discoverItems(serviceName); - for (DiscoverItems.Item item : items.getItems()) { - try { - info = sdm.discoverInfo(item.getEntityID(), item.getNode()); - } - catch (XMPPErrorException|NoResponseException e) { - // Don't throw this exceptions if one of the server's items fail - LOGGER.log(Level.WARNING, - "Exception while discovering info of " + item.getEntityID() - + " node: " + item.getNode(), e); - continue; - } - if (info.containsFeature(MultipleAddresses.NAMESPACE)) { - serviceAddress = serviceName; - break; - } - } - } - // Use the empty string to indicate that no service is known for this connection - serviceAddress = serviceAddress == null ? "" : serviceAddress; - // Cache the discovered information - services.put(serviceName, serviceAddress); + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + List services = sdm.findServices(MultipleAddresses.NAMESPACE, true, true); + if (services.size() > 0) { + return services.get(0); } - - return "".equals(serviceAddress) ? null : serviceAddress; + return null; } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index ca4e981b3..ca40f718d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -30,6 +30,7 @@ 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.smack.util.Cache; import org.jivesoftware.smackx.caps.EntityCapsManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverItems; @@ -45,6 +46,8 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Manages discovery of services in XMPP entities. This class provides: @@ -59,6 +62,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ServiceDiscoveryManager extends Manager { + private static final Logger LOGGER = Logger.getLogger(ServiceDiscoveryManager.class.getName()); private static final String DEFAULT_IDENTITY_NAME = "Smack"; private static final String DEFAULT_IDENTITY_CATEGORY = "client"; private static final String DEFAULT_IDENTITY_TYPE = "pc"; @@ -682,6 +686,77 @@ public class ServiceDiscoveryManager extends Manager { return result.containsFeature(feature); } + /** + * Create a cache to hold the 25 most recently lookup services for a given feature for a period + * of 24 hours. + */ + private Cache> services = new Cache>(25, + 24 * 60 * 60 * 1000); + + /** + * Find all services under the users service that provide a given feature. + * + * @param feature the feature to search for + * @param stopOnFirst if true, stop searching after the first service was found + * @param useCache if true, query a cache first to avoid network I/O + * @return a possible empty list of services providing the given feature + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + */ + public List findServices(String feature, boolean stopOnFirst, boolean useCache) + throws NoResponseException, XMPPErrorException, NotConnectedException { + List serviceAddresses = null; + String serviceName = connection().getServiceName(); + if (useCache) { + serviceAddresses = (List) services.get(feature); + if (serviceAddresses != null) { + return serviceAddresses; + } + } + serviceAddresses = new LinkedList(); + // Send the disco packet to the server itself + DiscoverInfo info = discoverInfo(serviceName); + // Check if the server supports XEP-33 + if (info.containsFeature(feature)) { + serviceAddresses.add(serviceName); + if (stopOnFirst) { + if (useCache) { + // Cache the discovered information + services.put(feature, serviceAddresses); + } + return serviceAddresses; + } + } + // Get the disco items and send the disco packet to each server item + DiscoverItems items = discoverItems(serviceName); + for (DiscoverItems.Item item : items.getItems()) { + try { + // TODO is it OK here in all cases to query without the node attribute? + // MultipleRecipientManager queried initially also with the node attribute, but this + // could be simply a fault instead of intentional. + info = discoverInfo(item.getEntityID()); + } + catch (XMPPErrorException | NoResponseException e) { + // Don't throw this exceptions if one of the server's items fail + LOGGER.log(Level.WARNING, "Exception while discovering info for feature " + feature + + " of " + item.getEntityID() + " node: " + item.getNode(), e); + continue; + } + if (info.containsFeature(feature)) { + serviceAddresses.add(item.getEntityID()); + if (stopOnFirst) { + break; + } + } + } + if (useCache) { + // Cache the discovered information + services.put(feature, serviceAddresses); + } + return serviceAddresses; + } + /** * Entity Capabilities */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java index 6ef8a6884..6a5e41f6a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -260,16 +260,8 @@ public class MultiUserChat { * @throws NotConnectedException */ public static Collection getServiceNames(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException { - final List answer = new ArrayList(); - ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection); - DiscoverItems items = discoManager.discoverItems(connection.getServiceName()); - for (DiscoverItems.Item item : items.getItems()) { - DiscoverInfo info = discoManager.discoverInfo(item.getEntityID()); - if (info.containsFeature(MUCInitialPresence.NAMESPACE)) { - answer.add(item.getEntityID()); - } - } - return answer; + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + return sdm.findServices(MUCInitialPresence.NAMESPACE, false, false); } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearch.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearch.java index 92a28d63d..395aad5a5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearch.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearch.java @@ -23,6 +23,7 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smackx.xdata.Form; import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.packet.DataForm; @@ -40,18 +41,23 @@ import org.xmlpull.v1.XmlPullParser; */ public class UserSearch extends IQ { + public static final String NAMESPACE = "jabber:iq:search"; + /** * Creates a new instance of UserSearch. */ public UserSearch() { } - public String getChildElementXML() { - StringBuilder buf = new StringBuilder(); - buf.append(""); - buf.append(getExtensionsXML()); - buf.append(""); - return buf.toString(); + @Override + public XmlStringBuilder getChildElementXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.halfOpenElement(IQ.QUERY_ELEMENT); + xml.xmlnsAttribute(NAMESPACE); + xml.rightAngelBracket(); + xml.append(getExtensionsXML()); + xml.closeElement(IQ.QUERY_ELEMENT); + return xml; } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearchManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearchManager.java index 170e9408a..26152abc0 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearchManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/search/UserSearchManager.java @@ -19,16 +19,11 @@ package org.jivesoftware.smackx.search; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; -import org.jivesoftware.smackx.disco.packet.DiscoverItems; import org.jivesoftware.smackx.xdata.Form; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * The UserSearchManager is a facade built upon Jabber Search Services (XEP-055) to allow for searching @@ -101,29 +96,7 @@ public class UserSearchManager { * @throws NotConnectedException */ public Collection getSearchServices() throws NoResponseException, XMPPErrorException, NotConnectedException { - final List searchServices = new ArrayList(); ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(con); - DiscoverItems items = discoManager.discoverItems(con.getServiceName()); - for (DiscoverItems.Item item : items.getItems()) { - try { - DiscoverInfo info; - try { - info = discoManager.discoverInfo(item.getEntityID()); - } - catch (XMPPException e) { - // Ignore Case - continue; - } - - if (info.containsFeature("jabber:iq:search")) { - searchServices.add(item.getEntityID()); - } - } - catch (Exception e) { - // No info found. - break; - } - } - return searchServices; + return discoManager.findServices(UserSearch.NAMESPACE, false, false); } }