mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-22 20:12:07 +01:00
Improve PubSub API
Use Manager pattern for PubSubManager. Also improve the API of ServiceDiscoverManager.
This commit is contained in:
parent
9e351f0535
commit
001e824fb9
16 changed files with 368 additions and 140 deletions
|
@ -53,7 +53,7 @@ Create a node with default configuration and then configure it:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Create the node
|
// Create the node
|
||||||
LeafNode leaf = mgr.createNode("testNode");
|
LeafNode leaf = mgr.createNode("testNode");
|
||||||
|
@ -71,7 +71,7 @@ Create and configure a node:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Create the node
|
// Create the node
|
||||||
ConfigureForm form = new ConfigureForm(FormType.submit);
|
ConfigureForm form = new ConfigureForm(FormType.submit);
|
||||||
|
@ -108,7 +108,7 @@ In this example we publish an item to a node that does not take payload:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -124,7 +124,7 @@ In this example we publish an item to a node that does take payload:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -167,7 +167,7 @@ subscribe for messages.
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -198,7 +198,7 @@ subscribe for item deletion messages.
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -230,7 +230,7 @@ subscribe for node configuration messages.
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
Node node = mgr.getNode("testNode");
|
Node node = mgr.getNode("testNode");
|
||||||
|
@ -286,7 +286,7 @@ In this example we can see how to retrieve the existing items from a node:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -298,7 +298,7 @@ In this example we can see how to retrieve the last N existing items:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -310,7 +310,7 @@ In this example we can see how to retrieve the specified existing items:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the node
|
// Get the node
|
||||||
LeafNode node = mgr.getNode("testNode");
|
LeafNode node = mgr.getNode("testNode");
|
||||||
|
@ -341,7 +341,7 @@ In this example we can see how to get pubsub capabilities:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the pubsub features that are supported
|
// Get the pubsub features that are supported
|
||||||
DiscoverInfo supportedFeatures = mgr.getSupportedFeatures();
|
DiscoverInfo supportedFeatures = mgr.getSupportedFeatures();
|
||||||
|
@ -351,7 +351,7 @@ In this example we can see how to get pubsub subscriptions for all nodes:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get all the subscriptions in the pubsub service
|
// Get all the subscriptions in the pubsub service
|
||||||
List<Subscription;> subscriptions = mgr.getSubscriptions();
|
List<Subscription;> subscriptions = mgr.getSubscriptions();
|
||||||
|
@ -362,7 +362,7 @@ on the pubsub service:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
|
|
||||||
// Get the affiliations for the users bare JID
|
// Get the affiliations for the users bare JID
|
||||||
List<Affiliation;> affiliations = mgr.getAffiliations();
|
List<Affiliation;> affiliations = mgr.getAffiliations();
|
||||||
|
@ -372,7 +372,7 @@ In this example we can see how to get information about the node:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
Node node = mgr.getNode("testNode");
|
Node node = mgr.getNode("testNode");
|
||||||
|
|
||||||
// Get the node information
|
// Get the node information
|
||||||
|
@ -383,7 +383,7 @@ In this example we can see how to discover the node items:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
Node node = mgr.getNode("testNode");
|
Node node = mgr.getNode("testNode");
|
||||||
|
|
||||||
// Discover the node items
|
// Discover the node items
|
||||||
|
@ -394,7 +394,7 @@ In this example we can see how to get node subscriptions:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Create a pubsub manager using an existing XMPPConnection
|
// Create a pubsub manager using an existing XMPPConnection
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = PubSubManager.getInstanceFor(con);
|
||||||
Node node = mgr.getNode("testNode");
|
Node node = mgr.getNode("testNode");
|
||||||
|
|
||||||
// Discover the node subscriptions
|
// Discover the node subscriptions
|
||||||
|
|
|
@ -283,11 +283,7 @@ public class MultipleRecipientManager {
|
||||||
*/
|
*/
|
||||||
private static DomainBareJid getMultipleRecipienServiceAddress(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
private static DomainBareJid getMultipleRecipienServiceAddress(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
List<DomainBareJid> services = sdm.findServices(MultipleAddresses.NAMESPACE, true, true);
|
return sdm.findService(MultipleAddresses.NAMESPACE, true);
|
||||||
if (services.size() > 0) {
|
|
||||||
return services.get(0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -684,7 +684,7 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
* Create a cache to hold the 25 most recently lookup services for a given feature for a period
|
* Create a cache to hold the 25 most recently lookup services for a given feature for a period
|
||||||
* of 24 hours.
|
* of 24 hours.
|
||||||
*/
|
*/
|
||||||
private Cache<String, List<DomainBareJid>> services = new ExpirationCache<>(25,
|
private Cache<String, List<DiscoverInfo>> services = new ExpirationCache<>(25,
|
||||||
24 * 60 * 60 * 1000);
|
24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -699,17 +699,17 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public List<DomainBareJid> findServices(String feature, boolean stopOnFirst, boolean useCache)
|
public List<DiscoverInfo> findServicesDiscoverInfo(String feature, boolean stopOnFirst, boolean useCache)
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
List<DomainBareJid> serviceAddresses = null;
|
List<DiscoverInfo> serviceDiscoInfo = null;
|
||||||
DomainBareJid serviceName = connection().getServiceName();
|
DomainBareJid serviceName = connection().getServiceName();
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
serviceAddresses = services.get(feature);
|
serviceDiscoInfo = services.get(feature);
|
||||||
if (serviceAddresses != null) {
|
if (serviceDiscoInfo != null) {
|
||||||
return serviceAddresses;
|
return serviceDiscoInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serviceAddresses = new LinkedList<>();
|
serviceDiscoInfo = new LinkedList<>();
|
||||||
// Send the disco packet to the server itself
|
// Send the disco packet to the server itself
|
||||||
DiscoverInfo info;
|
DiscoverInfo info;
|
||||||
try {
|
try {
|
||||||
|
@ -717,17 +717,17 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
} catch (XMPPErrorException e) {
|
} catch (XMPPErrorException e) {
|
||||||
// Be extra robust here: Return the empty linked list and log this situation
|
// Be extra robust here: Return the empty linked list and log this situation
|
||||||
LOGGER.log(Level.WARNING, "Could not discover information about service", e);
|
LOGGER.log(Level.WARNING, "Could not discover information about service", e);
|
||||||
return serviceAddresses;
|
return serviceDiscoInfo;
|
||||||
}
|
}
|
||||||
// Check if the server supports XEP-33
|
// Check if the server supports the feature
|
||||||
if (info.containsFeature(feature)) {
|
if (info.containsFeature(feature)) {
|
||||||
serviceAddresses.add(serviceName);
|
serviceDiscoInfo.add(info);
|
||||||
if (stopOnFirst) {
|
if (stopOnFirst) {
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
// Cache the discovered information
|
// Cache the discovered information
|
||||||
services.put(feature, serviceAddresses);
|
services.put(feature, serviceDiscoInfo);
|
||||||
}
|
}
|
||||||
return serviceAddresses;
|
return serviceDiscoInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiscoverItems items;
|
DiscoverItems items;
|
||||||
|
@ -736,7 +736,7 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
items = discoverItems(serviceName);
|
items = discoverItems(serviceName);
|
||||||
} catch(XMPPErrorException e) {
|
} catch(XMPPErrorException e) {
|
||||||
LOGGER.log(Level.WARNING, "Could not discover items about service", e);
|
LOGGER.log(Level.WARNING, "Could not discover items about service", e);
|
||||||
return serviceAddresses;
|
return serviceDiscoInfo;
|
||||||
}
|
}
|
||||||
for (DiscoverItems.Item item : items.getItems()) {
|
for (DiscoverItems.Item item : items.getItems()) {
|
||||||
try {
|
try {
|
||||||
|
@ -752,7 +752,8 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (info.containsFeature(feature)) {
|
if (info.containsFeature(feature)) {
|
||||||
serviceAddresses.add(item.getEntityID().asDomainBareJid());
|
serviceDiscoInfo.add(info);
|
||||||
|
//serviceAddresses.add(item.getEntityID().asDomainBareJid());
|
||||||
if (stopOnFirst) {
|
if (stopOnFirst) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -760,9 +761,54 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
}
|
}
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
// Cache the discovered information
|
// Cache the discovered information
|
||||||
services.put(feature, serviceAddresses);
|
services.put(feature, serviceDiscoInfo);
|
||||||
}
|
}
|
||||||
return serviceAddresses;
|
return serviceDiscoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
public List<DomainBareJid> findServices(String feature, boolean stopOnFirst, boolean useCache) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
List<DiscoverInfo> services = findServicesDiscoverInfo(feature, stopOnFirst, useCache);
|
||||||
|
List<DomainBareJid> res = new ArrayList<>(services.size());
|
||||||
|
for (DiscoverInfo info : services) {
|
||||||
|
res.add(info.getFrom().asDomainBareJid());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DomainBareJid findService(String feature, boolean useCache, String category, String type)
|
||||||
|
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
||||||
|
InterruptedException {
|
||||||
|
List<DiscoverInfo> services = findServicesDiscoverInfo(feature, true, useCache);
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
DiscoverInfo info = services.get(0);
|
||||||
|
if (category != null && type != null) {
|
||||||
|
if (!info.hasIdentity(category, type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (category != null || type != null) {
|
||||||
|
throw new IllegalArgumentException("Must specify either both, category and type, or none");
|
||||||
|
}
|
||||||
|
return info.getFrom().asDomainBareJid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DomainBareJid findService(String feature, boolean useCache) throws NoResponseException,
|
||||||
|
XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
return findService(feature, useCache, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,13 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
|
|
||||||
public class CollectionNode extends Node
|
public class CollectionNode extends Node
|
||||||
{
|
{
|
||||||
CollectionNode(XMPPConnection connection, String nodeId)
|
CollectionNode(PubSubManager pubSubManager, String nodeId)
|
||||||
{
|
{
|
||||||
super(connection, nodeId);
|
super(pubSubManager, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
||||||
|
|
||||||
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.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
@ -39,9 +38,9 @@ import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
*/
|
*/
|
||||||
public class LeafNode extends Node
|
public class LeafNode extends Node
|
||||||
{
|
{
|
||||||
LeafNode(XMPPConnection connection, String nodeName)
|
LeafNode(PubSubManager pubSubManager, String nodeId)
|
||||||
{
|
{
|
||||||
super(connection, nodeName);
|
super(pubSubManager, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,9 +56,9 @@ public class LeafNode extends Node
|
||||||
public DiscoverItems discoverItems() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
public DiscoverItems discoverItems() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
||||||
{
|
{
|
||||||
DiscoverItems items = new DiscoverItems();
|
DiscoverItems items = new DiscoverItems();
|
||||||
items.setTo(to);
|
items.setTo(pubSubManager.getServiceJid());
|
||||||
items.setNode(getId());
|
items.setNode(getId());
|
||||||
return (DiscoverItems) con.createPacketCollectorAndSend(items).nextResultOrThrow();
|
return pubSubManager.getConnection().createPacketCollectorAndSend(items).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,7 +192,7 @@ public class LeafNode extends Node
|
||||||
private <T extends Item> List<T> getItems(PubSub request,
|
private <T extends Item> List<T> getItems(PubSub request,
|
||||||
List<ExtensionElement> returnedExtensions) throws NoResponseException,
|
List<ExtensionElement> returnedExtensions) throws NoResponseException,
|
||||||
XMPPErrorException, NotConnectedException, InterruptedException {
|
XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
PubSub result = con.createPacketCollectorAndSend(request).nextResultOrThrow();
|
PubSub result = pubSubManager.getConnection().createPacketCollectorAndSend(request).nextResultOrThrow();
|
||||||
ItemsExtension itemsElem = result.getExtension(PubSubElementType.ITEMS);
|
ItemsExtension itemsElem = result.getExtension(PubSubElementType.ITEMS);
|
||||||
if (returnedExtensions != null) {
|
if (returnedExtensions != null) {
|
||||||
returnedExtensions.addAll(result.getExtensions());
|
returnedExtensions.addAll(result.getExtensions());
|
||||||
|
@ -219,7 +218,7 @@ public class LeafNode extends Node
|
||||||
{
|
{
|
||||||
PubSub packet = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PUBLISH, getId()));
|
PubSub packet = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PUBLISH, getId()));
|
||||||
|
|
||||||
con.sendStanza(packet);
|
pubSubManager.getConnection().sendStanza(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,7 +265,7 @@ public class LeafNode extends Node
|
||||||
{
|
{
|
||||||
PubSub packet = createPubsubPacket(Type.set, new PublishItem<T>(getId(), items));
|
PubSub packet = createPubsubPacket(Type.set, new PublishItem<T>(getId(), items));
|
||||||
|
|
||||||
con.sendStanza(packet);
|
pubSubManager.getConnection().sendStanza(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,7 +289,7 @@ public class LeafNode extends Node
|
||||||
{
|
{
|
||||||
PubSub packet = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PUBLISH, getId()));
|
PubSub packet = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PUBLISH, getId()));
|
||||||
|
|
||||||
con.createPacketCollectorAndSend(packet).nextResultOrThrow();
|
pubSubManager.getConnection().createPacketCollectorAndSend(packet).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -347,7 +346,7 @@ public class LeafNode extends Node
|
||||||
{
|
{
|
||||||
PubSub packet = createPubsubPacket(Type.set, new PublishItem<T>(getId(), items));
|
PubSub packet = createPubsubPacket(Type.set, new PublishItem<T>(getId(), items));
|
||||||
|
|
||||||
con.createPacketCollectorAndSend(packet).nextResultOrThrow();
|
pubSubManager.getConnection().createPacketCollectorAndSend(packet).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -364,7 +363,7 @@ public class LeafNode extends Node
|
||||||
{
|
{
|
||||||
PubSub request = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PURGE_OWNER, getId()), PubSubElementType.PURGE_OWNER.getNamespace());
|
PubSub request = createPubsubPacket(Type.set, new NodeExtension(PubSubElementType.PURGE_OWNER, getId()), PubSubElementType.PURGE_OWNER.getNamespace());
|
||||||
|
|
||||||
con.createPacketCollectorAndSend(request).nextResultOrThrow();
|
pubSubManager.getConnection().createPacketCollectorAndSend(request).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,6 +400,6 @@ public class LeafNode extends Node
|
||||||
items.add(new Item(id));
|
items.add(new Item(id));
|
||||||
}
|
}
|
||||||
PubSub request = createPubsubPacket(Type.set, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items));
|
PubSub request = createPubsubPacket(Type.set, new ItemsExtension(ItemsExtension.ItemsElementType.retract, getId(), items));
|
||||||
con.createPacketCollectorAndSend(request).nextResultOrThrow();
|
pubSubManager.getConnection().createPacketCollectorAndSend(request).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.jivesoftware.smack.StanzaListener;
|
import org.jivesoftware.smack.StanzaListener;
|
||||||
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.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.filter.OrFilter;
|
import org.jivesoftware.smack.filter.OrFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
|
@ -43,13 +42,11 @@ import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
||||||
import org.jivesoftware.smackx.shim.packet.Header;
|
import org.jivesoftware.smackx.shim.packet.Header;
|
||||||
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.Form;
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
|
|
||||||
abstract public class Node
|
abstract public class Node
|
||||||
{
|
{
|
||||||
protected XMPPConnection con;
|
protected final PubSubManager pubSubManager;
|
||||||
protected String id;
|
protected final String id;
|
||||||
protected Jid to;
|
|
||||||
|
|
||||||
protected ConcurrentHashMap<ItemEventListener<Item>, StanzaListener> itemEventToListenerMap = new ConcurrentHashMap<ItemEventListener<Item>, StanzaListener>();
|
protected ConcurrentHashMap<ItemEventListener<Item>, StanzaListener> itemEventToListenerMap = new ConcurrentHashMap<ItemEventListener<Item>, StanzaListener>();
|
||||||
protected ConcurrentHashMap<ItemDeleteListener, StanzaListener> itemDeleteToListenerMap = new ConcurrentHashMap<ItemDeleteListener, StanzaListener>();
|
protected ConcurrentHashMap<ItemDeleteListener, StanzaListener> itemDeleteToListenerMap = new ConcurrentHashMap<ItemDeleteListener, StanzaListener>();
|
||||||
|
@ -62,21 +59,10 @@ abstract public class Node
|
||||||
* @param connection The connection the node is associated with
|
* @param connection The connection the node is associated with
|
||||||
* @param nodeName The node id
|
* @param nodeName The node id
|
||||||
*/
|
*/
|
||||||
Node(XMPPConnection connection, String nodeName)
|
Node(PubSubManager pubSubManager, String nodeId)
|
||||||
{
|
{
|
||||||
con = connection;
|
this.pubSubManager = pubSubManager;
|
||||||
id = nodeName;
|
id = nodeId;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some XMPP servers may require a specific service to be addressed on the
|
|
||||||
* server.
|
|
||||||
*
|
|
||||||
* For example, OpenFire requires the server to be prefixed by <b>pubsub</b>
|
|
||||||
*/
|
|
||||||
void setTo(Jid toAddress)
|
|
||||||
{
|
|
||||||
to = toAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +105,7 @@ abstract public class Node
|
||||||
{
|
{
|
||||||
PubSub packet = createPubsubPacket(Type.set, new FormNode(FormNodeType.CONFIGURE_OWNER,
|
PubSub packet = createPubsubPacket(Type.set, new FormNode(FormNodeType.CONFIGURE_OWNER,
|
||||||
getId(), submitForm), PubSubNamespace.OWNER);
|
getId(), submitForm), PubSubNamespace.OWNER);
|
||||||
con.createPacketCollectorAndSend(packet).nextResultOrThrow();
|
pubSubManager.getConnection().createPacketCollectorAndSend(packet).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,9 +120,9 @@ abstract public class Node
|
||||||
public DiscoverInfo discoverInfo() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
public DiscoverInfo discoverInfo() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
||||||
{
|
{
|
||||||
DiscoverInfo info = new DiscoverInfo();
|
DiscoverInfo info = new DiscoverInfo();
|
||||||
info.setTo(to);
|
info.setTo(pubSubManager.getServiceJid());
|
||||||
info.setNode(getId());
|
info.setNode(getId());
|
||||||
return (DiscoverInfo) con.createPacketCollectorAndSend(info).nextResultOrThrow();
|
return pubSubManager.getConnection().createPacketCollectorAndSend(info).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -336,7 +322,7 @@ abstract public class Node
|
||||||
PubSub request = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
|
PubSub request = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
|
||||||
// CHECKSTYLE:ON
|
// CHECKSTYLE:ON
|
||||||
request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
|
request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
|
||||||
PubSub reply = PubSubManager.sendPubsubPacket(con, request);
|
PubSub reply = sendPubsubPacket(request);
|
||||||
return reply.getExtension(PubSubElementType.SUBSCRIPTION);
|
return reply.getExtension(PubSubElementType.SUBSCRIPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +406,7 @@ abstract public class Node
|
||||||
{
|
{
|
||||||
StanzaListener conListener = new ItemEventTranslator(listener);
|
StanzaListener conListener = new ItemEventTranslator(listener);
|
||||||
itemEventToListenerMap.put(listener, conListener);
|
itemEventToListenerMap.put(listener, conListener);
|
||||||
con.addSyncStanzaListener(conListener, new EventContentFilter(EventElementType.items.toString(), "item"));
|
pubSubManager.getConnection().addSyncStanzaListener(conListener, new EventContentFilter(EventElementType.items.toString(), "item"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -433,7 +419,7 @@ abstract public class Node
|
||||||
StanzaListener conListener = itemEventToListenerMap.remove(listener);
|
StanzaListener conListener = itemEventToListenerMap.remove(listener);
|
||||||
|
|
||||||
if (conListener != null)
|
if (conListener != null)
|
||||||
con.removeSyncStanzaListener(conListener);
|
pubSubManager.getConnection().removeSyncStanzaListener(conListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -446,7 +432,7 @@ abstract public class Node
|
||||||
{
|
{
|
||||||
StanzaListener conListener = new NodeConfigTranslator(listener);
|
StanzaListener conListener = new NodeConfigTranslator(listener);
|
||||||
configEventToListenerMap.put(listener, conListener);
|
configEventToListenerMap.put(listener, conListener);
|
||||||
con.addSyncStanzaListener(conListener, new EventContentFilter(EventElementType.configuration.toString()));
|
pubSubManager.getConnection().addSyncStanzaListener(conListener, new EventContentFilter(EventElementType.configuration.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -459,7 +445,7 @@ abstract public class Node
|
||||||
StanzaListener conListener = configEventToListenerMap .remove(listener);
|
StanzaListener conListener = configEventToListenerMap .remove(listener);
|
||||||
|
|
||||||
if (conListener != null)
|
if (conListener != null)
|
||||||
con.removeSyncStanzaListener(conListener);
|
pubSubManager.getConnection().removeSyncStanzaListener(conListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -475,7 +461,7 @@ abstract public class Node
|
||||||
EventContentFilter deleteItem = new EventContentFilter(EventElementType.items.toString(), "retract");
|
EventContentFilter deleteItem = new EventContentFilter(EventElementType.items.toString(), "retract");
|
||||||
EventContentFilter purge = new EventContentFilter(EventElementType.purge.toString());
|
EventContentFilter purge = new EventContentFilter(EventElementType.purge.toString());
|
||||||
|
|
||||||
con.addSyncStanzaListener(delListener, new OrFilter(deleteItem, purge));
|
pubSubManager.getConnection().addSyncStanzaListener(delListener, new OrFilter(deleteItem, purge));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -488,7 +474,7 @@ abstract public class Node
|
||||||
StanzaListener conListener = itemDeleteToListenerMap .remove(listener);
|
StanzaListener conListener = itemDeleteToListenerMap .remove(listener);
|
||||||
|
|
||||||
if (conListener != null)
|
if (conListener != null)
|
||||||
con.removeSyncStanzaListener(conListener);
|
pubSubManager.getConnection().removeSyncStanzaListener(conListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -504,12 +490,12 @@ abstract public class Node
|
||||||
|
|
||||||
protected PubSub createPubsubPacket(Type type, ExtensionElement ext, PubSubNamespace ns)
|
protected PubSub createPubsubPacket(Type type, ExtensionElement ext, PubSubNamespace ns)
|
||||||
{
|
{
|
||||||
return PubSub.createPubsubPacket(to, type, ext, ns);
|
return PubSub.createPubsubPacket(pubSubManager.getServiceJid(), type, ext, ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PubSub sendPubsubPacket(PubSub packet) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
protected PubSub sendPubsubPacket(PubSub packet) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
||||||
{
|
{
|
||||||
return PubSubManager.sendPubsubPacket(con, packet);
|
return pubSubManager.sendPubsubPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,22 @@
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
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.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.EmptyResultIQ;
|
import org.jivesoftware.smack.packet.EmptyResultIQ;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
@ -52,23 +58,72 @@ import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
*
|
*
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
*/
|
*/
|
||||||
final public class PubSubManager
|
public final class PubSubManager extends Manager {
|
||||||
{
|
|
||||||
private XMPPConnection con;
|
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
|
||||||
private DomainBareJid to;
|
private static final Map<XMPPConnection, Map<DomainBareJid, PubSubManager>> INSTANCES = new WeakHashMap<>();
|
||||||
private Map<String, Node> nodeMap = new ConcurrentHashMap<String, Node>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a pubsub manager associated to the specified connection. Defaults the service
|
* The JID of the PubSub service this manager manages.
|
||||||
* name to <i>pubsub</i>
|
|
||||||
*
|
|
||||||
* @param connection The XMPP connection
|
|
||||||
* @throws XmppStringprepException
|
|
||||||
*/
|
*/
|
||||||
public PubSubManager(XMPPConnection connection) throws XmppStringprepException
|
private final DomainBareJid pubSubService;
|
||||||
{
|
|
||||||
con = connection;
|
/**
|
||||||
to = JidCreate.domainBareFrom("pubsub." + connection.getServiceName());
|
* A map of node IDs to Nodes, used to cache those Nodes. This does only cache the type of Node,
|
||||||
|
* i.e. {@link CollectionNode} or {@link LeafNode}.
|
||||||
|
*/
|
||||||
|
private final Map<String, Node> nodeMap = new ConcurrentHashMap<String, Node>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a PubSub manager for the default PubSub service of the connection.
|
||||||
|
*
|
||||||
|
* @param connection
|
||||||
|
* @return the default PubSub manager.
|
||||||
|
*/
|
||||||
|
public static PubSubManager getInstance(XMPPConnection connection) {
|
||||||
|
DomainBareJid pubSubService = null;
|
||||||
|
if (connection.isAuthenticated()) {
|
||||||
|
try {
|
||||||
|
pubSubService = getPubSubService(connection);
|
||||||
|
}
|
||||||
|
catch (NoResponseException | XMPPErrorException | NotConnectedException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not determine PubSub service", e);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
LOGGER.log(Level.FINE, "Interupted while trying to determine PubSub service", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pubSubService == null) {
|
||||||
|
try {
|
||||||
|
// Perform an educated guess about what the PubSub service's domain bare JID may be
|
||||||
|
pubSubService = JidCreate.domainBareFrom("pubsub." + connection.getServiceName());
|
||||||
|
}
|
||||||
|
catch (XmppStringprepException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getInstance(connection, pubSubService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the PubSub manager for the given connection and PubSub service.
|
||||||
|
*
|
||||||
|
* @param connection the XMPP connection.
|
||||||
|
* @param pubSubService the PubSub service.
|
||||||
|
* @return a PubSub manager for the connection and service.
|
||||||
|
*/
|
||||||
|
public static synchronized PubSubManager getInstance(XMPPConnection connection, DomainBareJid pubSubService) {
|
||||||
|
Map<DomainBareJid, PubSubManager> managers = INSTANCES.get(connection);
|
||||||
|
if (managers == null) {
|
||||||
|
managers = new HashMap<>();
|
||||||
|
INSTANCES.put(connection, managers);
|
||||||
|
}
|
||||||
|
PubSubManager pubSubManager = managers.get(pubSubService);
|
||||||
|
if (pubSubManager == null) {
|
||||||
|
pubSubManager = new PubSubManager(connection, pubSubService);
|
||||||
|
managers.put(pubSubService, pubSubManager);
|
||||||
|
}
|
||||||
|
return pubSubManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,10 +133,10 @@ final public class PubSubManager
|
||||||
* @param connection The XMPP connection
|
* @param connection The XMPP connection
|
||||||
* @param toAddress The pubsub specific to address (required for some servers)
|
* @param toAddress The pubsub specific to address (required for some servers)
|
||||||
*/
|
*/
|
||||||
public PubSubManager(XMPPConnection connection, DomainBareJid toAddress)
|
PubSubManager(XMPPConnection connection, DomainBareJid toAddress)
|
||||||
{
|
{
|
||||||
con = connection;
|
super(connection);
|
||||||
to = toAddress;
|
pubSubService = toAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,8 +153,7 @@ final public class PubSubManager
|
||||||
PubSub reply = sendPubsubPacket(Type.set, new NodeExtension(PubSubElementType.CREATE), null);
|
PubSub reply = sendPubsubPacket(Type.set, new NodeExtension(PubSubElementType.CREATE), null);
|
||||||
NodeExtension elem = reply.getExtension("create", PubSubNamespace.BASIC.getXmlns());
|
NodeExtension elem = reply.getExtension("create", PubSubNamespace.BASIC.getXmlns());
|
||||||
|
|
||||||
LeafNode newNode = new LeafNode(con, elem.getNode());
|
LeafNode newNode = new LeafNode(this, elem.getNode());
|
||||||
newNode.setTo(to);
|
|
||||||
nodeMap.put(newNode.getId(), newNode);
|
nodeMap.put(newNode.getId(), newNode);
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
|
@ -108,7 +162,7 @@ final public class PubSubManager
|
||||||
/**
|
/**
|
||||||
* Creates a node with default configuration.
|
* Creates a node with default configuration.
|
||||||
*
|
*
|
||||||
* @param id The id of the node, which must be unique within the
|
* @param nodeId The id of the node, which must be unique within the
|
||||||
* pubsub service
|
* pubsub service
|
||||||
* @return The node that was created
|
* @return The node that was created
|
||||||
* @throws XMPPErrorException
|
* @throws XMPPErrorException
|
||||||
|
@ -116,9 +170,9 @@ final public class PubSubManager
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public LeafNode createNode(String id) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
public LeafNode createNode(String nodeId) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
||||||
{
|
{
|
||||||
return (LeafNode)createNode(id, null);
|
return (LeafNode) createNode(nodeId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,7 +180,7 @@ final public class PubSubManager
|
||||||
*
|
*
|
||||||
* Note: This is the only way to create a collection node.
|
* Note: This is the only way to create a collection node.
|
||||||
*
|
*
|
||||||
* @param name The name of the node, which must be unique within the
|
* @param nodeId The name of the node, which must be unique within the
|
||||||
* pubsub service
|
* pubsub service
|
||||||
* @param config The configuration for the node
|
* @param config The configuration for the node
|
||||||
* @return The node that was created
|
* @return The node that was created
|
||||||
|
@ -135,9 +189,9 @@ final public class PubSubManager
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public Node createNode(String name, Form config) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
public Node createNode(String nodeId, Form config) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
||||||
{
|
{
|
||||||
PubSub request = PubSub.createPubsubPacket(to, Type.set, new NodeExtension(PubSubElementType.CREATE, name), null);
|
PubSub request = PubSub.createPubsubPacket(pubSubService, Type.set, new NodeExtension(PubSubElementType.CREATE, nodeId), null);
|
||||||
boolean isLeafNode = true;
|
boolean isLeafNode = true;
|
||||||
|
|
||||||
if (config != null)
|
if (config != null)
|
||||||
|
@ -151,9 +205,8 @@ final public class PubSubManager
|
||||||
|
|
||||||
// Errors will cause exceptions in getReply, so it only returns
|
// Errors will cause exceptions in getReply, so it only returns
|
||||||
// on success.
|
// on success.
|
||||||
sendPubsubPacket(con, request);
|
sendPubsubPacket(request);
|
||||||
Node newNode = isLeafNode ? new LeafNode(con, name) : new CollectionNode(con, name);
|
Node newNode = isLeafNode ? new LeafNode(this, nodeId) : new CollectionNode(this, nodeId);
|
||||||
newNode.setTo(to);
|
|
||||||
nodeMap.put(newNode.getId(), newNode);
|
nodeMap.put(newNode.getId(), newNode);
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
|
@ -177,16 +230,16 @@ final public class PubSubManager
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
DiscoverInfo info = new DiscoverInfo();
|
DiscoverInfo info = new DiscoverInfo();
|
||||||
info.setTo(to);
|
info.setTo(pubSubService);
|
||||||
info.setNode(id);
|
info.setNode(id);
|
||||||
|
|
||||||
DiscoverInfo infoReply = (DiscoverInfo) con.createPacketCollectorAndSend(info).nextResultOrThrow();
|
DiscoverInfo infoReply = connection().createPacketCollectorAndSend(info).nextResultOrThrow();
|
||||||
|
|
||||||
if (infoReply.hasIdentity(PubSub.ELEMENT, "leaf")) {
|
if (infoReply.hasIdentity(PubSub.ELEMENT, "leaf")) {
|
||||||
node = new LeafNode(con, id);
|
node = new LeafNode(this, id);
|
||||||
}
|
}
|
||||||
else if (infoReply.hasIdentity(PubSub.ELEMENT, "collection")) {
|
else if (infoReply.hasIdentity(PubSub.ELEMENT, "collection")) {
|
||||||
node = new CollectionNode(con, id);
|
node = new CollectionNode(this, id);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// XEP-60 5.3 states that
|
// XEP-60 5.3 states that
|
||||||
|
@ -194,12 +247,11 @@ final public class PubSubManager
|
||||||
// If this is not the case, then we are dealing with an PubSub implementation that doesn't follow the specification.
|
// If this is not the case, then we are dealing with an PubSub implementation that doesn't follow the specification.
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
"PubSub service '"
|
"PubSub service '"
|
||||||
+ to
|
+ pubSubService
|
||||||
+ "' returned disco info result for node '"
|
+ "' returned disco info result for node '"
|
||||||
+ id
|
+ id
|
||||||
+ "', but it did not contain an Identity of type 'leaf' or 'collection' (and category 'pubsub'), which is not allowed according to XEP-60 5.3.");
|
+ "', but it did not contain an Identity of type 'leaf' or 'collection' (and category 'pubsub'), which is not allowed according to XEP-60 5.3.");
|
||||||
}
|
}
|
||||||
node.setTo(to);
|
|
||||||
nodeMap.put(id, node);
|
nodeMap.put(id, node);
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -229,8 +281,8 @@ final public class PubSubManager
|
||||||
|
|
||||||
if (nodeId != null)
|
if (nodeId != null)
|
||||||
items.setNode(nodeId);
|
items.setNode(nodeId);
|
||||||
items.setTo(to);
|
items.setTo(pubSubService);
|
||||||
DiscoverItems nodeItems = (DiscoverItems) con.createPacketCollectorAndSend(items).nextResultOrThrow();
|
DiscoverItems nodeItems = connection().createPacketCollectorAndSend(items).nextResultOrThrow();
|
||||||
return nodeItems;
|
return nodeItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,6 +351,15 @@ final public class PubSubManager
|
||||||
return NodeUtils.getFormFromPacket(reply, PubSubElementType.DEFAULT);
|
return NodeUtils.getFormFromPacket(reply, PubSubElementType.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the JID of the PubSub service managed by this manager.
|
||||||
|
*
|
||||||
|
* @return the JID of the PubSub service.
|
||||||
|
*/
|
||||||
|
public DomainBareJid getServiceJid() {
|
||||||
|
return pubSubService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the supported features of the servers pubsub implementation
|
* Gets the supported features of the servers pubsub implementation
|
||||||
* as a standard {@link DiscoverInfo} instance.
|
* as a standard {@link DiscoverInfo} instance.
|
||||||
|
@ -311,33 +372,92 @@ final public class PubSubManager
|
||||||
*/
|
*/
|
||||||
public DiscoverInfo getSupportedFeatures() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
public DiscoverInfo getSupportedFeatures() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
||||||
{
|
{
|
||||||
ServiceDiscoveryManager mgr = ServiceDiscoveryManager.getInstanceFor(con);
|
ServiceDiscoveryManager mgr = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||||
return mgr.discoverInfo(to);
|
return mgr.discoverInfo(pubSubService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if it is possible to create PubSub nodes on this service. It could be possible that the
|
||||||
|
* PubSub service allows only certain XMPP entities (clients) to create nodes and publish items
|
||||||
|
* to them.
|
||||||
|
* <p>
|
||||||
|
* Note that since XEP-60 does not provide an API to determine if an XMPP entity is allowed to
|
||||||
|
* create nodes, therefore this method creates an instant node calling {@link #createNode()} to
|
||||||
|
* determine if it is possible to create nodes.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if it is possible to create nodes, <code>false</code> otherwise.
|
||||||
|
* @throws NoResponseException
|
||||||
|
* @throws NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws XMPPErrorException
|
||||||
|
*/
|
||||||
|
public boolean canCreateNodesAndPublishItems() throws NoResponseException, NotConnectedException, InterruptedException, XMPPErrorException {
|
||||||
|
LeafNode leafNode = null;
|
||||||
|
try {
|
||||||
|
leafNode = createNode();
|
||||||
|
}
|
||||||
|
catch (XMPPErrorException e) {
|
||||||
|
if (e.getXMPPError().getCondition() == XMPPError.Condition.forbidden) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (leafNode != null) {
|
||||||
|
deleteNode(leafNode.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PubSub sendPubsubPacket(Type type, ExtensionElement ext, PubSubNamespace ns)
|
private PubSub sendPubsubPacket(Type type, ExtensionElement ext, PubSubNamespace ns)
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
return sendPubsubPacket(con, to, type, Collections.singletonList(ext), ns);
|
return sendPubsubPacket(pubSubService, type, Collections.singletonList(ext), ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PubSub sendPubsubPacket(XMPPConnection con, Jid to, Type type, List<ExtensionElement> extList, PubSubNamespace ns) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
XMPPConnection getConnection() {
|
||||||
{
|
return connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
PubSub sendPubsubPacket(Jid to, Type type, List<ExtensionElement> extList, PubSubNamespace ns)
|
||||||
|
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
||||||
|
InterruptedException {
|
||||||
// CHECKSTYLE:OFF
|
// CHECKSTYLE:OFF
|
||||||
PubSub pubSub = new PubSub(to, type, ns);
|
PubSub pubSub = new PubSub(to, type, ns);
|
||||||
for (ExtensionElement pe : extList) {
|
for (ExtensionElement pe : extList) {
|
||||||
pubSub.addExtension(pe);
|
pubSub.addExtension(pe);
|
||||||
}
|
}
|
||||||
// CHECKSTYLE:ON
|
// CHECKSTYLE:ON
|
||||||
return sendPubsubPacket(con ,pubSub);
|
return sendPubsubPacket(pubSub);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PubSub sendPubsubPacket(XMPPConnection con, PubSub packet) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
|
PubSub sendPubsubPacket(PubSub packet) throws NoResponseException, XMPPErrorException,
|
||||||
{
|
NotConnectedException, InterruptedException {
|
||||||
IQ resultIQ = con.createPacketCollectorAndSend(packet).nextResultOrThrow();
|
IQ resultIQ = connection().createPacketCollectorAndSend(packet).nextResultOrThrow();
|
||||||
if (resultIQ instanceof EmptyResultIQ) {
|
if (resultIQ instanceof EmptyResultIQ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (PubSub) resultIQ;
|
return (PubSub) resultIQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the "default" PubSub service for a given XMPP connection. The default PubSub service is
|
||||||
|
* simply an arbitrary XMPP service with the PubSub feature and an identity of category "pubsub"
|
||||||
|
* and type "service".
|
||||||
|
*
|
||||||
|
* @param connection
|
||||||
|
* @return the default PubSub service or <code>null</code>.
|
||||||
|
* @throws NoResponseException
|
||||||
|
* @throws XMPPErrorException
|
||||||
|
* @throws NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @see <a href="http://xmpp.org/extensions/xep-0060.html#entity-features">XEP-60 § 5.1 Discover
|
||||||
|
* Features</a>
|
||||||
|
*/
|
||||||
|
public static DomainBareJid getPubSubService(XMPPConnection connection)
|
||||||
|
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
||||||
|
InterruptedException {
|
||||||
|
return ServiceDiscoveryManager.getInstanceFor(connection).findService(PubSub.NAMESPACE,
|
||||||
|
true, "pubsub", "service");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO describe me.
|
* Listeners for Publish-Subscribe (XEP-60) events.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub.listener;
|
package org.jivesoftware.smackx.pubsub.listener;
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO describe me.
|
* Smack's API for XEP-60: Publish-Subscribe.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO describe me.
|
* Stanzas and extension elements for Publish-Subscribe (XEP-60).
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub.packet;
|
package org.jivesoftware.smackx.pubsub.packet;
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO describe me.
|
* Providers for Publish-Subscribe (XEP-60).
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub.provider;
|
package org.jivesoftware.smackx.pubsub.provider;
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO describe me.
|
* Utilities for Publish-Subscribe (XEP-60).
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub.util;
|
package org.jivesoftware.smackx.pubsub.util;
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class ConfigureFormTest
|
||||||
public void getConfigFormWithInsufficientPriviliges() throws XMPPException, SmackException, IOException, InterruptedException
|
public void getConfigFormWithInsufficientPriviliges() throws XMPPException, SmackException, IOException, InterruptedException
|
||||||
{
|
{
|
||||||
ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
|
ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = new PubSubManager(con, PubSubManagerTest.DUMMY_PUBSUB_SERVICE);
|
||||||
DiscoverInfo info = new DiscoverInfo();
|
DiscoverInfo info = new DiscoverInfo();
|
||||||
Identity ident = new Identity("pubsub", null, "leaf");
|
Identity ident = new Identity("pubsub", null, "leaf");
|
||||||
info.addIdentity(ident);
|
info.addIdentity(ident);
|
||||||
|
@ -81,7 +81,7 @@ public class ConfigureFormTest
|
||||||
public void getConfigFormWithTimeout() throws XMPPException, SmackException, InterruptedException, XmppStringprepException
|
public void getConfigFormWithTimeout() throws XMPPException, SmackException, InterruptedException, XmppStringprepException
|
||||||
{
|
{
|
||||||
ThreadedDummyConnection con = new ThreadedDummyConnection();
|
ThreadedDummyConnection con = new ThreadedDummyConnection();
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = new PubSubManager(con, PubSubManagerTest.DUMMY_PUBSUB_SERVICE);
|
||||||
DiscoverInfo info = new DiscoverInfo();
|
DiscoverInfo info = new DiscoverInfo();
|
||||||
Identity ident = new Identity("pubsub", null, "leaf");
|
Identity ident = new Identity("pubsub", null, "leaf");
|
||||||
info.addIdentity(ident);
|
info.addIdentity(ident);
|
||||||
|
|
|
@ -25,13 +25,29 @@ import org.jivesoftware.smack.ThreadedDummyConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
public class PubSubManagerTest {
|
public class PubSubManagerTest {
|
||||||
|
|
||||||
|
public static final DomainBareJid DUMMY_PUBSUB_SERVICE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
DomainBareJid pubSubService;
|
||||||
|
try {
|
||||||
|
pubSubService = JidCreate.domainBareFrom("pubsub.dummy.org");
|
||||||
|
}
|
||||||
|
catch (XmppStringprepException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
DUMMY_PUBSUB_SERVICE = pubSubService;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void deleteNodeTest() throws InterruptedException, SmackException, IOException, XMPPException {
|
public void deleteNodeTest() throws InterruptedException, SmackException, IOException, XMPPException {
|
||||||
ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
|
ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
|
||||||
PubSubManager mgr = new PubSubManager(con);
|
PubSubManager mgr = new PubSubManager(con, DUMMY_PUBSUB_SERVICE);
|
||||||
|
|
||||||
mgr.deleteNode("foo@bar.org");
|
mgr.deleteNode("foo@bar.org");
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2015 Florian Schmaus
|
||||||
|
*
|
||||||
|
* 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.pubsub;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
||||||
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
|
|
||||||
|
public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
private final PubSubManager pubSubManagerOne;
|
||||||
|
|
||||||
|
public PubSubIntegrationTest(SmackIntegrationTestEnvironment environment)
|
||||||
|
throws TestNotPossibleException, NoResponseException, XMPPErrorException,
|
||||||
|
NotConnectedException, InterruptedException {
|
||||||
|
super(environment);
|
||||||
|
DomainBareJid pubSubService = PubSubManager.getPubSubService(conOne);
|
||||||
|
if (pubSubService == null) {
|
||||||
|
throw new TestNotPossibleException("No PubSub service found");
|
||||||
|
}
|
||||||
|
pubSubManagerOne = PubSubManager.getInstance(conOne, pubSubService);
|
||||||
|
if (!pubSubManagerOne.canCreateNodesAndPublishItems()) {
|
||||||
|
throw new TestNotPossibleException("PubSub service does not allow node creation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void simplePubSubNodeTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
final String nodename = "sinttest-simple-nodename-" + testRunId;
|
||||||
|
final String itemId = "sintest-simple-itemid-" + testRunId;
|
||||||
|
LeafNode leafNode = pubSubManagerOne.createNode(nodename);
|
||||||
|
try {
|
||||||
|
leafNode.publish(new Item(itemId));
|
||||||
|
List<Item> items = leafNode.getItems();
|
||||||
|
assertEquals(1, items.size());
|
||||||
|
Item item = items.get(0);
|
||||||
|
assertEquals(itemId, item.getId());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
pubSubManagerOne.deleteNode(nodename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/package-info.java
|
Loading…
Reference in a new issue