/******************************************************************************* * Copyright (c) 2009 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: May 7, 2009 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.cache; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; /** * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>) * @param <K> * Type of key * @param <V> * Tyoe of values * */ public class SegmentedCache<K, V> implements ICache<K, V> { SoftReference<SpecialHashMap> softCacheLink; CopyOnWriteArraySet<ICacheListener<K, V>> listeners = new CopyOnWriteArraySet<ICacheListener<K, V>>(); LinkedList<SpecialHashMap> stack = new LinkedList<SpecialHashMap>(); int maxSize = 0; private final int numberOfSegments = 3; final Object lock = new Object(); long lastModCount = 0; @SuppressWarnings("unused") private final String cacheName; SegmentedCache(String cacheName, int maxSize) { this.cacheName = cacheName; this.maxSize = maxSize; softCacheLink = createSoftReference(null); } private SoftReference<SpecialHashMap> createSoftReference(SpecialHashMap map) { if (map == null) { map = new SpecialHashMap(); } return new SoftReference<SpecialHashMap>(map); } public void clear() { synchronized (lock) { stack.clear(); if (softCacheLink.get() == null) { softCacheLink = createSoftReference(null); return; } else { softCacheLink.get().clear(); } lastModCount = 0; } } void prune() { lastModCount++; synchronized (lock) { SpecialHashMap map = softCacheLink.get(); if (map != null) { if (map.size() > 0) { for (Map.Entry<K, V> entry : map.entrySet()) { for (ICacheListener<K, V> listener : listeners) { listener.elementRemoved(entry.getKey(), entry.getValue()); } } } map.clear(); } softCacheLink = createSoftReference(stack.size() > 0 ? stack.removeLast() : null); } } void reorg() { if (lastModCount > maxSize / ((numberOfSegments + 1) * 2)) { synchronized (lock) { if (softCacheLink.get() != null) { SpecialHashMap map = softCacheLink.get(); int needsMore = (maxSize / (numberOfSegments + 1)) - map.size(); if (needsMore > 0 && stack.size() > 0) { SpecialHashMap lastMap = stack.getLast(); while (lastMap != null && needsMore > 0) { for (Iterator<Map.Entry<K, V>> iterator = lastMap.entrySet().iterator(); needsMore-- > 0 && iterator.hasNext();) { Map.Entry<K, V> entry = iterator.next(); map.put(entry.getKey(), entry.getValue()); iterator.remove(); } if (lastMap.size() == 0) { stack.removeLast(); } lastMap = stack.getLast(); } } } lastModCount = 0; } } } public V get(K key) { synchronized (lock) { if (stack.size() > 0) { for (HashMap<K, V> map : stack) { V v = map.get(key); if (v != null) { if (map != stack.getFirst()) { map.remove(key); stack.getFirst().put(key, v); } return v; } } } if (softCacheLink.get() == null) { softCacheLink = createSoftReference(stack.size() > 0 ? stack.removeLast() : null); return null; } return softCacheLink.get().get(key); } } public V remove(K key) { lastModCount++; synchronized (lock) { if (stack.size() > 0) { HashMap<K, V> mapToRemove = null; try { for (HashMap<K, V> map : stack) { V v = map.remove(key); if (v != null) { if (map.size() == 0) { mapToRemove = map; } return v; } } } finally { if (mapToRemove != null) { stack.remove(mapToRemove); } } } if (softCacheLink.get() == null) { softCacheLink = createSoftReference(stack.size() > 0 ? stack.removeLast() : null); return null; } V v = softCacheLink.get().remove(key); if (v != null) { if (softCacheLink.get().size() == 0 && stack.size() > 0) { softCacheLink = createSoftReference(stack.removeLast()); } } return v; } } public Set<K> keySet() { synchronized (lock) { HashSet<K> keys = new HashSet<K>(); for (HashMap<K, V> map : stack) { keys.addAll(map.keySet()); } if (softCacheLink.get() == null) { softCacheLink = createSoftReference(stack.size() > 0 ? stack.removeLast() : null); } else { keys.addAll(softCacheLink.get().keySet()); } return keys; } } public V put(K key, V value) { lastModCount++; synchronized (lock) { remove(key); if (stack.size() > 0) { SpecialHashMap map = stack.getFirst(); if (map.size() > (maxSize / (numberOfSegments + 1))) { map = new SpecialHashMap(); stack.push(map); if (stack.size() > numberOfSegments) { softCacheLink = createSoftReference(stack.removeLast()); } } if (softCacheLink.get() == null) { softCacheLink = createSoftReference(stack.size() > 0 ? stack.removeLast() : null); } return map.put(key, value); } else { if (softCacheLink.get() == null) { softCacheLink = createSoftReference(null); } return softCacheLink.get().put(key, value); } } } public void registerListener(ICacheListener<K, V> listener) { listeners.add(listener); } public void unregisterListener(ICacheListener<K, V> listener) { listeners.remove(listener); } class SpecialHashMap extends HashMap<K, V> { private static final long serialVersionUID = 72943522689159363L; @Override protected void finalize() throws Throwable { if (size() > 0) { for (Map.Entry<K, V> entry : entrySet()) { for (ICacheListener<K, V> listener : listeners) { listener.elementRemoved(entry.getKey(), entry.getValue()); } } } super.finalize(); } } }