001/** 002 * 003 * Copyright © 2014-2016 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jxmpp.util.cache; 018 019import java.util.Collection; 020import java.util.HashSet; 021import java.util.Map; 022import java.util.Set; 023 024public class ExpirationCache<K, V> implements Cache<K, V>, Map<K, V>{ 025 026 private final LruCache<K, ExpireElement<V>> cache; 027 028 private long defaultExpirationTime; 029 030 /** 031 * Construct a new expiration cache. 032 * 033 * @param maxSize the maximum size. 034 * @param defaultExpirationTime the default expiration time in milliseconds. 035 */ 036 public ExpirationCache(int maxSize, long defaultExpirationTime) { 037 cache = new LruCache<K, ExpireElement<V>>(maxSize); 038 setDefaultExpirationTime(defaultExpirationTime); 039 } 040 041 /** 042 * Set the default expiration time in milliseconds. 043 * 044 * @param defaultExpirationTime the default expiration time. 045 */ 046 public void setDefaultExpirationTime(long defaultExpirationTime) { 047 if (defaultExpirationTime <= 0) { 048 throw new IllegalArgumentException(); 049 } 050 this.defaultExpirationTime = defaultExpirationTime; 051 } 052 053 @Override 054 public V put(K key, V value) { 055 return put(key, value, defaultExpirationTime); 056 } 057 058 /** 059 * Put a value in the cahce with the specified expiration time in milliseconds. 060 * 061 * @param key the key of the value. 062 * @param value the value. 063 * @param expirationTime the expiration time in milliseconds. 064 * @return the previous value or {@code null}. 065 */ 066 public V put(K key, V value, long expirationTime) { 067 ExpireElement<V> eOld = cache.put(key, new ExpireElement<V>(value, expirationTime)); 068 if (eOld == null) { 069 return null; 070 } 071 return eOld.element; 072 } 073 074 @Override 075 public V lookup(K key) { 076 return get(key); 077 } 078 079 @SuppressWarnings("deprecation") 080 @Deprecated 081 @Override 082 public V get(Object key) { 083 ExpireElement<V> v = cache.get(key); 084 if (v == null) { 085 return null; 086 } 087 if (v.isExpired()) { 088 remove(key); 089 return null; 090 } 091 return v.element; 092 } 093 094 /** 095 * Remove a entry with the given key from the cache. 096 * 097 * @param key the key of the value to remove. 098 * @return the remove value, or {@code null}. 099 */ 100 @Override 101 public V remove(Object key) { 102 ExpireElement<V> e = cache.remove(key); 103 if (e == null) { 104 return null; 105 } 106 return e.element; 107 } 108 109 @Override 110 public int getMaxCacheSize() { 111 return cache.getMaxCacheSize(); 112 } 113 114 @Override 115 public void setMaxCacheSize(int maxCacheSize) { 116 cache.setMaxCacheSize(maxCacheSize); 117 } 118 119 private static class ExpireElement<V> { 120 private final V element; 121 private final long expirationTimestamp; 122 123 private ExpireElement(V element, long expirationTime) { 124 this.element = element; 125 this.expirationTimestamp = System.currentTimeMillis() + expirationTime; 126 } 127 128 private boolean isExpired() { 129 return System.currentTimeMillis() > expirationTimestamp; 130 } 131 132 @Override 133 public int hashCode() { 134 return element.hashCode(); 135 } 136 137 @Override 138 public boolean equals(Object other) { 139 if (!(other instanceof ExpireElement)) 140 return false; 141 ExpireElement<?> otherElement = (ExpireElement<?>) other; 142 return element.equals(otherElement.element); 143 } 144 } 145 146 @Override 147 public int size() { 148 return cache.size(); 149 } 150 151 @Override 152 public boolean isEmpty() { 153 return cache.isEmpty(); 154 } 155 156 @Override 157 public boolean containsKey(Object key) { 158 return cache.containsKey(key); 159 } 160 161 @Override 162 public boolean containsValue(Object value) { 163 return cache.containsValue(value); 164 } 165 166 @Override 167 public void putAll(Map<? extends K, ? extends V> m) { 168 for (Entry<? extends K, ? extends V> entry : m.entrySet()) { 169 put(entry.getKey(), entry.getValue()); 170 } 171 } 172 173 @Override 174 public void clear() { 175 cache.clear(); 176 } 177 178 @Override 179 public Set<K> keySet() { 180 return cache.keySet(); 181 } 182 183 @Override 184 public Collection<V> values() { 185 Set<V> res = new HashSet<V>(); 186 for (ExpireElement<V> value : cache.values()) { 187 res.add(value.element); 188 } 189 return res; 190 } 191 192 @Override 193 public Set<java.util.Map.Entry<K, V>> entrySet() { 194 Set<Entry<K, V>> res = new HashSet<Entry<K, V>>(); 195 for (Entry<K, ExpireElement<V>> entry : cache.entrySet()) { 196 res.add(new EntryImpl<K, V>(entry.getKey(), entry.getValue().element)); 197 } 198 return res; 199 } 200 201 private static class EntryImpl<K, V> implements Entry<K, V> { 202 203 private final K key; 204 private V value; 205 206 EntryImpl(K key, V value) { 207 this.key = key; 208 this.value = value; 209 } 210 @Override 211 public K getKey() { 212 return key; 213 } 214 215 @Override 216 public V getValue() { 217 return value; 218 } 219 220 @Override 221 public V setValue(V value) { 222 V oldValue = this.value; 223 this.value = value; 224 return oldValue; 225 } 226 } 227}