PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/jain-slee/resources/xmpp/library/src/main/java/org/jivesoftware/smackx/ServiceDiscoveryManager.java

http://mobicents.googlecode.com/
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
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jivesoftware.smackx;
  23. import java.util.*;
  24. import org.jivesoftware.smack.*;
  25. import org.jivesoftware.smack.filter.*;
  26. import org.jivesoftware.smack.packet.*;
  27. import org.jivesoftware.smackx.packet.*;
  28. /**
  29. * Manages discovery of services in XMPP entities. This class provides:
  30. * <ol>
  31. * <li>A registry of supported features in this XMPP entity.
  32. * <li>Automatic response when this XMPP entity is queried for information.
  33. * <li>Ability to discover items and information of remote XMPP entities.
  34. * <li>Ability to publish publicly available items.
  35. * </ol>
  36. *
  37. * @author Gaston Dombiak
  38. */
  39. public class ServiceDiscoveryManager {
  40. private static String identityName = "Smack";
  41. private static String identityCategory = "client";
  42. private static String identityType = "pc";
  43. private static Map instances = new Hashtable();
  44. private XMPPConnection connection;
  45. private List features = new ArrayList();
  46. private Map nodeInformationProviders = new Hashtable();
  47. // Create a new ServiceDiscoveryManager on every established connection
  48. static {
  49. XMPPConnection.addConnectionListener(new ConnectionEstablishedListener() {
  50. public void connectionEstablished(XMPPConnection connection) {
  51. new ServiceDiscoveryManager(connection);
  52. }
  53. });
  54. }
  55. /**
  56. * Creates a new ServiceDiscoveryManager for a given XMPPConnection. This means that the
  57. * service manager will respond to any service discovery request that the connection may
  58. * receive.
  59. *
  60. * @param connection the connection to which a ServiceDiscoveryManager is going to be created.
  61. */
  62. public ServiceDiscoveryManager(XMPPConnection connection) {
  63. this.connection = connection;
  64. init();
  65. }
  66. /**
  67. * Returns the ServiceDiscoveryManager instance associated with a given XMPPConnection.
  68. *
  69. * @param connection the connection used to look for the proper ServiceDiscoveryManager.
  70. * @return the ServiceDiscoveryManager associated with a given XMPPConnection.
  71. */
  72. public static ServiceDiscoveryManager getInstanceFor(XMPPConnection connection) {
  73. return (ServiceDiscoveryManager) instances.get(connection);
  74. }
  75. /**
  76. * Returns the name of the connection that will be returned when asked for the connection identity
  77. * in a disco request. The name could be any value you need to identity this connection.
  78. *
  79. * @return the name of the connection that will be returned when asked for the connection identity
  80. * in a disco request.
  81. */
  82. public static String getIdentityName() {
  83. return identityName;
  84. }
  85. /**
  86. * Sets the name of the client that will be returned when asked for the client identity
  87. * in a disco request. The name could be any value you need to identity this client.
  88. *
  89. * @param name the name of the client that will be returned when asked for the client identity
  90. * in a disco request.
  91. */
  92. public static void setIdentityName(String name) {
  93. identityName = name;
  94. }
  95. /**
  96. * Returns the type of connection that will be returned when asked for the connection identity in a
  97. * disco request.
  98. *
  99. * @return the type of connection that will be returned when asked for the connection identity in a
  100. * disco request.
  101. */
  102. public static String getIdentityType() {
  103. return identityType;
  104. }
  105. /**
  106. * Sets the type of connection that will be returned when asked for the connection identity in a
  107. * disco request.
  108. *
  109. * @param type the type of connection that will be returned when asked for the connection identity in a
  110. * disco request.
  111. */
  112. public static void setIdentityType(String type) {
  113. identityType = type;
  114. }
  115. /**
  116. * Returns the category of connection that will be returned when asked for the connection identity in a
  117. * disco request.
  118. *
  119. * @author Eduardo Martins
  120. * @return the category of connection that will be returned when asked for the connection identity in a
  121. * disco request.
  122. */
  123. public static String getIdentityCategory() {
  124. return identityCategory;
  125. }
  126. /**
  127. * Sets the category of connection that will be returned when asked for the connection identity in a
  128. * disco request.
  129. *
  130. * @author Eduardo Martins
  131. * @param category the category of connection that will be returned when asked for the connection identity in a
  132. * disco request.
  133. */
  134. public static void setIdentityCategory(String category) {
  135. identityCategory = category;
  136. }
  137. /**
  138. * Initializes the packet listeners of the connection that will answer to any
  139. * service discovery request.
  140. */
  141. private void init() {
  142. // Register the new instance and associate it with the connection
  143. instances.put(connection, this);
  144. // Add a listener to the connection that removes the registered instance when
  145. // the connection is closed
  146. connection.addConnectionListener(new ConnectionListener() {
  147. public void connectionClosed() {
  148. // Unregister this instance since the connection has been closed
  149. instances.remove(connection);
  150. }
  151. public void connectionClosedOnError(Exception e) {
  152. // Unregister this instance since the connection has been closed
  153. instances.remove(connection);
  154. }
  155. });
  156. // Listen for disco#items requests and answer with an empty result
  157. PacketFilter packetFilter = new PacketTypeFilter(DiscoverItems.class);
  158. PacketListener packetListener = new PacketListener() {
  159. public void processPacket(Packet packet) {
  160. DiscoverItems discoverItems = (DiscoverItems) packet;
  161. // Send back the items defined in the client if the request is of type GET
  162. if (discoverItems != null && discoverItems.getType() == IQ.Type.GET) {
  163. DiscoverItems response = new DiscoverItems();
  164. response.setType(IQ.Type.RESULT);
  165. response.setTo(discoverItems.getFrom());
  166. response.setPacketID(discoverItems.getPacketID());
  167. // Add the defined items related to the requested node. Look for
  168. // the NodeInformationProvider associated with the requested node.
  169. if (getNodeInformationProvider(discoverItems.getNode()) != null) {
  170. Iterator items =
  171. getNodeInformationProvider(discoverItems.getNode()).getNodeItems();
  172. while (items.hasNext()) {
  173. response.addItem((DiscoverItems.Item) items.next());
  174. }
  175. } else if(discoverItems.getNode() != null) {
  176. // Return an <item-not-found/> error since the client
  177. // doesn't contain the specified node
  178. response.setNode(discoverItems.getNode());
  179. response.setType(IQ.Type.ERROR);
  180. response.setError(new XMPPError(404, "item-not-found"));
  181. }
  182. connection.sendPacket(response);
  183. }
  184. }
  185. };
  186. connection.addPacketListener(packetListener, packetFilter);
  187. // Listen for disco#info requests and answer the client's supported features
  188. // To add a new feature as supported use the #addFeature message
  189. packetFilter = new PacketTypeFilter(DiscoverInfo.class);
  190. packetListener = new PacketListener() {
  191. public void processPacket(Packet packet) {
  192. DiscoverInfo discoverInfo = (DiscoverInfo) packet;
  193. // Answer the client's supported features if the request is of the GET type
  194. if (discoverInfo != null && discoverInfo.getType() == IQ.Type.GET) {
  195. DiscoverInfo response = new DiscoverInfo();
  196. response.setType(IQ.Type.RESULT);
  197. response.setTo(discoverInfo.getFrom());
  198. response.setPacketID(discoverInfo.getPacketID());
  199. // Add the client's identity and features only if "node" is null
  200. if (discoverInfo.getNode() == null) {
  201. // Set this client identity
  202. DiscoverInfo.Identity identity = new DiscoverInfo.Identity(getIdentityCategory(),
  203. getIdentityName());
  204. identity.setType(getIdentityType());
  205. response.addIdentity(identity);
  206. // Add the registered features to the response
  207. synchronized (features) {
  208. for (Iterator it = getFeatures(); it.hasNext();) {
  209. response.addFeature((String) it.next());
  210. }
  211. }
  212. }
  213. else {
  214. // Return an <item-not-found/> error since a client doesn't have nodes
  215. response.setNode(discoverInfo.getNode());
  216. response.setType(IQ.Type.ERROR);
  217. response.setError(new XMPPError(404, "item-not-found"));
  218. }
  219. connection.sendPacket(response);
  220. }
  221. }
  222. };
  223. connection.addPacketListener(packetListener, packetFilter);
  224. }
  225. /**
  226. * Returns the NodeInformationProvider responsible for providing information
  227. * (ie items) related to a given node or <tt>null</null> if none.<p>
  228. *
  229. * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
  230. * NodeInformationProvider will provide information about the rooms where the user has joined.
  231. *
  232. * @param node the node that contains items associated with an entity not addressable as a JID.
  233. * @return the NodeInformationProvider responsible for providing information related
  234. * to a given node.
  235. */
  236. private NodeInformationProvider getNodeInformationProvider(String node) {
  237. if (node == null) {
  238. return null;
  239. }
  240. return (NodeInformationProvider) nodeInformationProviders.get(node);
  241. }
  242. /**
  243. * Sets the NodeInformationProvider responsible for providing information
  244. * (ie items) related to a given node. Every time this client receives a disco request
  245. * regarding the items of a given node, the provider associated to that node will be the
  246. * responsible for providing the requested information.<p>
  247. *
  248. * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
  249. * NodeInformationProvider will provide information about the rooms where the user has joined.
  250. *
  251. * @param node the node whose items will be provided by the NodeInformationProvider.
  252. * @param listener the NodeInformationProvider responsible for providing items related
  253. * to the node.
  254. */
  255. public void setNodeInformationProvider(String node, NodeInformationProvider listener) {
  256. nodeInformationProviders.put(node, listener);
  257. }
  258. /**
  259. * Removes the NodeInformationProvider responsible for providing information
  260. * (ie items) related to a given node. This means that no more information will be
  261. * available for the specified node.
  262. *
  263. * In MUC, a node could be 'http://jabber.org/protocol/muc#rooms' which means that the
  264. * NodeInformationProvider will provide information about the rooms where the user has joined.
  265. *
  266. * @param node the node to remove the associated NodeInformationProvider.
  267. */
  268. public void removeNodeInformationProvider(String node) {
  269. nodeInformationProviders.remove(node);
  270. }
  271. /**
  272. * Returns the supported features by this XMPP entity.
  273. *
  274. * @return an Iterator on the supported features by this XMPP entity.
  275. */
  276. public Iterator getFeatures() {
  277. synchronized (features) {
  278. return Collections.unmodifiableList(new ArrayList(features)).iterator();
  279. }
  280. }
  281. /**
  282. * Registers that a new feature is supported by this XMPP entity. When this client is
  283. * queried for its information the registered features will be answered.<p>
  284. *
  285. * Since no packet is actually sent to the server it is safe to perform this operation
  286. * before logging to the server. In fact, you may want to configure the supported features
  287. * before logging to the server so that the information is already available if it is required
  288. * upon login.
  289. *
  290. * @param feature the feature to register as supported.
  291. */
  292. public void addFeature(String feature) {
  293. synchronized (features) {
  294. features.add(feature);
  295. }
  296. }
  297. /**
  298. * Removes the specified feature from the supported features by this XMPP entity.<p>
  299. *
  300. * Since no packet is actually sent to the server it is safe to perform this operation
  301. * before logging to the server.
  302. *
  303. * @param feature the feature to remove from the supported features.
  304. */
  305. public void removeFeature(String feature) {
  306. synchronized (features) {
  307. features.remove(feature);
  308. }
  309. }
  310. /**
  311. * Returns true if the specified feature is registered in the ServiceDiscoveryManager.
  312. *
  313. * @param feature the feature to look for.
  314. * @return a boolean indicating if the specified featured is registered or not.
  315. */
  316. public boolean includesFeature(String feature) {
  317. synchronized (features) {
  318. return features.contains(feature);
  319. }
  320. }
  321. /**
  322. * Returns the discovered information of a given XMPP entity addressed by its JID.
  323. *
  324. * @param entityID the address of the XMPP entity.
  325. * @return the discovered information.
  326. * @throws XMPPException if the operation failed for some reason.
  327. */
  328. public DiscoverInfo discoverInfo(String entityID) throws XMPPException {
  329. return discoverInfo(entityID, null);
  330. }
  331. /**
  332. * Returns the discovered information of a given XMPP entity addressed by its JID and
  333. * note attribute. Use this message only when trying to query information which is not
  334. * directly addressable.
  335. *
  336. * @param entityID the address of the XMPP entity.
  337. * @param node the attribute that supplements the 'jid' attribute.
  338. * @return the discovered information.
  339. * @throws XMPPException if the operation failed for some reason.
  340. */
  341. public DiscoverInfo discoverInfo(String entityID, String node) throws XMPPException {
  342. // Discover the entity's info
  343. DiscoverInfo disco = new DiscoverInfo();
  344. disco.setType(IQ.Type.GET);
  345. disco.setTo(entityID);
  346. disco.setNode(node);
  347. // Create a packet collector to listen for a response.
  348. PacketCollector collector =
  349. connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
  350. connection.sendPacket(disco);
  351. // Wait up to 5 seconds for a result.
  352. IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
  353. // Stop queuing results
  354. collector.cancel();
  355. if (result == null) {
  356. throw new XMPPException("No response from the server.");
  357. }
  358. if (result.getType() == IQ.Type.ERROR) {
  359. throw new XMPPException(result.getError());
  360. }
  361. return (DiscoverInfo) result;
  362. }
  363. /**
  364. * Returns the discovered items of a given XMPP entity addressed by its JID.
  365. *
  366. * @param entityID the address of the XMPP entity.
  367. * @return the discovered information.
  368. * @throws XMPPException if the operation failed for some reason.
  369. */
  370. public DiscoverItems discoverItems(String entityID) throws XMPPException {
  371. return discoverItems(entityID, null);
  372. }
  373. /**
  374. * Returns the discovered items of a given XMPP entity addressed by its JID and
  375. * note attribute. Use this message only when trying to query information which is not
  376. * directly addressable.
  377. *
  378. * @param entityID the address of the XMPP entity.
  379. * @param node the attribute that supplements the 'jid' attribute.
  380. * @return the discovered items.
  381. * @throws XMPPException if the operation failed for some reason.
  382. */
  383. public DiscoverItems discoverItems(String entityID, String node) throws XMPPException {
  384. // Discover the entity's items
  385. DiscoverItems disco = new DiscoverItems();
  386. disco.setType(IQ.Type.GET);
  387. disco.setTo(entityID);
  388. disco.setNode(node);
  389. // Create a packet collector to listen for a response.
  390. PacketCollector collector =
  391. connection.createPacketCollector(new PacketIDFilter(disco.getPacketID()));
  392. connection.sendPacket(disco);
  393. // Wait up to 5 seconds for a result.
  394. IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
  395. // Stop queuing results
  396. collector.cancel();
  397. if (result == null) {
  398. throw new XMPPException("No response from the server.");
  399. }
  400. if (result.getType() == IQ.Type.ERROR) {
  401. throw new XMPPException(result.getError());
  402. }
  403. return (DiscoverItems) result;
  404. }
  405. /**
  406. * Returns true if the server supports publishing of items. A client may wish to publish items
  407. * to the server so that the server can provide items associated to the client. These items will
  408. * be returned by the server whenever the server receives a disco request targeted to the bare
  409. * address of the client (i.e. user@host.com).
  410. *
  411. * @param entityID the address of the XMPP entity.
  412. * @return true if the server supports publishing of items.
  413. * @throws XMPPException if the operation failed for some reason.
  414. */
  415. public boolean canPublishItems(String entityID) throws XMPPException {
  416. DiscoverInfo info = discoverInfo(entityID);
  417. return info.containsFeature("http://jabber.org/protocol/disco#publish");
  418. }
  419. /**
  420. * Publishes new items to a parent entity. The item elements to publish MUST have at least
  421. * a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
  422. * specifies the action being taken for that item. Possible action values are: "update" and
  423. * "remove".
  424. *
  425. * @param entityID the address of the XMPP entity.
  426. * @param discoverItems the DiscoveryItems to publish.
  427. * @throws XMPPException if the operation failed for some reason.
  428. */
  429. public void publishItems(String entityID, DiscoverItems discoverItems)
  430. throws XMPPException {
  431. publishItems(entityID, null, discoverItems);
  432. }
  433. /**
  434. * Publishes new items to a parent entity and node. The item elements to publish MUST have at
  435. * least a 'jid' attribute specifying the Entity ID of the item, and an action attribute which
  436. * specifies the action being taken for that item. Possible action values are: "update" and
  437. * "remove".
  438. *
  439. * @param entityID the address of the XMPP entity.
  440. * @param node the attribute that supplements the 'jid' attribute.
  441. * @param discoverItems the DiscoveryItems to publish.
  442. * @throws XMPPException if the operation failed for some reason.
  443. */
  444. public void publishItems(String entityID, String node, DiscoverItems discoverItems)
  445. throws XMPPException {
  446. discoverItems.setType(IQ.Type.SET);
  447. discoverItems.setTo(entityID);
  448. discoverItems.setNode(node);
  449. // Create a packet collector to listen for a response.
  450. PacketCollector collector =
  451. connection.createPacketCollector(new PacketIDFilter(discoverItems.getPacketID()));
  452. connection.sendPacket(discoverItems);
  453. // Wait up to 5 seconds for a result.
  454. IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
  455. // Stop queuing results
  456. collector.cancel();
  457. if (result == null) {
  458. throw new XMPPException("No response from the server.");
  459. }
  460. if (result.getType() == IQ.Type.ERROR) {
  461. throw new XMPPException(result.getError());
  462. }
  463. }
  464. }