mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 22:32:06 +01:00
Remove smack.util.Cache
and use the Cache from jxmpp-util-cache instead.
This commit is contained in:
parent
9016120d71
commit
f2718c2d76
4 changed files with 9 additions and 753 deletions
|
@ -1,746 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2005 Jive Software.
|
|
||||||
*
|
|
||||||
* 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.smack.util;
|
|
||||||
|
|
||||||
import java.util.AbstractCollection;
|
|
||||||
import java.util.AbstractSet;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A specialized Map that is size-limited (using an LRU algorithm) and
|
|
||||||
* has an optional expiration time for cache items. The Map is thread-safe.<p>
|
|
||||||
*
|
|
||||||
* The algorithm for cache is as follows: a HashMap is maintained for fast
|
|
||||||
* object lookup. Two linked lists are maintained: one keeps objects in the
|
|
||||||
* order they are accessed from cache, the other keeps objects in the order
|
|
||||||
* they were originally added to cache. When objects are added to cache, they
|
|
||||||
* are first wrapped by a CacheObject which maintains the following pieces
|
|
||||||
* of information:<ul>
|
|
||||||
* <li> A pointer to the node in the linked list that maintains accessed
|
|
||||||
* order for the object. Keeping a reference to the node lets us avoid
|
|
||||||
* linear scans of the linked list.
|
|
||||||
* <li> A pointer to the node in the linked list that maintains the age
|
|
||||||
* of the object in cache. Keeping a reference to the node lets us avoid
|
|
||||||
* linear scans of the linked list.</ul>
|
|
||||||
* <p/>
|
|
||||||
* To get an object from cache, a hash lookup is performed to get a reference
|
|
||||||
* to the CacheObject that wraps the real object we are looking for.
|
|
||||||
* The object is subsequently moved to the front of the accessed linked list
|
|
||||||
* and any necessary cache cleanups are performed. Cache deletion and expiration
|
|
||||||
* is performed as needed.
|
|
||||||
*
|
|
||||||
* @author Matt Tucker
|
|
||||||
*/
|
|
||||||
public class Cache<K, V> implements Map<K, V> {
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(Cache.class.getName());
|
|
||||||
/**
|
|
||||||
* The map the keys and values are stored in.
|
|
||||||
*/
|
|
||||||
protected Map<K, CacheObject<V>> map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Linked list to maintain order that cache objects are accessed
|
|
||||||
* in, most used to least used.
|
|
||||||
*/
|
|
||||||
protected LinkedList lastAccessedList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Linked list to maintain time that cache objects were initially added
|
|
||||||
* to the cache, most recently added to oldest added.
|
|
||||||
*/
|
|
||||||
protected LinkedList ageList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of items the cache will hold.
|
|
||||||
*/
|
|
||||||
protected int maxCacheSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum length of time objects can exist in cache before expiring.
|
|
||||||
*/
|
|
||||||
protected long maxLifetime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maintain the number of cache hits and misses. A cache hit occurs every
|
|
||||||
* time the get method is called and the cache contains the requested
|
|
||||||
* object. A cache miss represents the opposite occurence.<p>
|
|
||||||
*
|
|
||||||
* Keeping track of cache hits and misses lets one measure how efficient
|
|
||||||
* the cache is; the higher the percentage of hits, the more efficient.
|
|
||||||
*/
|
|
||||||
protected long cacheHits, cacheMisses = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new cache and specify the maximum size of for the cache in
|
|
||||||
* bytes, and the maximum lifetime of objects.
|
|
||||||
*
|
|
||||||
* @param maxSize the maximum number of objects the cache will hold. -1
|
|
||||||
* means the cache has no max size.
|
|
||||||
* @param maxLifetime the maximum amount of time (in ms) objects can exist in
|
|
||||||
* cache before being deleted. -1 means objects never expire.
|
|
||||||
*/
|
|
||||||
public Cache(int maxSize, long maxLifetime) {
|
|
||||||
if (maxSize == 0) {
|
|
||||||
throw new IllegalArgumentException("Max cache size cannot be 0.");
|
|
||||||
}
|
|
||||||
this.maxCacheSize = maxSize;
|
|
||||||
this.maxLifetime = maxLifetime;
|
|
||||||
|
|
||||||
// Our primary data structure is a hash map. The default capacity of 11
|
|
||||||
// is too small in almost all cases, so we set it bigger.
|
|
||||||
map = new HashMap<K, CacheObject<V>>(103);
|
|
||||||
|
|
||||||
lastAccessedList = new LinkedList();
|
|
||||||
ageList = new LinkedList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized V put(K key, V value) {
|
|
||||||
V oldValue = null;
|
|
||||||
// Delete an old entry if it exists.
|
|
||||||
if (map.containsKey(key)) {
|
|
||||||
oldValue = remove(key, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheObject<V> cacheObject = new CacheObject<V>(value);
|
|
||||||
map.put(key, cacheObject);
|
|
||||||
// Make an entry into the cache order list.
|
|
||||||
// Store the cache order list entry so that we can get back to it
|
|
||||||
// during later lookups.
|
|
||||||
cacheObject.lastAccessedListNode = lastAccessedList.addFirst(key);
|
|
||||||
// Add the object to the age list
|
|
||||||
LinkedListNode ageNode = ageList.addFirst(key);
|
|
||||||
ageNode.timestamp = System.currentTimeMillis();
|
|
||||||
cacheObject.ageListNode = ageNode;
|
|
||||||
|
|
||||||
// If cache is too full, remove least used cache entries until it is not too full.
|
|
||||||
cullCache();
|
|
||||||
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized V get(Object key) {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
CacheObject<V> cacheObject = map.get(key);
|
|
||||||
if (cacheObject == null) {
|
|
||||||
// The object didn't exist in cache, so increment cache misses.
|
|
||||||
cacheMisses++;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Remove the object from it's current place in the cache order list,
|
|
||||||
// and re-insert it at the front of the list.
|
|
||||||
cacheObject.lastAccessedListNode.remove();
|
|
||||||
lastAccessedList.addFirst(cacheObject.lastAccessedListNode);
|
|
||||||
|
|
||||||
// The object exists in cache, so increment cache hits. Also, increment
|
|
||||||
// the object's read count.
|
|
||||||
cacheHits++;
|
|
||||||
cacheObject.readCount++;
|
|
||||||
|
|
||||||
return cacheObject.object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized V remove(Object key) {
|
|
||||||
return remove(key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove operation with a flag so we can tell coherence if the remove was
|
|
||||||
* caused by cache internal processing such as eviction or loading
|
|
||||||
*/
|
|
||||||
public synchronized V remove(Object key, boolean internal) {
|
|
||||||
//noinspection SuspiciousMethodCalls
|
|
||||||
CacheObject<V> cacheObject = map.remove(key);
|
|
||||||
// If the object is not in cache, stop trying to remove it.
|
|
||||||
if (cacheObject == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Remove from the cache order list
|
|
||||||
cacheObject.lastAccessedListNode.remove();
|
|
||||||
cacheObject.ageListNode.remove();
|
|
||||||
// Remove references to linked list nodes
|
|
||||||
cacheObject.ageListNode = null;
|
|
||||||
cacheObject.lastAccessedListNode = null;
|
|
||||||
|
|
||||||
return cacheObject.object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void clear() {
|
|
||||||
Object[] keys = map.keySet().toArray();
|
|
||||||
for (Object key : keys) {
|
|
||||||
remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, reset all containers.
|
|
||||||
map.clear();
|
|
||||||
lastAccessedList.clear();
|
|
||||||
ageList.clear();
|
|
||||||
|
|
||||||
cacheHits = 0;
|
|
||||||
cacheMisses = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized int size() {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
return map.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean isEmpty() {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
return map.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Collection<V> values() {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
return Collections.unmodifiableCollection(new AbstractCollection<V>() {
|
|
||||||
Collection<CacheObject<V>> values = map.values();
|
|
||||||
public Iterator<V> iterator() {
|
|
||||||
return new Iterator<V>() {
|
|
||||||
Iterator<CacheObject<V>> it = values.iterator();
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return it.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public V next() {
|
|
||||||
return it.next().object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return values.size();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean containsKey(Object key) {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
return map.containsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void putAll(Map<? extends K, ? extends V> map) {
|
|
||||||
for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
|
||||||
V value = entry.getValue();
|
|
||||||
// If the map is another DefaultCache instance than the
|
|
||||||
// entry values will be CacheObject instances that need
|
|
||||||
// to be converted to the normal object form.
|
|
||||||
if (value instanceof CacheObject) {
|
|
||||||
//noinspection unchecked
|
|
||||||
value = ((CacheObject<V>) value).object;
|
|
||||||
}
|
|
||||||
put(entry.getKey(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean containsValue(Object value) {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
CacheObject<V> cacheObject = new CacheObject<V>((V) value);
|
|
||||||
|
|
||||||
return map.containsValue(cacheObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Set<Map.Entry<K, V>> entrySet() {
|
|
||||||
// Warning -- this method returns CacheObject instances and not Objects
|
|
||||||
// in the same form they were put into cache.
|
|
||||||
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
return new AbstractSet<Map.Entry<K, V>>() {
|
|
||||||
private final Set<Map.Entry<K, CacheObject<V>>> set = map.entrySet();
|
|
||||||
|
|
||||||
public Iterator<Entry<K, V>> iterator() {
|
|
||||||
return new Iterator<Entry<K, V>>() {
|
|
||||||
private final Iterator<Entry<K, CacheObject<V>>> it = set.iterator();
|
|
||||||
public boolean hasNext() {
|
|
||||||
return it.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entry<K, V> next() {
|
|
||||||
Map.Entry<K, CacheObject<V>> entry = it.next();
|
|
||||||
return new AbstractMapEntry<K, V>(entry.getKey(), entry.getValue().object) {
|
|
||||||
@Override
|
|
||||||
public V setValue(V value) {
|
|
||||||
throw new UnsupportedOperationException("Cannot set");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return set.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Set<K> keySet() {
|
|
||||||
// First, clear all entries that have been in cache longer than the
|
|
||||||
// maximum defined age.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
|
|
||||||
return Collections.unmodifiableSet(map.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCacheHits() {
|
|
||||||
return cacheHits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCacheMisses() {
|
|
||||||
return cacheMisses;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxCacheSize() {
|
|
||||||
return maxCacheSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setMaxCacheSize(int maxCacheSize) {
|
|
||||||
this.maxCacheSize = maxCacheSize;
|
|
||||||
// It's possible that the new max size is smaller than our current cache
|
|
||||||
// size. If so, we need to delete infrequently used items.
|
|
||||||
cullCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMaxLifetime() {
|
|
||||||
return maxLifetime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxLifetime(long maxLifetime) {
|
|
||||||
this.maxLifetime = maxLifetime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all entries out of cache where the entries are older than the
|
|
||||||
* maximum defined age.
|
|
||||||
*/
|
|
||||||
protected synchronized void deleteExpiredEntries() {
|
|
||||||
// Check if expiration is turned on.
|
|
||||||
if (maxLifetime <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all old entries. To do this, we remove objects from the end
|
|
||||||
// of the linked list until they are no longer too old. We get to avoid
|
|
||||||
// any hash lookups or looking at any more objects than is strictly
|
|
||||||
// neccessary.
|
|
||||||
LinkedListNode node = ageList.getLast();
|
|
||||||
// If there are no entries in the age list, return.
|
|
||||||
if (node == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the expireTime, which is the moment in time that elements
|
|
||||||
// should expire from cache. Then, we can do an easy check to see
|
|
||||||
// if the expire time is greater than the expire time.
|
|
||||||
long expireTime = System.currentTimeMillis() - maxLifetime;
|
|
||||||
|
|
||||||
while (expireTime > node.timestamp) {
|
|
||||||
if (remove(node.object, true) == null) {
|
|
||||||
LOGGER.warning("Error attempting to remove(" + node.object.toString() + ") - cacheObject not found in cache!");
|
|
||||||
// remove from the ageList
|
|
||||||
node.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the next node.
|
|
||||||
node = ageList.getLast();
|
|
||||||
// If there are no more entries in the age list, return.
|
|
||||||
if (node == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the least recently used elements if the cache size is greater than
|
|
||||||
* or equal to the maximum allowed size until the cache is at least 10% empty.
|
|
||||||
*/
|
|
||||||
protected synchronized void cullCache() {
|
|
||||||
// Check if a max cache size is defined.
|
|
||||||
if (maxCacheSize < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if the cache is too big. If so, clean out cache until it's 10% free.
|
|
||||||
if (map.size() > maxCacheSize) {
|
|
||||||
// First, delete any old entries to see how much memory that frees.
|
|
||||||
deleteExpiredEntries();
|
|
||||||
// Next, delete the least recently used elements until 10% of the cache
|
|
||||||
// has been freed.
|
|
||||||
int desiredSize = (int) (maxCacheSize * .90);
|
|
||||||
for (int i=map.size(); i>desiredSize; i--) {
|
|
||||||
// Get the key and invoke the remove method on it.
|
|
||||||
if (remove(lastAccessedList.getLast().object, true) == null) {
|
|
||||||
LOGGER.warning("Error attempting to cullCache with remove(" + lastAccessedList.getLast().object.toString() + ") - cacheObject not found in cache!");
|
|
||||||
lastAccessedList.getLast().remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for all objects put into cache. It's primary purpose is to maintain
|
|
||||||
* references to the linked lists that maintain the creation time of the object
|
|
||||||
* and the ordering of the most used objects.
|
|
||||||
*
|
|
||||||
* This class is optimized for speed rather than strictly correct encapsulation.
|
|
||||||
*/
|
|
||||||
private static class CacheObject<V> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Underlying object wrapped by the CacheObject.
|
|
||||||
*/
|
|
||||||
public V object;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reference to the node in the cache order list. We keep the reference
|
|
||||||
* here to avoid linear scans of the list. Every time the object is
|
|
||||||
* accessed, the node is removed from its current spot in the list and
|
|
||||||
* moved to the front.
|
|
||||||
*/
|
|
||||||
public LinkedListNode lastAccessedListNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reference to the node in the age order list. We keep the reference
|
|
||||||
* here to avoid linear scans of the list. The reference is used if the
|
|
||||||
* object has to be deleted from the list.
|
|
||||||
*/
|
|
||||||
public LinkedListNode ageListNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A count of the number of times the object has been read from cache.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public int readCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new cache object wrapper.
|
|
||||||
*
|
|
||||||
* @param object the underlying Object to wrap.
|
|
||||||
*/
|
|
||||||
public CacheObject(V object) {
|
|
||||||
this.object = object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!(o instanceof CacheObject)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CacheObject<?> cacheObject = (CacheObject<?>) o;
|
|
||||||
|
|
||||||
return object.equals(cacheObject.object);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode() {
|
|
||||||
return object.hashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple LinkedList implementation. The main feature is that list nodes
|
|
||||||
* are public, which allows very fast delete operations when one has a
|
|
||||||
* reference to the node that is to be deleted.<p>
|
|
||||||
*/
|
|
||||||
private static class LinkedList {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root of the list keeps a reference to both the first and last
|
|
||||||
* elements of the list.
|
|
||||||
*/
|
|
||||||
private LinkedListNode head = new LinkedListNode("head", null, null);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new linked list.
|
|
||||||
*/
|
|
||||||
public LinkedList() {
|
|
||||||
head.next = head.previous = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first linked list node in the list.
|
|
||||||
*
|
|
||||||
* @return the first element of the list.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public LinkedListNode getFirst() {
|
|
||||||
LinkedListNode node = head.next;
|
|
||||||
if (node == head) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the last linked list node in the list.
|
|
||||||
*
|
|
||||||
* @return the last element of the list.
|
|
||||||
*/
|
|
||||||
public LinkedListNode getLast() {
|
|
||||||
LinkedListNode node = head.previous;
|
|
||||||
if (node == head) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a node to the beginning of the list.
|
|
||||||
*
|
|
||||||
* @param node the node to add to the beginning of the list.
|
|
||||||
* @return the node
|
|
||||||
*/
|
|
||||||
public LinkedListNode addFirst(LinkedListNode node) {
|
|
||||||
node.next = head.next;
|
|
||||||
node.previous = head;
|
|
||||||
node.previous.next = node;
|
|
||||||
node.next.previous = node;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an object to the beginning of the list by automatically creating a
|
|
||||||
* a new node and adding it to the beginning of the list.
|
|
||||||
*
|
|
||||||
* @param object the object to add to the beginning of the list.
|
|
||||||
* @return the node created to wrap the object.
|
|
||||||
*/
|
|
||||||
public LinkedListNode addFirst(Object object) {
|
|
||||||
LinkedListNode node = new LinkedListNode(object, head.next, head);
|
|
||||||
node.previous.next = node;
|
|
||||||
node.next.previous = node;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an object to the end of the list by automatically creating a
|
|
||||||
* a new node and adding it to the end of the list.
|
|
||||||
*
|
|
||||||
* @param object the object to add to the end of the list.
|
|
||||||
* @return the node created to wrap the object.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public LinkedListNode addLast(Object object) {
|
|
||||||
LinkedListNode node = new LinkedListNode(object, head, head.previous);
|
|
||||||
node.previous.next = node;
|
|
||||||
node.next.previous = node;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Erases all elements in the list and re-initializes it.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
//Remove all references in the list.
|
|
||||||
LinkedListNode node = getLast();
|
|
||||||
while (node != null) {
|
|
||||||
node.remove();
|
|
||||||
node = getLast();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Re-initialize.
|
|
||||||
head.next = head.previous = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a String representation of the linked list with a comma
|
|
||||||
* delimited list of all the elements in the list.
|
|
||||||
*
|
|
||||||
* @return a String representation of the LinkedList.
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
LinkedListNode node = head.next;
|
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
while (node != head) {
|
|
||||||
buf.append(node.toString()).append(", ");
|
|
||||||
node = node.next;
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Doubly linked node in a LinkedList. Most LinkedList implementations keep the
|
|
||||||
* equivalent of this class private. We make it public so that references
|
|
||||||
* to each node in the list can be maintained externally.
|
|
||||||
*
|
|
||||||
* Exposing this class lets us make remove operations very fast. Remove is
|
|
||||||
* built into this class and only requires two reference reassignments. If
|
|
||||||
* remove existed in the main LinkedList class, a linear scan would have to
|
|
||||||
* be performed to find the correct node to delete.
|
|
||||||
*
|
|
||||||
* The linked list implementation was specifically written for the Jive
|
|
||||||
* cache system. While it can be used as a general purpose linked list, for
|
|
||||||
* most applications, it is more suitable to use the linked list that is part
|
|
||||||
* of the Java Collections package.
|
|
||||||
*/
|
|
||||||
private static class LinkedListNode {
|
|
||||||
|
|
||||||
public LinkedListNode previous;
|
|
||||||
public LinkedListNode next;
|
|
||||||
public Object object;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is further customized for the Jive cache system. It
|
|
||||||
* maintains a timestamp of when a Cacheable object was first added to
|
|
||||||
* cache. Timestamps are stored as long values and represent the number
|
|
||||||
* of milliseconds passed since January 1, 1970 00:00:00.000 GMT.<p>
|
|
||||||
*
|
|
||||||
* The creation timestamp is used in the case that the cache has a
|
|
||||||
* maximum lifetime set. In that case, when
|
|
||||||
* [current time] - [creation time] > [max lifetime], the object will be
|
|
||||||
* deleted from cache.
|
|
||||||
*/
|
|
||||||
public long timestamp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new linked list node.
|
|
||||||
*
|
|
||||||
* @param object the Object that the node represents.
|
|
||||||
* @param next a reference to the next LinkedListNode in the list.
|
|
||||||
* @param previous a reference to the previous LinkedListNode in the list.
|
|
||||||
*/
|
|
||||||
public LinkedListNode(Object object, LinkedListNode next,
|
|
||||||
LinkedListNode previous)
|
|
||||||
{
|
|
||||||
this.object = object;
|
|
||||||
this.next = next;
|
|
||||||
this.previous = previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes this node from the linked list that it is a part of.
|
|
||||||
*/
|
|
||||||
public void remove() {
|
|
||||||
previous.next = next;
|
|
||||||
next.previous = previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a String representation of the linked list node by calling the
|
|
||||||
* toString method of the node's object.
|
|
||||||
*
|
|
||||||
* @return a String representation of the LinkedListNode.
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
return object.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class AbstractMapEntry<K, V> implements Map.Entry<K, V> {
|
|
||||||
final K key;
|
|
||||||
V value;
|
|
||||||
|
|
||||||
AbstractMapEntry(K key, V value) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public K getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V setValue(V value) {
|
|
||||||
V answer = this.value;
|
|
||||||
this.value = value;
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares this Map Entry with another Map Entry.
|
|
||||||
* <p/>
|
|
||||||
* Implemented per API documentation of {@link java.util.Map.Entry#equals(Object)}
|
|
||||||
*
|
|
||||||
* @param obj the object to compare to
|
|
||||||
* @return true if equal key and value
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj == this) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj instanceof Map.Entry == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Map.Entry other = (Map.Entry) obj;
|
|
||||||
return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey()))
|
|
||||||
&& (getValue() == null ? other.getValue() == null : getValue().equals(
|
|
||||||
other.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a hashCode compatible with the equals method.
|
|
||||||
* <p/>
|
|
||||||
* Implemented per API documentation of {@link java.util.Map.Entry#hashCode()}
|
|
||||||
*
|
|
||||||
* @return a suitable hash code
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return (getKey() == null ? 0 : getKey().hashCode())
|
|
||||||
^ (getValue() == null ? 0 : getValue().hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,10 +27,11 @@ import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
import org.jivesoftware.smack.util.Cache;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
|
import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
|
||||||
|
import org.jxmpp.util.cache.Cache;
|
||||||
|
import org.jxmpp.util.cache.ExpirationCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Socks5BytestreamRequest class handles incoming SOCKS5 Bytestream requests.
|
* Socks5BytestreamRequest class handles incoming SOCKS5 Bytestream requests.
|
||||||
|
@ -46,7 +47,7 @@ public class Socks5BytestreamRequest implements BytestreamRequest {
|
||||||
private static final int BLACKLIST_MAX_SIZE = 100;
|
private static final int BLACKLIST_MAX_SIZE = 100;
|
||||||
|
|
||||||
/* blacklist of addresses of SOCKS5 proxies */
|
/* blacklist of addresses of SOCKS5 proxies */
|
||||||
private static final Cache<String, Integer> ADDRESS_BLACKLIST = new Cache<String, Integer>(
|
private static final Cache<String, Integer> ADDRESS_BLACKLIST = new ExpirationCache<String, Integer>(
|
||||||
BLACKLIST_MAX_SIZE, BLACKLIST_LIFETIME);
|
BLACKLIST_MAX_SIZE, BLACKLIST_LIFETIME);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.jivesoftware.smack.filter.PacketFilter;
|
||||||
import org.jivesoftware.smack.filter.AndFilter;
|
import org.jivesoftware.smack.filter.AndFilter;
|
||||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||||
import org.jivesoftware.smack.filter.PacketExtensionFilter;
|
import org.jivesoftware.smack.filter.PacketExtensionFilter;
|
||||||
import org.jivesoftware.smack.util.Cache;
|
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||||
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
||||||
import org.jivesoftware.smackx.caps.packet.CapsExtension;
|
import org.jivesoftware.smackx.caps.packet.CapsExtension;
|
||||||
|
@ -47,6 +46,7 @@ import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
import org.jxmpp.util.cache.LruCache;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -96,7 +96,7 @@ public class EntityCapsManager extends Manager {
|
||||||
/**
|
/**
|
||||||
* Map of (node + '#" + hash algorithm) to DiscoverInfo data
|
* Map of (node + '#" + hash algorithm) to DiscoverInfo data
|
||||||
*/
|
*/
|
||||||
private static final Cache<String, DiscoverInfo> CAPS_CACHE = new Cache<String, DiscoverInfo>(1000, -1);
|
private static final LruCache<String, DiscoverInfo> CAPS_CACHE = new LruCache<String, DiscoverInfo>(1000);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of Full JID -> DiscoverInfo/null. In case of c2s connection the
|
* Map of Full JID -> DiscoverInfo/null. In case of c2s connection the
|
||||||
|
@ -104,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
|
||||||
*/
|
*/
|
||||||
private static final Cache<String, NodeVerHash> JID_TO_NODEVER_CACHE = new Cache<String, NodeVerHash>(10000, -1);
|
private static final LruCache<String, NodeVerHash> JID_TO_NODEVER_CACHE = new LruCache<String, NodeVerHash>(10000);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
|
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||||
|
|
|
@ -32,12 +32,13 @@ import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Packet;
|
import org.jivesoftware.smack.packet.Packet;
|
||||||
import org.jivesoftware.smack.packet.PacketExtension;
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
import org.jivesoftware.smack.util.Cache;
|
|
||||||
import org.jivesoftware.smackx.caps.EntityCapsManager;
|
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.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
import org.jxmpp.util.cache.Cache;
|
||||||
|
import org.jxmpp.util.cache.ExpirationCache;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -682,7 +683,7 @@ public 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<String>> services = new Cache<String, List<String>>(25,
|
private Cache<String, List<String>> services = new ExpirationCache<String, List<String>>(25,
|
||||||
24 * 60 * 60 * 1000);
|
24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue