mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 22:32:06 +01:00
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.
This commit is contained in:
parent
6b7fa4a788
commit
7b3331dda0
5 changed files with 95 additions and 93 deletions
|
@ -25,18 +25,13 @@ import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Packet;
|
import org.jivesoftware.smack.packet.Packet;
|
||||||
import org.jivesoftware.smack.util.Cache;
|
|
||||||
import org.jivesoftware.smackx.address.packet.MultipleAddresses;
|
import org.jivesoftware.smackx.address.packet.MultipleAddresses;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
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 org.jxmpp.util.XmppStringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
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
|
* 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 {
|
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<String, String> services = new Cache<String, String>(100, 24 * 60 * 60 * 1000);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the specified packet to the list of specified recipients using the
|
* 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
|
* specified connection. If the server has support for XEP-33 then only one
|
||||||
|
@ -295,43 +282,12 @@ public class MultipleRecipientManager {
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
*/
|
*/
|
||||||
private static String getMultipleRecipienServiceAddress(XMPPConnection connection) throws NoResponseException, XMPPErrorException, 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);
|
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
// Send the disco packet to the server itself
|
List<String> services = sdm.findServices(MultipleAddresses.NAMESPACE, true, true);
|
||||||
DiscoverInfo info = sdm.discoverInfo(serviceName);
|
if (services.size() > 0) {
|
||||||
// Check if the server supports XEP-33
|
return services.get(0);
|
||||||
if (info.containsFeature(MultipleAddresses.NAMESPACE)) {
|
|
||||||
serviceAddress = serviceName;
|
|
||||||
}
|
}
|
||||||
else {
|
return null;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "".equals(serviceAddress) ? null : serviceAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Packet;
|
import org.jivesoftware.smack.packet.Packet;
|
||||||
import org.jivesoftware.smack.packet.PacketExtension;
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.util.Cache;
|
||||||
import org.jivesoftware.smackx.caps.EntityCapsManager;
|
import org.jivesoftware.smackx.caps.EntityCapsManager;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
|
@ -45,6 +46,8 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
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:
|
* Manages discovery of services in XMPP entities. This class provides:
|
||||||
|
@ -59,6 +62,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
*/
|
*/
|
||||||
public class ServiceDiscoveryManager extends Manager {
|
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_NAME = "Smack";
|
||||||
private static final String DEFAULT_IDENTITY_CATEGORY = "client";
|
private static final String DEFAULT_IDENTITY_CATEGORY = "client";
|
||||||
private static final String DEFAULT_IDENTITY_TYPE = "pc";
|
private static final String DEFAULT_IDENTITY_TYPE = "pc";
|
||||||
|
@ -682,6 +686,77 @@ public class ServiceDiscoveryManager extends Manager {
|
||||||
return result.containsFeature(feature);
|
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<String, List<String>> services = new Cache<String, List<String>>(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<String> findServices(String feature, boolean stopOnFirst, boolean useCache)
|
||||||
|
throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||||
|
List<String> serviceAddresses = null;
|
||||||
|
String serviceName = connection().getServiceName();
|
||||||
|
if (useCache) {
|
||||||
|
serviceAddresses = (List<String>) services.get(feature);
|
||||||
|
if (serviceAddresses != null) {
|
||||||
|
return serviceAddresses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serviceAddresses = new LinkedList<String>();
|
||||||
|
// 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
|
* Entity Capabilities
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -260,16 +260,8 @@ public class MultiUserChat {
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
*/
|
*/
|
||||||
public static Collection<String> getServiceNames(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException {
|
public static Collection<String> getServiceNames(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||||
final List<String> answer = new ArrayList<String>();
|
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
return sdm.findServices(MUCInitialPresence.NAMESPACE, false, false);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.provider.IQProvider;
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.Form;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
@ -40,18 +41,23 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
*/
|
*/
|
||||||
public class UserSearch extends IQ {
|
public class UserSearch extends IQ {
|
||||||
|
|
||||||
|
public static final String NAMESPACE = "jabber:iq:search";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of UserSearch.
|
* Creates a new instance of UserSearch.
|
||||||
*/
|
*/
|
||||||
public UserSearch() {
|
public UserSearch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getChildElementXML() {
|
@Override
|
||||||
StringBuilder buf = new StringBuilder();
|
public XmlStringBuilder getChildElementXML() {
|
||||||
buf.append("<query xmlns=\"jabber:iq:search\">");
|
XmlStringBuilder xml = new XmlStringBuilder();
|
||||||
buf.append(getExtensionsXML());
|
xml.halfOpenElement(IQ.QUERY_ELEMENT);
|
||||||
buf.append("</query>");
|
xml.xmlnsAttribute(NAMESPACE);
|
||||||
return buf.toString();
|
xml.rightAngelBracket();
|
||||||
|
xml.append(getExtensionsXML());
|
||||||
|
xml.closeElement(IQ.QUERY_ELEMENT);
|
||||||
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,16 +19,11 @@ package org.jivesoftware.smackx.search;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
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 org.jivesoftware.smackx.xdata.Form;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The UserSearchManager is a facade built upon Jabber Search Services (XEP-055) to allow for searching
|
* 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
|
* @throws NotConnectedException
|
||||||
*/
|
*/
|
||||||
public Collection<String> getSearchServices() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
public Collection<String> getSearchServices() throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||||
final List<String> searchServices = new ArrayList<String>();
|
|
||||||
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(con);
|
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(con);
|
||||||
DiscoverItems items = discoManager.discoverItems(con.getServiceName());
|
return discoManager.findServices(UserSearch.NAMESPACE, false, false);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue