1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-12-14 23:11:08 +01:00

Merge branch '4.2'

This commit is contained in:
Florian Schmaus 2017-05-25 10:39:59 +02:00
commit 7a5f9e6a03
187 changed files with 2284 additions and 588 deletions

View file

@ -139,7 +139,7 @@ public class Affiliation implements ExtensionElement
*/
public boolean isAffiliationModification() {
if (jid != null && affiliation != null) {
assert(node == null && namespace == PubSubNamespace.OWNER);
assert (node == null && namespace == PubSubNamespace.OWNER);
return true;
}
return false;

View file

@ -55,6 +55,6 @@ public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketE
if (getConfiguration() == null)
return Collections.emptyList();
else
return Arrays.asList(((ExtensionElement)getConfiguration().getDataFormToSend()));
return Arrays.asList(((ExtensionElement) getConfiguration().getDataFormToSend()));
}
}

View file

@ -61,7 +61,7 @@ public class EventElement implements EmbeddedPacketExtension
@Override
public List<ExtensionElement> getExtensions()
{
return Arrays.asList(new ExtensionElement[]{getEvent()});
return Arrays.asList(new ExtensionElement[] {getEvent()});
}
public NodeExtension getEvent()

View file

@ -134,7 +134,7 @@ public class ItemsExtension extends NodeExtension implements EmbeddedPacketExten
@SuppressWarnings("unchecked")
public List<ExtensionElement> getExtensions()
{
return (List<ExtensionElement>)getItems();
return (List<ExtensionElement>) getItems();
}
/**

View file

@ -303,7 +303,7 @@ public class LeafNode extends Node
public <T extends Item> void publish(T item) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
{
Collection<T> items = new ArrayList<T>(1);
items.add((item == null ? (T)new Item() : item));
items.add((item == null ? (T) new Item() : item));
publish(items);
}

View file

@ -573,7 +573,7 @@ abstract public class Node
private static List<String> getSubscriptionIds(Stanza packet)
{
HeadersExtension headers = (HeadersExtension)packet.getExtension("headers", "http://jabber.org/protocol/shim");
HeadersExtension headers = (HeadersExtension) packet.getExtension("headers", "http://jabber.org/protocol/shim");
List<String> values = null;
if (headers != null)
@ -608,10 +608,8 @@ abstract public class Node
@SuppressWarnings({ "rawtypes", "unchecked" })
public void processStanza(Stanza packet)
{
// CHECKSTYLE:OFF
EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
// CHECKSTYLE:ON
ItemsExtension itemsElem = (ItemsExtension)event.getEvent();
EventElement event = (EventElement) packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
ItemsExtension itemsElem = (ItemsExtension) event.getEvent();
ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), itemsElem.getItems(), getSubscriptionIds(packet), DelayInformationManager.getDelayTimestamp(packet));
listener.handlePublishedItems(eventItems);
}
@ -681,8 +679,8 @@ abstract public class Node
@Override
public void processStanza(Stanza packet)
{
EventElement event = (EventElement)packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
ConfigurationEvent config = (ConfigurationEvent)event.getEvent();
EventElement event = (EventElement) packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
ConfigurationEvent config = (ConfigurationEvent) event.getEvent();
listener.handleNodeConfiguration(config);
}
@ -735,7 +733,7 @@ abstract public class Node
if (embedEvent instanceof EmbeddedPacketExtension)
{
List<ExtensionElement> secondLevelList = ((EmbeddedPacketExtension)embedEvent).getExtensions();
List<ExtensionElement> secondLevelList = ((EmbeddedPacketExtension) embedEvent).getExtensions();
// XEP-0060 allows no elements on second level for notifications. See schema or
// for example § 4.3:

View file

@ -73,7 +73,7 @@ public enum PubSubElementType
public static PubSubElementType valueOfFromElemName(String elemName, String namespace)
{
int index = namespace.lastIndexOf('#');
String fragment = (index == -1 ? null : namespace.substring(index+1));
String fragment = (index == -1 ? null : namespace.substring(index + 1));
if (fragment != null)
{

View file

@ -0,0 +1,53 @@
/**
*
* Copyright 2017 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 org.jivesoftware.smack.SmackException;
import org.jxmpp.jid.BareJid;
public abstract class PubSubException extends SmackException {
/**
*
*/
private static final long serialVersionUID = 1L;
public static class NotALeafNodeException extends PubSubException {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String nodeId;
private final BareJid pubSubService;
NotALeafNodeException(String nodeId, BareJid pubSubService) {
this.nodeId = nodeId;
this.pubSubService = pubSubService;
}
public String getNodeId() {
return nodeId;
}
public BareJid getPubSubService() {
return pubSubService;
}
}
}

