Improve Entity Capabilities code

Most importantly, rename reload() method of persistent cache to
"DiscoverInfo lookup(String nodeVer)" and lazily load the data from the
persistent cache to the memory cache.
This commit is contained in:
Florian Schmaus 2014-05-25 17:49:17 +02:00
parent 47d9146c76
commit 6dd180e9d3
4 changed files with 67 additions and 57 deletions

View File

@ -62,7 +62,6 @@ import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.io.IOException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -97,7 +96,7 @@ public class EntityCapsManager extends Manager {
/** /**
* Map of (node + '#" + hash algorithm) to DiscoverInfo data * Map of (node + '#" + hash algorithm) to DiscoverInfo data
*/ */
protected static Map<String, DiscoverInfo> caps = new Cache<String, DiscoverInfo>(1000, -1); private static final Cache<String, DiscoverInfo> CAPS_CACHE = new Cache<String, DiscoverInfo>(1000, -1);
/** /**
* Map of Full JID -&gt; DiscoverInfo/null. In case of c2s connection the * Map of Full JID -&gt; DiscoverInfo/null. In case of c2s connection the
@ -105,7 +104,7 @@ public class EntityCapsManager extends Manager {
* link-local connection the key is formed as user@host (no resource) In * link-local connection the key is formed as user@host (no resource) In
* case of a server or component the key is formed as domain * case of a server or component the key is formed as domain
*/ */
protected static Map<String, NodeVerHash> jidCaps = new Cache<String, NodeVerHash>(10000, -1); private static final Cache<String, NodeVerHash> JID_TO_NODEVER_CACHE = new Cache<String, NodeVerHash>(10000, -1);
static { static {
XMPPConnection.addConnectionCreationListener(new ConnectionCreationListener() { XMPPConnection.addConnectionCreationListener(new ConnectionCreationListener() {
@ -141,7 +140,7 @@ public class EntityCapsManager extends Manager {
* DiscoverInfo for the specified node. * DiscoverInfo for the specified node.
*/ */
public static void addDiscoverInfoByNode(String nodeVer, DiscoverInfo info) { public static void addDiscoverInfoByNode(String nodeVer, DiscoverInfo info) {
caps.put(nodeVer, info); CAPS_CACHE.put(nodeVer, info);
if (persistentCache != null) if (persistentCache != null)
persistentCache.addDiscoverInfoByNodePersistent(nodeVer, info); persistentCache.addDiscoverInfoByNodePersistent(nodeVer, info);
@ -156,7 +155,7 @@ public class EntityCapsManager extends Manager {
* @return the node version (node#ver) or null * @return the node version (node#ver) or null
*/ */
public static String getNodeVersionByJid(String jid) { public static String getNodeVersionByJid(String jid) {
NodeVerHash nvh = jidCaps.get(jid); NodeVerHash nvh = JID_TO_NODEVER_CACHE.get(jid);
if (nvh != null) { if (nvh != null) {
return nvh.nodeVer; return nvh.nodeVer;
} else { } else {
@ -165,7 +164,7 @@ public class EntityCapsManager extends Manager {
} }
public static NodeVerHash getNodeVerHashByJid(String jid) { public static NodeVerHash getNodeVerHashByJid(String jid) {
return jidCaps.get(jid); return JID_TO_NODEVER_CACHE.get(jid);
} }
/** /**
@ -178,7 +177,7 @@ public class EntityCapsManager extends Manager {
* @return the discovered info * @return the discovered info
*/ */
public static DiscoverInfo getDiscoverInfoByUser(String user) { public static DiscoverInfo getDiscoverInfoByUser(String user) {
NodeVerHash nvh = jidCaps.get(user); NodeVerHash nvh = JID_TO_NODEVER_CACHE.get(user);
if (nvh == null) if (nvh == null)
return null; return null;
@ -194,7 +193,18 @@ public class EntityCapsManager extends Manager {
* @return The corresponding DiscoverInfo or null if none is known. * @return The corresponding DiscoverInfo or null if none is known.
*/ */
public static DiscoverInfo getDiscoveryInfoByNodeVer(String nodeVer) { public static DiscoverInfo getDiscoveryInfoByNodeVer(String nodeVer) {
DiscoverInfo info = caps.get(nodeVer); DiscoverInfo info = CAPS_CACHE.get(nodeVer);
// If it was not in CAPS_CACHE, try to retrieve the information from persistentCache
if (info == null) {
info = persistentCache.lookup(nodeVer);
// Promote the information to CAPS_CACHE if one was found
if (info != null) {
CAPS_CACHE.put(nodeVer, info);
}
}
// If we were able to retrieve information from one of the caches, copy it before returning
if (info != null) if (info != null)
info = new DiscoverInfo(info); info = new DiscoverInfo(info);
@ -205,40 +215,37 @@ public class EntityCapsManager extends Manager {
* Set the persistent cache implementation * Set the persistent cache implementation
* *
* @param cache * @param cache
* @throws IOException
*/ */
public static void setPersistentCache(EntityCapsPersistentCache cache) throws IOException { public static void setPersistentCache(EntityCapsPersistentCache cache) {
if (persistentCache != null)
throw new IllegalStateException("Entity Caps Persistent Cache was already set");
persistentCache = cache; persistentCache = cache;
persistentCache.replay();
} }
/** /**
* Sets the maximum Cache size for the JID to nodeVer Cache * Sets the maximum cache sizes
* *
* @param maxCacheSize * @param maxJidToNodeVerSize
* @param maxCapsCacheSize
*/ */
@SuppressWarnings("rawtypes") public static void setMaxsCacheSizes(int maxJidToNodeVerSize, int maxCapsCacheSize) {
public static void setJidCapsMaxCacheSize(int maxCacheSize) { JID_TO_NODEVER_CACHE.setMaxCacheSize(maxJidToNodeVerSize);
((Cache) jidCaps).setMaxCacheSize(maxCacheSize); CAPS_CACHE.setMaxCacheSize(maxCapsCacheSize);
} }
/** /**
* Sets the maximum Cache size for the nodeVer to DiscoverInfo Cache * Clears the memory cache.
*
* @param maxCacheSize
*/ */
@SuppressWarnings("rawtypes") public static void clearMemoryCache() {
public static void setCapsMaxCacheSize(int maxCacheSize) { JID_TO_NODEVER_CACHE.clear();
((Cache) caps).setMaxCacheSize(maxCacheSize); CAPS_CACHE.clear();
} }
private ServiceDiscoveryManager sdm; private final Queue<String> lastLocalCapsVersions = new ConcurrentLinkedQueue<String>();
private final ServiceDiscoveryManager sdm;
private boolean entityCapsEnabled; private boolean entityCapsEnabled;
private String currentCapsVersion; private String currentCapsVersion;
private boolean presenceSend = false; private boolean presenceSend = false;
private Queue<String> lastLocalCapsVersions = new ConcurrentLinkedQueue<String>();
/** /**
* The entity node String used by this EntityCapsManager instance. * The entity node String used by this EntityCapsManager instance.
@ -286,7 +293,7 @@ public class EntityCapsManager extends Manager {
String node = ext.getNode(); String node = ext.getNode();
String ver = ext.getVer(); String ver = ext.getVer();
jidCaps.put(from, new NodeVerHash(node, ver, hash)); JID_TO_NODEVER_CACHE.put(from, new NodeVerHash(node, ver, hash));
} }
}, PRESENCES_WITH_CAPS); }, PRESENCES_WITH_CAPS);
@ -297,7 +304,7 @@ public class EntityCapsManager extends Manager {
// always remove the JID from the map, even if entityCaps are // always remove the JID from the map, even if entityCaps are
// disabled // disabled
String from = packet.getFrom(); String from = packet.getFrom();
jidCaps.remove(from); JID_TO_NODEVER_CACHE.remove(from);
} }
}, PRESENCES_WITHOUT_CAPS); }, PRESENCES_WITHOUT_CAPS);
@ -367,7 +374,7 @@ public class EntityCapsManager extends Manager {
* the user (Full JID) * the user (Full JID)
*/ */
public void removeUserCapsNode(String user) { public void removeUserCapsNode(String user) {
jidCaps.remove(user); JID_TO_NODEVER_CACHE.remove(user);
} }
/** /**
@ -441,9 +448,9 @@ public class EntityCapsManager extends Manager {
} }
lastLocalCapsVersions.add(currentCapsVersion); lastLocalCapsVersions.add(currentCapsVersion);
caps.put(currentCapsVersion, discoverInfo); CAPS_CACHE.put(currentCapsVersion, discoverInfo);
if (connection != null) if (connection != null)
jidCaps.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion, "sha-1")); JID_TO_NODEVER_CACHE.put(connection.getUser(), new NodeVerHash(entityNode, currentCapsVersion, "sha-1"));
final List<Identity> identities = new LinkedList<Identity>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities()); final List<Identity> identities = new LinkedList<Identity>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities());
sdm.setNodeInformationProvider(entityNode + '#' + currentCapsVersion, new NodeInformationProvider() { sdm.setNodeInformationProvider(entityNode + '#' + currentCapsVersion, new NodeInformationProvider() {

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2011-2013 Florian Schmaus * Copyright © 2011-2014 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,23 +16,21 @@
*/ */
package org.jivesoftware.smackx.caps.cache; package org.jivesoftware.smackx.caps.cache;
import java.io.IOException;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
public interface EntityCapsPersistentCache { public interface EntityCapsPersistentCache {
/** /**
* Add an DiscoverInfo to the persistent Cache * Add an DiscoverInfo to the persistent Cache
* *
* @param node * @param nodeVer
* @param info * @param info
*/ */
void addDiscoverInfoByNodePersistent(String node, DiscoverInfo info); void addDiscoverInfoByNodePersistent(String nodeVer, DiscoverInfo info);
/** /**
* Replay the Caches data into EntityCapsManager * Lookup DiscoverInfo by a Node string
*/ */
void replay() throws IOException; DiscoverInfo lookup(String nodeVer);
/** /**
* Empty the Cache * Empty the Cache

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright © 2011 Florian Schmaus * Copyright © 2011-2014 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,7 +31,6 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider; import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.Base32Encoder; import org.jivesoftware.smack.util.Base32Encoder;
import org.jivesoftware.smack.util.StringEncoder; import org.jivesoftware.smack.util.StringEncoder;
import org.jivesoftware.smackx.caps.EntityCapsManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider; import org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
@ -87,9 +86,8 @@ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache
} }
@Override @Override
public void addDiscoverInfoByNodePersistent(String node, DiscoverInfo info) { public void addDiscoverInfoByNodePersistent(String nodeVer, DiscoverInfo info) {
String filename = filenameEncoder.encode(node); File nodeFile = getFileFor(nodeVer);
File nodeFile = new File(cacheDir, filename);
try { try {
if (nodeFile.createNewFile()) if (nodeFile.createNewFile())
writeInfoToFile(nodeFile, info); writeInfoToFile(nodeFile, info);
@ -99,18 +97,28 @@ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache
} }
@Override @Override
public void replay() throws IOException { public DiscoverInfo lookup(String nodeVer) {
File[] files = cacheDir.listFiles(); File nodeFile = getFileFor(nodeVer);
for (File f : files) { if (!nodeFile.isFile()) {
String node = filenameEncoder.decode(f.getName()); return null;
DiscoverInfo info = restoreInfoFromFile(f);
if (info == null)
continue;
EntityCapsManager.addDiscoverInfoByNode(node, info);
} }
DiscoverInfo info = null;
try {
info = restoreInfoFromFile(nodeFile);
}
catch (IOException e) {
LOGGER.log(Level.WARNING, "Coud not restore info from file", e);
}
return info;
} }
private File getFileFor(String nodeVer) {
String filename = filenameEncoder.encode(nodeVer);
File nodeFile = new File(cacheDir, filename);
return nodeFile;
}
@Override
public void emptyCache() { public void emptyCache() {
File[] files = cacheDir.listFiles(); File[] files = cacheDir.listFiles();
for (File f : files) { for (File f : files) {

View File

@ -94,10 +94,7 @@ public class EntityCapsManagerTest {
EntityCapsManager.addDiscoverInfoByNode(nodeVer, di); EntityCapsManager.addDiscoverInfoByNode(nodeVer, di);
// Lose all the data // Lose all the data
EntityCapsManager.caps.clear(); EntityCapsManager.clearMemoryCache();
// Restore the data from the persistent Cache
cache.replay();
DiscoverInfo restored_di = EntityCapsManager.getDiscoveryInfoByNodeVer(nodeVer); DiscoverInfo restored_di = EntityCapsManager.getDiscoveryInfoByNodeVer(nodeVer);
assertNotNull(restored_di); assertNotNull(restored_di);