| 1 | package org.jtoolkit.essence.data.impl; |
| 2 | |
| 3 | import org.jetbrains.annotations.NotNull; |
| 4 | import org.jetbrains.annotations.Nullable; |
| 5 | import org.jtoolkit.essence.app.pojo.impl.DataValueClass; |
| 6 | import org.jtoolkit.essence.concurrency.Concurrency; |
| 7 | import org.jtoolkit.essence.concurrency.ThreadSafe; |
| 8 | import org.jtoolkit.essence.data.Store; |
| 9 | import org.jtoolkit.essence.data.Transaction; |
| 10 | import static org.jtoolkit.essence.data.Transaction.include; |
| 11 | import static org.jtoolkit.essence.data.Transaction.isTransactional; |
| 12 | import org.jtoolkit.essence.data.Visitor; |
| 13 | import org.jtoolkit.essence.data.VisitorException; |
| 14 | import org.jtoolkit.essence.utils.RWLock; |
| 15 | |
| 16 | import java.util.*; |
| 17 | import java.util.concurrent.ConcurrentHashMap; |
| 18 | import java.util.concurrent.ConcurrentMap; |
| 19 | import java.util.concurrent.locks.Lock; |
| 20 | import java.util.concurrent.locks.ReadWriteLock; |
| 21 | /* |
| 22 | Copyright 2006 Peter Lawrey |
| 23 | |
| 24 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 25 | you may not use this file except in compliance with the License. |
| 26 | You may obtain a copy of the License at |
| 27 | |
| 28 | http://www.apache.org/licenses/LICENSE-2.0 |
| 29 | |
| 30 | Unless required by applicable law or agreed to in writing, software |
| 31 | distributed under the License is distributed on an "AS IS" BASIS, |
| 32 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 33 | See the License for the specific language governing permissions and |
| 34 | limitations under the License. |
| 35 | */ |
| 36 | |
| 37 | /** |
| 38 | * @author Peter Lawrey |
| 39 | * @noinspection unchecked,RedundantCast,SuspiciousMethodCalls |
| 40 | */ |
| 41 | @ThreadSafe(Concurrency.CONCURRENT_READ_WRITE) |
| 42 | public class MemoryStore<K, V> extends AbstractStore<K, V> { |
| 43 | private final ConcurrentMap<K, V> map = new ConcurrentHashMap<K, V>(256, 0.5f, 64); |
| 44 | private final ReadWriteLock lock = RWLock.createLock("memoryStore"); |
| 45 | @Nullable private final SortedSet<K> queue; |
| 46 | |
| 47 | // Note used in container-test. |
| 48 | public MemoryStore(@NotNull String name, @NotNull CollectionType collectionType, @Nullable DataValueClass<K> keyClass, @NotNull DataValueClass<V> valueClass) { |
| 49 | this(name, collectionType, keyClass, valueClass, ReadMode.NONE, PersistMode.NONE); |
| 50 | } |
| 51 | |
| 52 | protected MemoryStore(@NotNull String name, @NotNull CollectionType collectionType, @Nullable DataValueClass<K> keyClass, @NotNull DataValueClass<V> valueClass, @NotNull ReadMode readMode, @NotNull PersistMode persistMode) { |
| 53 | super(name, collectionType, keyClass, valueClass, new ListenerSetImpl(), readMode, persistMode); |
| 54 | listenerSet.notifyReset(true); |
| 55 | if (isQueue) |
| 56 | queue = new TreeSet<K>(); |
| 57 | else |
| 58 | queue = null; |
| 59 | } |
| 60 | |
| 61 | // accessible via JMX/reflection. |
| 62 | protected int getSize() { |
| 63 | Lock nonexclusiveLock = lock.readLock(); |
| 64 | try { |
| 65 | nonexclusiveLock.lock(); |
| 66 | return map.size(); |
| 67 | } finally { |
| 68 | nonexclusiveLock.unlock(); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | @NotNull public Map<K, V> asMap() { |
| 73 | Lock nonexclusiveLock = lock.readLock(); |
| 74 | try { |
| 75 | nonexclusiveLock.lock(); |
| 76 | return new LinkedHashMap<K, V>(map); |
| 77 | } finally { |
| 78 | nonexclusiveLock.unlock(); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | @SuppressWarnings({"SuspiciousMethodCalls"}) |
| 83 | protected boolean containsKey(@NotNull Object key) { |
| 84 | Lock nonexclusiveLock = lock.readLock(); |
| 85 | try { |
| 86 | nonexclusiveLock.lock(); |
| 87 | return map.containsKey(key); |
| 88 | } finally { |
| 89 | nonexclusiveLock.unlock(); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | protected boolean containsValue(@NotNull Object value) { |
| 94 | Lock nonexclusiveLock = lock.readLock(); |
| 95 | try { |
| 96 | nonexclusiveLock.lock(); |
| 97 | return map.containsValue(value); |
| 98 | } finally { |
| 99 | nonexclusiveLock.unlock(); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | protected void clear() { |
| 104 | Lock exclusiveLock = lock.writeLock(); |
| 105 | try { |
| 106 | exclusiveLock.lock(); |
| 107 | map.clear(); |
| 108 | } finally { |
| 109 | exclusiveLock.unlock(); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | @Nullable protected V first() { |
| 114 | Lock exclusiveLock = lock.writeLock(); |
| 115 | try { |
| 116 | exclusiveLock.lock(); |
| 117 | if (map.isEmpty()) |
| 118 | return null; |
| 119 | return map.get(queue == null ? map.keySet().iterator().next() : queue.first()); |
| 120 | } finally { |
| 121 | exclusiveLock.unlock(); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | @Nullable protected K firstKey() { |
| 126 | Lock exclusiveLock = lock.writeLock(); |
| 127 | try { |
| 128 | exclusiveLock.lock(); |
| 129 | if (map.isEmpty()) |
| 130 | return null; |
| 131 | return queue == null ? map.keySet().iterator().next() : queue.first(); |
| 132 | } finally { |
| 133 | exclusiveLock.unlock(); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | @Nullable protected V get(Object key) { |
| 138 | // don't check for speed and to allow a get() after closed. |
| 139 | // checkKey(key); |
| 140 | Lock nonexclusiveLock = lock.readLock(); |
| 141 | try { |
| 142 | nonexclusiveLock.lock(); |
| 143 | return map.get(key); |
| 144 | } finally { |
| 145 | nonexclusiveLock.unlock(); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | @NotNull protected Set<K> keySet() { |
| 150 | Lock exclusiveLock = lock.writeLock(); |
| 151 | try { |
| 152 | exclusiveLock.lock(); |
| 153 | return map.keySet(); |
| 154 | } finally { |
| 155 | exclusiveLock.unlock(); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | @Nullable protected V doPut(K key, V value, boolean ifAbsent, boolean ifPresent) { |
| 160 | checkWrite(); |
| 161 | checkKey(key); |
| 162 | checkValue(value); |
| 163 | boolean exclusive = !ifAbsent || !ifPresent; |
| 164 | Lock lock2 = exclusive ? lock.writeLock() : lock.readLock(); |
| 165 | try { |
| 166 | lock2.lock(); |
| 167 | // get the previous if used. |
| 168 | V prev = exclusive || isTransactional() ? map.get(key) : null; |
| 169 | if (prev == null && !ifAbsent) |
| 170 | return null; |
| 171 | if (prev != null && !ifPresent) |
| 172 | return null; |
| 173 | if (isTransactional()) { |
| 174 | Map<String, Map> mapOfKeys = new HashMap<String, Map>(); |
| 175 | Map<K, V> map2 = new HashMap<K, V>(); |
| 176 | map2.put(key, value); |
| 177 | mapOfKeys.put(getName(), map2); |
| 178 | include(changeCallback, mapOfKeys, null); |
| 179 | return prev; |
| 180 | } |
| 181 | |
| 182 | put0(key, value); |
| 183 | return prev; |
| 184 | } finally { |
| 185 | lock2.unlock(); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | @Nullable protected V doRemove(@NotNull K key, boolean byValue, @Nullable V value) { |
| 190 | checkWrite(); |
| 191 | try { |
| 192 | checkKey(key); |
| 193 | if (byValue) |
| 194 | checkValue(value); |
| 195 | } catch (IllegalArgumentException ignored) { |
| 196 | return null; |
| 197 | } |
| 198 | |
| 199 | Lock lock2 = byValue ? lock.writeLock() : lock.readLock(); |
| 200 | try { |
| 201 | lock2.lock(); |
| 202 | |
| 203 | V prev = byValue || isTransactional() ? map.get(key) : null; |
| 204 | if (byValue && !equals2(prev, value)) |
| 205 | return null; |
| 206 | if (isTransactional()) { |
| 207 | Map<String, Map> mapOfKeys = new HashMap<String, Map>(); |
| 208 | Map<K, V> map2 = new HashMap<K, V>(); |
| 209 | map2.put((K) key, null); |
| 210 | mapOfKeys.put(name, map2); |
| 211 | include(changeCallback, mapOfKeys, null); |
| 212 | return prev; |
| 213 | } |
| 214 | |
| 215 | return remove(key); |
| 216 | } catch (Exception e) { |
| 217 | throw new IllegalStateException(e); |
| 218 | } finally { |
| 219 | lock2.unlock(); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | protected void put0(K key, V value) { |
| 224 | V prev = map.put(key, value); |
| 225 | if (queue != null && prev == null) |
| 226 | queue.add(key); |
| 227 | notifyUpdate(key, value); |
| 228 | } |
| 229 | |
| 230 | @Nullable private V remove(@NotNull Object key) { |
| 231 | Lock nonexclusiveLock = lock.readLock(); |
| 232 | V value; |
| 233 | try { |
| 234 | nonexclusiveLock.lock(); |
| 235 | if (queue != null) queue.remove(key); |
| 236 | value = map.remove(key); |
| 237 | } finally { |
| 238 | nonexclusiveLock.unlock(); |
| 239 | } |
| 240 | notifyUpdate((K) key, null); |
| 241 | return value; |
| 242 | } |
| 243 | |
| 244 | @Nullable protected V removeFirst() { |
| 245 | Lock exclusiveLock = lock.writeLock(); |
| 246 | try { |
| 247 | exclusiveLock.lock(); |
| 248 | if (map.isEmpty()) |
| 249 | return null; |
| 250 | K key = queue == null ? map.keySet().iterator().next() : queue.first(); |
| 251 | return remove(key); |
| 252 | } finally { |
| 253 | exclusiveLock.unlock(); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | @NotNull protected Collection<V> values() { |
| 258 | Lock exclusiveLock = lock.writeLock(); |
| 259 | try { |
| 260 | exclusiveLock.lock(); |
| 261 | return map.values(); |
| 262 | } finally { |
| 263 | exclusiveLock.unlock(); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | @NotNull protected Collection<V> orderedValues() { |
| 268 | List<V> ret = new ArrayList<V>(); |
| 269 | Lock exclusiveLock = lock.writeLock(); |
| 270 | try { |
| 271 | exclusiveLock.lock(); |
| 272 | if (queue == null) |
| 273 | return map.values(); |
| 274 | for(K key: queue) |
| 275 | ret.add(map.get(key)); |
| 276 | return ret; |
| 277 | } finally { |
| 278 | exclusiveLock.unlock(); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | @Nullable public <R> R visit(@NotNull Visitor<Store<K, V>, R> visitor) throws VisitorException { |
| 283 | Transaction t = Transaction.start("visit MemoryStore"); |
| 284 | Lock exclusiveLock = lock.writeLock(); |
| 285 | try { |
| 286 | exclusiveLock.lock(); |
| 287 | R r = visitor.visit(this); |
| 288 | t.commit(); |
| 289 | return r; |
| 290 | } finally { |
| 291 | t.complete(); |
| 292 | exclusiveLock.unlock(); |
| 293 | } |
| 294 | } |
| 295 | } |