From e6a51f21518481e265eb9e38e40dd56d07f624f4 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 19 Apr 2017 11:44:01 +0200 Subject: [PATCH] Add PubSubManager.getLeafNode() and improve prosody workaround by using the protected constructor and not reflection. --- .../smackx/pubsub/PubSubException.java | 53 +++++++++++++ .../smackx/pubsub/PubSubManager.java | 77 +++++++++++++++---- 2 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubException.java diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubException.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubException.java new file mode 100644 index 000000000..eb885981a --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubException.java @@ -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; + } + + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java index 02578077b..663fc308d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java @@ -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; @@ -269,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); } @@ -301,25 +301,70 @@ public final class PubSubManager extends Manager { } } + /** + * 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) { + + } + throw e; + } + + if (node instanceof LeafNode) { + return (LeafNode) node; + } + + throw new PubSubException.NotALeafNodeException(id, pubSubService); + } + + private LeafNode getLeafNodeProsoydWorkaround(final String id) throws NoResponseException, NotConnectedException, InterruptedException, NotALeafNodeException { + 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(); + switch (condition) { + // XEP-0060 § 6.5.9.11: Node does not exist + case item_not_found: + return null; + // XEP-0060 § 6.5.9.5: Item retrieval not supported, e.g. because node is a collection node + case feature_not_implemented: + throw new PubSubException.NotALeafNodeException(id, pubSubService); + default: + break; + } + } + + 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 getLeafNodeProsoydWorkaround(id); } throw e1; }