diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index ffe84d231..5d35cf1b5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -59,6 +59,8 @@ import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache; import org.jivesoftware.smackx.caps.packet.CapsExtension; import org.jivesoftware.smackx.disco.AbstractNodeInformationProvider; +import org.jivesoftware.smackx.disco.DiscoInfoLookupShortcutMechanism; +import org.jivesoftware.smackx.disco.EntityCapabilitiesChangedListener; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Feature; @@ -128,6 +130,36 @@ public final class EntityCapsManager extends Manager { } catch (NoSuchAlgorithmException e) { // Ignore } + + ServiceDiscoveryManager.addDiscoInfoLookupShortcutMechanism(new DiscoInfoLookupShortcutMechanism("XEP-0115: Entity Capabilities", 100) { + @Override + public DiscoverInfo getDiscoverInfoByUser(ServiceDiscoveryManager serviceDiscoveryManager, Jid jid) { + DiscoverInfo info = EntityCapsManager.getDiscoverInfoByUser(jid); + if (info != null) { + return info; + } + + NodeVerHash nodeVerHash = getNodeVerHashByJid(jid); + if (nodeVerHash == null) { + return null; + } + + try { + info = serviceDiscoveryManager.discoverInfo(jid, nodeVerHash.getNodeVer()); + } catch (NoResponseException | XMPPErrorException | NotConnectedException | InterruptedException e) { + // TODO log + return null; + } + + if (verifyDiscoverInfoVersion(nodeVerHash.getVer(), nodeVerHash.getHash(), info)) { + addDiscoverInfoByNode(nodeVerHash.getNodeVer(), info); + } else { + // TODO log + } + + return info; + } + }); } /** @@ -148,7 +180,7 @@ public final class EntityCapsManager extends Manager { * @param info * DiscoverInfo for the specified node. */ - public static void addDiscoverInfoByNode(String nodeVer, DiscoverInfo info) { + static void addDiscoverInfoByNode(String nodeVer, DiscoverInfo info) { CAPS_CACHE.put(nodeVer, info); if (persistentCache != null) @@ -365,7 +397,16 @@ public final class EntityCapsManager extends Manager { connection.addStanzaInterceptor(packetInterceptor, PresenceTypeFilter.AVAILABLE); // It's important to do this as last action. Since it changes the // behavior of the SDM in some ways - sdm.setEntityCapsManager(this); + sdm.addEntityCapabilitiesChangedListener(new EntityCapabilitiesChangedListener() { + @Override + public void onEntityCapailitiesChanged() { + if (!entityCapsEnabled()) { + return; + } + + updateLocalEntityCaps(); + } + }); } public static synchronized EntityCapsManager getInstanceFor(XMPPConnection connection) { @@ -473,7 +514,7 @@ public final class EntityCapsManager extends Manager { * presence is send to inform others about your new Entity Caps node string. * */ - public void updateLocalEntityCaps() { + private void updateLocalEntityCaps() { XMPPConnection connection = connection(); DiscoverInfo discoverInfo = new DiscoverInfo(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java new file mode 100644 index 000000000..1404f97e8 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java @@ -0,0 +1,54 @@ +/** + * + * Copyright 2018 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.disco; + +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; + +import org.jxmpp.jid.Jid; + +public abstract class DiscoInfoLookupShortcutMechanism implements Comparable { + + private final String name; + private final int priority; + + protected DiscoInfoLookupShortcutMechanism(String name, int priority) { + this.name = name; + this.priority = priority; + } + + public final String getName() { + return name; + } + + /** + * Get the priority of this mechanism. Lower values mean higher priority. + * + * @return the priority of this mechanism. + */ + public final int getPriority() { + return priority; + } + + public abstract DiscoverInfo getDiscoverInfoByUser(ServiceDiscoveryManager serviceDiscoveryManager, Jid jid); + + @Override + public final int compareTo(DiscoInfoLookupShortcutMechanism other) { + // Switch to Integer.compare(int, int) once Smack is on Android 19 or higher. + Integer ourPriority = getPriority(); + return ourPriority.compareTo(other.getPriority()); + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java new file mode 100644 index 000000000..e075117f2 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java @@ -0,0 +1,23 @@ +/** + * + * Copyright 2018 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.disco; + +public interface EntityCapabilitiesChangedListener { + + void onEntityCapailitiesChanged(); + +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index bb04d50c4..eabc7773f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.Manager; @@ -44,7 +45,6 @@ import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.caps.EntityCapsManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; import org.jivesoftware.smackx.disco.packet.DiscoverItems; @@ -74,13 +74,15 @@ public final class ServiceDiscoveryManager extends Manager { private static final String DEFAULT_IDENTITY_CATEGORY = "client"; private static final String DEFAULT_IDENTITY_TYPE = "pc"; + private static final List discoInfoLookupShortcutMechanisms = new ArrayList<>(2); + private static DiscoverInfo.Identity defaultIdentity = new Identity(DEFAULT_IDENTITY_CATEGORY, DEFAULT_IDENTITY_NAME, DEFAULT_IDENTITY_TYPE); private final Set identities = new HashSet<>(); private DiscoverInfo.Identity identity = defaultIdentity; - private EntityCapsManager capsManager; + private final Set entityCapabilitiesChangedListeners = new CopyOnWriteArraySet<>(); private static final Map instances = new WeakHashMap<>(); @@ -488,30 +490,19 @@ public final class ServiceDiscoveryManager extends Manager { if (entityID == null) return discoverInfo(null, null); - // Check if the have it cached in the Entity Capabilities Manager - DiscoverInfo info = EntityCapsManager.getDiscoverInfoByUser(entityID); - - if (info != null) { - // We were able to retrieve the information from Entity Caps and - // avoided a disco request, hurray! - return info; + synchronized (discoInfoLookupShortcutMechanisms) { + for (DiscoInfoLookupShortcutMechanism discoInfoLookupShortcutMechanism : discoInfoLookupShortcutMechanisms) { + DiscoverInfo info = discoInfoLookupShortcutMechanism.getDiscoverInfoByUser(this, entityID); + if (info != null) { + // We were able to retrieve the information from Entity Caps and + // avoided a disco request, hurray! + return info; + } + } } - // Try to get the newest node#version if it's known, otherwise null is - // returned - EntityCapsManager.NodeVerHash nvh = EntityCapsManager.getNodeVerHashByJid(entityID); - - // Discover by requesting the information from the remote entity - // Note that wee need to use NodeVer as argument for Node if it exists - info = discoverInfo(entityID, nvh != null ? nvh.getNodeVer() : null); - - // If the node version is known, store the new entry. - if (nvh != null) { - if (EntityCapsManager.verifyDiscoverInfoVersion(nvh.getVer(), nvh.getHash(), info)) - EntityCapsManager.addDiscoverInfoByNode(nvh.getNodeVer(), info); - } - - return info; + // Last resort: Standard discovery. + return discoverInfo(entityID, null); } /** @@ -933,24 +924,29 @@ public final class ServiceDiscoveryManager extends Manager { return findService(feature, useCache, null, null); } - /** - * Entity Capabilities - */ - - /** - * Loads the ServiceDiscoveryManager with an EntityCapsManger that speeds up certain lookups. - * - * @param manager - */ - public void setEntityCapsManager(EntityCapsManager manager) { - capsManager = manager; + public boolean addEntityCapabilitiesChangedListener(EntityCapabilitiesChangedListener entityCapabilitiesChangedListener) { + return entityCapabilitiesChangedListeners.add(entityCapabilitiesChangedListener); } /** - * Updates the Entity Capabilities Verification String if EntityCaps is enabled. + * Notify the {@link EntityCapabilitiesChangedListener} about changed capabilities. */ private void renewEntityCapsVersion() { - if (capsManager != null && capsManager.entityCapsEnabled()) - capsManager.updateLocalEntityCaps(); + for (EntityCapabilitiesChangedListener entityCapabilitiesChangedListener : entityCapabilitiesChangedListeners) { + entityCapabilitiesChangedListener.onEntityCapailitiesChanged(); + } + } + + public static void addDiscoInfoLookupShortcutMechanism(DiscoInfoLookupShortcutMechanism discoInfoLookupShortcutMechanism) { + synchronized (discoInfoLookupShortcutMechanisms) { + discoInfoLookupShortcutMechanisms.add(discoInfoLookupShortcutMechanism); + Collections.sort(discoInfoLookupShortcutMechanisms); + } + } + + public static void removeDiscoInfoLookupShortcutMechanism(DiscoInfoLookupShortcutMechanism discoInfoLookupShortcutMechanism) { + synchronized (discoInfoLookupShortcutMechanisms) { + discoInfoLookupShortcutMechanisms.remove(discoInfoLookupShortcutMechanism); + } } }