/servers/jain-slee/resources/xmpp/library/src/main/java/org/jivesoftware/smackx/ServiceDiscoveryManager.java
Java | 509 lines | 212 code | 45 blank | 252 comment | 32 complexity | df8051bc3552c214dde810f7b07c35e4 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
- /*
- * JBoss, Home of Professional Open Source
- * Copyright 2011, Red Hat, Inc. and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
- package org.jivesoftware.smackx;
-
- import java.util.*;
-
- import org.jivesoftware.smack.*;
- import org.jivesoftware.smack.filter.*;
- import org.jivesoftware.smack.packet.*;
- import org.jivesoftware.smackx.packet.*;
-
- /**
- * 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 String identityName = "Smack";
- private static String identityCategory = "client";
- private static String identityType = "pc";
-
- private static Map instances = new Hashtable();
-
- private XMPPConnection connection;
- private List features = new ArrayList();
- private Map nodeInformationProviders = new Hashtable();
-
- // Create a new ServiceDiscoveryManager on every established connection
- static {
- XMPPConnection.addConnectionListener(new ConnectionEstablishedListener() {
- public void connectionEstablished(XMPPConnection connection) {
- new ServiceDiscoveryManager(connection);
- }
- });
- }
-
- /**
- * Creates a new ServiceDiscoveryManager for a given XMPPConnection. 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(XMPPConnection connection) {
- this.connection = connection;
- init();
- }
-
- /**
- * Returns the ServiceDiscoveryManager instance associated with a given XMPPConnection.
- *
- * @param connection the connection used to look for the proper ServiceDiscoveryManager.
- * @return the ServiceDiscoveryManager associated with a given XMPPConnection.
- */
- public static ServiceDiscoveryManager getInstanceFor(XMPPConnection connection) {
- return (ServiceDiscoveryManager) instances.get(connection);
- }
-
- /**
- * Returns the name of the connection that will be returned when asked for the connection identity
- * in a disco request. The name could be any value you need to identity this connection.
- *
- * @return the name of the connection that will be returned when asked for the connection identity
- * in a disco request.
- */
- public static String getIdentityName() {
- return identityName;
- }
-
- /**
- * 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) {
- identityName = name;
- }
-
- /**
- * Returns the type of connection that will be returned when asked for the connection identity in a
- * disco request.
- *
- * @return the type of connection that will be returned when asked for the connection identity in a
- * disco request.
- */
- public static String getIdentityType() {
- return identityType;
- }
-
- /**
- * Sets the type of connection that will be returned when asked for the connection identity in a
- * disco request.
- *
- * @param type the type of connection that will be returned when asked for the connection identity in a
- * disco request.
- */
- public static void setIdentityType(String type) {
- identityType = type;
- }
-
-
- /**
- * Returns the category of connection that will be returned when asked for the connection identity in a
- * disco request.
- *
- * @author Eduardo Martins
- * @return the category of connection that will be returned when asked for the connection identity in a
- * disco request.
- */
- public static String getIdentityCategory() {
- return identityCategory;
- }
-
- /**
- * Sets the category of connection that will be returned when asked for the connection identity in a
- * disco request.
- *
- * @author Eduardo Martins
- * @param category the category of connection that will be returned when asked for the connection identity in a
- * disco request.
- */
- public static void setIdentityCategory(String category) {
- identityCategory = category;
- }
-
- /**
- * 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);
- // 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) {
- // Unregister this instance since the connection has been closed
- instances.remove(connection);
- }
- });
-
- // 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());
-
- // Add the defined items related to the requested node. Look for
- // the NodeInformationProvider associated with the requested node.
- if (getNodeInformationProvider(discoverItems.getNode()) != null) {
- Iterator items =
- getNodeInformationProvider(discoverItems.getNode()).getNodeItems();
- while (items.hasNext()) {
- response.addItem((DiscoverItems.Item) items.next());
- }
- } else if(discoverItems.getNode() != null) {
- // Return an <item-not-found/> error since the client
- // doesn't contain the specified node
- response.setNode(discoverItems.getNode());
- response.setType(IQ.Type.ERROR);
- response.setError(new XMPPError(404, "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());
- // Add the client's identity and features only if "node" is null
- if (discoverInfo.getNode() == null) {
- // Set this client identity
- DiscoverInfo.Identity identity = new DiscoverInfo.Identity(getIdentityCategory(),
- getIdentityName());
- identity.setType(getIdentityType());
- response.addIdentity(identity);
- // Add the registered features to the response
- synchronized (features) {
- for (Iterator it = getFeatures(); it.hasNext();) {
- response.addFeature((String) it.next());
- }
- }
- }
- else {
- // Return an <item-not-found/> error since a client doesn't have nodes
- response.setNode(discoverInfo.getNode());
- response.setType(IQ.Type.ERROR);
- response.setError(new XMPPError(404, "item-not-found"));
- }
- connection.sendPacket(response);
- }
- }
- };
- connection.addPacketListener(packetListener, packetFilter);
- }
-
- /**
- * 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 (NodeInformationProvider) 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 getFeatures() {
- synchronized (features) {
- return Collections.unmodifiableList(new ArrayList(features)).iterator();
- }
- }
-
- /**
- * 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);
- }
- }
-
- /**
- * 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);
- }
- }
-
- /**
- * 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);
- }
- }
-
- /**
- * Returns the discovered information 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 DiscoverInfo discoverInfo(String entityID) throws XMPPException {
- return discoverInfo(entityID, null);
- }
-
- /**
- * 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.
- *
- * @param entityID the address of the XMPP entity.
- * @param node the 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 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 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());
- }
- }
- }