View file

@ -16,8 +16,6 @@
*/
package org.jivesoftware.smackx.pubsub;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -42,6 +40,7 @@ import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
@ -64,6 +63,8 @@ import org.jxmpp.stringprep.XmppStringprepException;
*/
public final class PubSubManager extends Manager {
public static final String AUTO_CREATE_FEATURE = "http://jabber.org/protocol/pubsub#auto-create";
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
private static final Map<XMPPConnection, Map<BareJid, PubSubManager>> INSTANCES = new WeakHashMap<>();
@ -267,10 +268,11 @@ public final class PubSubManager extends Manager {
* @throws NotConnectedException
* @throws InterruptedException
* @throws XMPPErrorException
* @throws NotALeafNodeException in case the node already exists as collection node.
* @since 4.2.1
*/
public LeafNode getOrCreateLeafNode(final String id)
throws NoResponseException, NotConnectedException, InterruptedException, XMPPErrorException {
throws NoResponseException, NotConnectedException, InterruptedException, XMPPErrorException, NotALeafNodeException {
try {
return getNode(id);
}
@ -287,42 +289,115 @@ public final class PubSubManager extends Manager {
throw e2;
}
}
if (e1.getXMPPError().getCondition() == Condition.service_unavailable) {
// This could be caused by Prosody bug #805 (see https://prosody.im/issues/issue/805). Prosody does not
// answer to disco#info requests on the node ID, which makes it undecidable if a node is a leaf or
// collection node.
LOGGER.warning("The PubSub service " + pubSubService
+ " threw an DiscoInfoNodeAssertionError, trying workaround for Prosody bug #805 (https://prosody.im/issues/issue/805)");
return getOrCreateLeafNodeProsodyWorkaround(id);
}
throw e1;
}
catch (PubSubAssertionError.DiscoInfoNodeAssertionError e) {
// This could be caused by Prosody bug #805 (see https://prosody.im/issues/issue/805). Prosody does not
// answer to disco#info requests on the node ID, which makes it undecidable if a node is a leaf or
// collection node.
LOGGER.warning("The PubSub service " + pubSubService
+ " threw an DiscoInfoNodeAssertionError, trying workaround for Prosody bug #805 (https://prosody.im/issues/issue/805)");
return getOrCreateLeafNodeProsodyWorkaround(id);
}
}
/**
* Try to get a leaf node with the given node ID.
*
* @param id the node ID.
* @return the requested leaf node.
* @throws NotALeafNodeException in case the node exists but is a collection node.
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
* @throws XMPPErrorException
* @since 4.2.1
*/
public LeafNode getLeafNode(String id) throws NotALeafNodeException, NoResponseException, NotConnectedException,
InterruptedException, XMPPErrorException {
Node node;
try {
node = getNode(id);
}
catch (XMPPErrorException e) {
if (e.getXMPPError().getCondition() == Condition.service_unavailable) {
// This could be caused by Prosody bug #805 (see https://prosody.im/issues/issue/805). Prosody does not
// answer to disco#info requests on the node ID, which makes it undecidable if a node is a leaf or
// collection node.
return getLeafNodeProsodyWorkaround(id);
}
throw e;
}
if (node instanceof LeafNode) {
return (LeafNode) node;
}
throw new PubSubException.NotALeafNodeException(id, pubSubService);
}
private LeafNode getLeafNodeProsodyWorkaround(final String id) throws NoResponseException, NotConnectedException,
InterruptedException, NotALeafNodeException, XMPPErrorException {
LeafNode leafNode = new LeafNode(this, id);
try {
// Try to ensure that this is not a collection node by asking for one item form the node.
leafNode.getItems(1);
} catch (XMPPErrorException e) {
Condition condition = e.getXMPPError().getCondition();
if (condition == Condition.feature_not_implemented) {
// XEP-0060 § 6.5.9.5: Item retrieval not supported, e.g. because node is a collection node
throw new PubSubException.NotALeafNodeException(id, pubSubService);
}
throw e;
}
nodeMap.put(id, leafNode);
return leafNode;
}
private LeafNode getOrCreateLeafNodeProsodyWorkaround(final String id)
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException {
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotALeafNodeException {
try {
return createNode(id);
}
catch (XMPPErrorException e1) {
if (e1.getXMPPError().getCondition() == Condition.conflict) {
Constructor<?> constructor = LeafNode.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
LeafNode res;
try {
res = (LeafNode) constructor.newInstance(this, id);
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e2) {
throw new AssertionError(e2);
}
// TODO: How to verify that this is actually a leafe node and not a conflict with a collection node?
return res;
return getLeafNodeProsodyWorkaround(id);
}
throw e1;
}
}
/**
* Try to publish an item and, if the node with the given ID does not exists, auto-create the node.
* <p>
* Not every PubSub service supports automatic node creation. You can discover if this service supports it by using
* {@link #supportsAutomaticNodeCreation()}.
* </p>
*
* @param id The unique id of the node.
* @param item The item to publish.
* @return the LeafNode on which the item was published.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.2.1
*/
public <I extends Item> LeafNode tryToPublishAndPossibleAutoCreate(String id, I item)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
LeafNode leafNode = new LeafNode(this, id);
leafNode.publish(item);
// If LeafNode.publish() did not throw then we have successfully published an item and possible auto-created
// (XEP-0163 § 3., XEP-0060 § 7.1.4) the node. So we can put the node into the nodeMap.
nodeMap.put(id, leafNode);
return leafNode;
}
/**
* Get all the nodes that currently exist as a child of the specified
* collection node. If the service does not support collection nodes
@ -440,6 +515,23 @@ public final class PubSubManager extends Manager {
return mgr.discoverInfo(pubSubService);
}
/**
* Check if the PubSub service supports automatic node creation.
*
* @return true if the PubSub service supports automatic node creation.
* @throws NoResponseException
* @throws XMPPErrorException
* @throws NotConnectedException
* @throws InterruptedException
* @since 4.2.1
* @see <a href="https://xmpp.org/extensions/xep-0060.html#publisher-publish-autocreate">XEP-0060 § 7.1.4 Automatic Node Creation</a>
*/
public boolean supportsAutomaticNodeCreation()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
return sdm.supportsFeature(pubSubService, AUTO_CREATE_FEATURE);
}
/**
* 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

View file

@ -61,7 +61,7 @@ public enum PubSubNamespace
if (index != -1)
{
String suffix = ns.substring(ns.lastIndexOf('#')+1);
String suffix = ns.substring(ns.lastIndexOf('#') + 1);
return valueOf(suffix.toUpperCase(Locale.US));
}
else

View file

@ -35,7 +35,7 @@ import org.jivesoftware.smackx.pubsub.AffiliationsExtension;
@Override
protected AffiliationsExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends ExtensionElement> content)
{
return new AffiliationsExtension((List<Affiliation>)content);
return new AffiliationsExtension((List<Affiliation>) content);
}
}

View file

@ -39,6 +39,6 @@ public class ConfigEventProvider extends EmbeddedExtensionProvider<Configuration
if (content.size() == 0)
return new ConfigurationEvent(attMap.get("node"));
else
return new ConfigurationEvent(attMap.get("node"), new ConfigureForm((DataForm)content.iterator().next()));
return new ConfigurationEvent(attMap.get("node"), new ConfigureForm((DataForm) content.iterator().next()));
}
}

View file

@ -36,6 +36,6 @@ public class EventProvider extends EmbeddedExtensionProvider<EventElement>
@Override
protected EventElement createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attMap, List<? extends ExtensionElement> content)
{
return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension)content.get(0));
return new EventElement(EventElementType.valueOf(content.get(0).getElementName()), (NodeExtension) content.get(0));
}
}

View file

@ -37,6 +37,6 @@ public class FormNodeProvider extends EmbeddedExtensionProvider<FormNode>
@Override
protected FormNode createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends ExtensionElement> content)
{
return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), new Form((DataForm)content.iterator().next()));
return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), new Form((DataForm) content.iterator().next()));
}
}

View file

@ -36,7 +36,7 @@ public class SubscriptionsProvider extends EmbeddedExtensionProvider<Subscriptio
@Override
protected SubscriptionsExtension createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends ExtensionElement> content)
{
return new SubscriptionsExtension(attributeMap.get("node"), (List<Subscription>)content);
return new SubscriptionsExtension(attributeMap.get("node"), (List<Subscription>) content);
}
}