|
Trouver une ressource
Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !
SPRITES CACHE MANAGER POUR GESTION MÉMOIRE DE GRAND NOMBRE DE SPRITES DANS VOTRE APPLICATION (SOFTVALUES)
Information sur la source
Description
Une classe de gestion de cache utile pour ceux qui aiment les applications ou les jeux avec bitmaps ou autre photos en temps réel. Pour l'instant je ne sais pas si cette classe supporte vraiment tout type de situation. ;) Elle gère parfaitement des petites animations en bitmaps ou dessins avec transparence. >> :::::... :.... ::::::: ;;;::: 2006 (ajout: key file I/O, ou la gestion de la sécurité avec des fichiers sérialisés en clé; c.f. static File makeKeyFile(Serializable[], Serializable[], String, String, boolean)) (ajout: sept 2007 - le cache est à présent pleinement synchronisé sur les vues de collections de clefs et valeurs (aka Collections-views). de plus les accès en lecture/ecriture sont protégé par deux moniteurs de synchronisation. et enfin, les Reference's sont castées sur l'objet à référencer, ainsi que pour le pool d'allocation ReferenceQueue. )
Source
- package sf3.system;
-
- import installer.ExampleFileFilter;
- import java.io.EOFException;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.OptionalDataException;
- import java.io.OutputStream;
- import java.io.RandomAccessFile;
- import java.io.Serializable;
- import java.lang.ref.Reference;
- import java.lang.ref.ReferenceQueue;
- import java.lang.ref.SoftReference;
- import java.lang.reflect.InvocationTargetException;
- import java.util.*;
- import java.util.concurrent.ConcurrentMap;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipInputStream;
- import java.util.zip.ZipOutputStream;
- import sf3.system.Threaded;
-
- /** Management of cache for many objects, thus the errors of java heap space are avoided at the time of the execution:)
- * The listCapacity value will set the limit of elements simultaneously to be loaded into the heap memory space, then the default value 1 is the best choice if you enable swap (1 living element in the heap space while the other will be swapped into files). Other values will cause OutOfMemoryError when adding new elements if the heap memory space limit is over, then it would be recommended to use more frequentely the memorySensitiveCallback to load the elements before to add them.
- *Implementation note: Unlike other Maps, the Collections-views Iterators returned by this Map don't support directly removals, or removals won't be reflected to the base Map. Use the keys to make the correct removals!
- */
- public class SpritesCacheManager<K, V> implements ConcurrentMap<K, V>, SortedMap<K, V>, Serializable, Threaded {
-
- /** serial version uid of this class */
- private static final long serialVersionUID = 2323;
- /** @see SpritesCacheListener
- * instances associated to the cache */
- private transient Set<SpritesCacheListener> listeners = Collections.synchronizedSet(new HashSet<SpritesCacheListener>());
- /** last thrown error */
- private Throwable lastError;
- /** list capacity of the implemented maps*/
- private int listCapacity;
- /***/
- private int initialListCapacity;
- /** last recently used sprites */
- private transient Map<K, V> lru = Collections.synchronizedMap(new HashMap<K, V>(listCapacity));
- /** last recently used keys */
- private transient List<K> lruK = Collections.synchronizedList(new Stack<K>());
- /** most recently used sprites */
- private transient Map<K, V> mru = Collections.synchronizedMap(new HashMap<K, V>(listCapacity));
- /** most recentrly used keys */
- private transient List<K> mruK = Collections.synchronizedList(new Stack<K>());
- /** softly cached sprites (synchronizedMap)*/
- private transient Map<K, SoftValue<K, V>> cache;
- /** references queue to poll by the Garbage Collector*/
- public transient ReferenceQueue<? super V> _cacheBack = new ReferenceQueue<Object>();
- /** file cached sprites */
- private SortedMap<K, File> cacheDisk;
- /** compress switch @default false */
- private boolean compress = false;
- /** swap switch @default false*/
- private boolean swap = false;
- /** timer instance to use memory auto cleaning up*/
- private java.util.Timer timer = null;
- /** file swapping extension @default sp3*/
- protected String cacheDisk_ext = "sp3";
- /** disk directory of file swapping @default .cache*/
- protected String cacheDisk_dir = "cache";
- /** files prefix */
- protected String cacheDisk_prefix = "_cache";
- /***/
- protected boolean debug = true;
- /***/
- protected transient boolean writing = false;
- /***/
- protected transient CoalescedThreadsMonitor writeMonitor;
- /***/
- protected transient boolean reading = false;
- /***/
- protected transient CoalescedThreadsMonitor readMonitor;
- /***/
- protected transient Vector<SoftReference<Map<K, V>>> subMaps = new Vector<SoftReference<Map<K, V>>>();
- /***/
- protected transient Vector<SoftReference<Set<K>>> keysSubLists = new Vector<SoftReference<Set<K>>>();
- /***/
- protected transient Vector<SoftReference<Set<V>>> valuesSubLists = new Vector<SoftReference<Set<V>>>();
-
- /***/
- public void setDebugEnabled(boolean b) {
- debug = b;
- }
-
- /***/
- public boolean isDebugEnabled() {
- return debug;
- }
-
- /***/
- private void debug(Object message) {
- if (debug) {
- System.err.println(message);
- }
- }
- /**
- * buffered values are referenced by Reference instances that are linked to the main ReferenceQueue so that the buffer is cleaned up every time cleanup() is called.* e.g. Object[] value = new Object[100];
- * this.buffer(value);
- * value = null; // then value is discarded within a short delay by the GarbageCollector.
- *
- * @see #_cacheBack
- * @see #cleanup()
- */
- private transient Map<Integer, SoftValue> buffer = Collections.synchronizedMap(new HashMap<Integer, SoftValue>());
-
- /***/
- public SpritesCacheManager(Comparator<? super K> c, int capacity) {
- this(capacity);
- comparator = c;
- }
-
- /***/
- public SpritesCacheManager(Comparator<? super K> c) {
- this(c, 1);
- }
-
- /** no arg contructor , memory will be limited to one living element in cache*/
- public SpritesCacheManager() {
- this(1);
- }
-
- /** Contructs the cache with initialized capacity and a WeakHashMap for the main living cache.
- *@see #listCapacity
- *@see WeakHashMap
- * @param capacity cache memory init and sensitive limit
- * @see #memorySensitiveCallback(Object, Object)*/
- public SpritesCacheManager(int capacity) {
- /*for(Method method : getClass().getMethods()) {
- for(Type generic : method.getGenericParameterTypes()) {
- for(Class<?> normal : method.getParameterTypes())
- System.out.println(method.getName() + " : generics params : " + generic + " normal : " + normal);
- }
- }*/
- File d;
- if (!(d = new File(cacheDisk_dir)).isDirectory()) {
- d.mkdirs();
- }
- listCapacity = initialListCapacity = capacity;
- setThreadGroup(new CoalescedThreadsMonitor(getClass().getName() + " TG"));
- cache = Collections.synchronizedMap(new WeakHashMap<K, SoftValue<K, V>>(capacity));
- buffer(cache);
- cacheDisk = Collections.synchronizedSortedMap(new TreeMap<K, File>());
- buffer(cacheDisk);
- }
-
- /***/
- public void setThreadGroup(ThreadGroup tg) {
- if (writeMonitor instanceof ThreadGroup) {
- writeMonitor.interrupt();
- }
- if (readMonitor instanceof ThreadGroup) {
- readMonitor.interrupt();
- }
- if (tg instanceof ThreadGroup) {
- writeMonitor = new CoalescedThreadsMonitor(tg, "TG-" + getClass().getName() + "-write");
- readMonitor = new CoalescedThreadsMonitor(tg, "TG-" + getClass().getName() + "-read");
- } else {
- writeMonitor = new CoalescedThreadsMonitor("TG-" + getClass().getName() + "-write");
- readMonitor = new CoalescedThreadsMonitor("TG-" + getClass().getName() + "-read");
- }
- writeMonitor.setCoalesceEnabled(false);
- readMonitor.setCoalesceEnabled(false);
- }
-
- /***/
- public ThreadGroup getThreadGroup() {
- return writeMonitor.getParent();
- }
-
- /***/
- public int getInitialListCapacity() {
- return initialListCapacity;
- }
-
- /** trunks the cache maps to fit lists capacity
- * @see #listCapacity
- * @param ru recently used map
- * @see #lru
- * @param ruK recently used keys
- * @see #lruK
- * @param n desired trunk-size*/
- private void trunk(Map<K, V> ru, List<K> ruK, int n) {
- while (ru.size() > n) {
- ru.remove(ruK.remove(ruK.size() - 1)); // fit to cache capacity, pop oldest added entry
- }
- cleanup();
- }
-
- /** the memory sensitive update to the cache Recently Used lists/maps. overflow-protected by a memory sensitive-calback
- * @see #memorySensitiveCallback(Object, Object)
- * @param key the key to update to
- * @param sp the associated object to update to*/
- private void memorySensitiveUpdate(K key, V sp) throws Throwable {
- memorySensitiveCallback("memoryUpdate", this, new Object[]{key, sp}, new Class[]{Object.class, Object.class});
- }
-
- /** memory lists are updated to the given pair K,V
- * outdated elements are removed. this method needs to be overflow-protected, so we use a memory sensitive-callback to avoid overflow.
- * @see #memorySensitiveUpdate(Object, Object)
- * @param key the key to update to
- * @param sp the associated object to update to*/
- public void memoryUpdate(K key, V sp) {
- int currentPty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- if (lru.containsKey(key)) {
- // if the same sprite has been found in last used items then it will be added to the most used items
- mru.put(key, sp);
- mruK.add(0, key);
- trunk(mru, mruK, listCapacity);
- }
- // here we update last recently used list
- lru.put(key, sp);
- lruK.add(0, key);
- trunk(lru, lruK, listCapacity);
- Thread.currentThread().setPriority(currentPty);
- }
-
- /** removes strong references and key references to object , thus it can be garbage collected . overflow-protected by a memory sensitive-callback.
- * @see #memorySensitiveCallback(Object, Object)
- *@param sp the object to be cleared of memory
- *@return the cleared object (same as the parameter)
- */
- private V memorySensitiveClear(V sp) throws Throwable {
- return (V) memorySensitiveCallback("memoryClear", this, new Object[]{sp}, new Class[]{Object.class});
- }
-
- /** removes all strong references to the given Object. not overflow-protected, we use a memory sensitive-callback.
- * @see #memorySensitiveClear(Object)
- *@param sp the object to be cleared of memory
- *@return the cleared object (same as the parameter)*/
- public V memoryClear(V sp) {
- int currentPty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- if (lru.containsValue(sp)) {
- Set<Map.Entry<K, V>> set = lru.entrySet();
- synchronized (lru) {
- Iterator<Map.Entry<K, V>> i0 = set.iterator();
- for (buffer(i0); i0.hasNext();) {
- // check for any value in LRU
- Map.Entry<K, V> entry = i0.next();
- buffer(entry);
- V value = (entry != null) ? entry.getValue() : null;
- buffer(value);
- if (value != null) {
- if (value.equals(sp)) {
- synchronized (lruK) {
- Iterator<K> i1 = lruK.iterator();
- for (buffer(i1); i1.hasNext();) {
- // also remove keys in stack
- if (i1.next().equals(entry.getKey())) {
- i1.remove();
- }
- }
- i0.remove();
- }
- }
- }
- }
- }
- }
- if (mru.containsValue(sp)) {
- Set<Map.Entry<K, V>> set = mru.entrySet();
- synchronized (mru) {
- Iterator<Map.Entry<K, V>> i0 = set.iterator();
- for (buffer(i0); i0.hasNext();) {
- // check for any value in MRU
- Map.Entry<K, V> entry = i0.next();
- buffer(entry);
- V value = (entry != null) ? entry.getValue() : null;
- buffer(value);
- if (value != null) {
- if (value.equals(sp)) {
- synchronized (mruK) {
- Iterator<K> i1 = mruK.iterator();
- for (buffer(i1); i1.hasNext();) {
- // also remove keys in stack
- if (i1.next().equals(entry.getKey())) {
- i1.remove();
- }
- }
- i0.remove();
- }
- }
- }
- }
- }
- }
- Thread.currentThread().setPriority(currentPty);
- return sp;
- }
-
- /** clears the mapping memory */
- protected void clearMemory() {
- mru.clear();
- mruK.clear();
- lru.clear();
- lruK.clear();
- }
-
- /** compresses cache object
- * @param pSp the object to compress
- * @return compressed byte array of the argument */
- private void compress(Serializable sp, OutputStream out) {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- cleanup();
- buffer(sp);
- buffer(out);
- try {
- ZipEntry ze = new ZipEntry("sp_" + sp.hashCode());
- ZipOutputStream zip = new ZipOutputStream(out);
- zip.putNextEntry(ze);
- buffer(ze);
- buffer(zip);
- ObjectOutputStream oos = new ObjectOutputStream(zip);
- oos.writeObject(sp);
- zip.closeEntry();
- zip.finish();
- /*zip.close();
- oos.close();*/
- debug("Compressed Sprite caching " + ze.getCompressedSize() + " !");
- } catch (IOException ex) {
- ex.printStackTrace();
- } finally {
- Thread.currentThread().setPriority(pty);
- }
- }
-
- /** return the synchronized map of file swapping
- * @return file swapping map that has been synchronized */
- public Map<K, File> getSwapMap() {
- return cacheDisk;
- }
-
- /** uncompresses cache object
- * @param b the compressed data to use
- * @return the uncompressed object*/
- private Serializable uncompress(InputStream in) throws NullPointerException {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- cleanup();
- Serializable o = null;
- ZipInputStream zis = new ZipInputStream(in);
- buffer(zis);
- ObjectInputStream ois;
- try {
- zis.getNextEntry();
- ois = new ObjectInputStream(zis);
- buffer(ois);
- o = (Serializable) ois.readObject();
- buffer(o);
- zis.closeEntry();
- /*zis.close();
- ois.close();*/
- } catch (EOFException e) {
- debug("CacheEntry: uncompress: done.");
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } finally {
- zis = null;
- ois = null;
- if (o == null) {
- throw new NullPointerException("Null CacheEntry: cannot uncompress!");
- }
- Thread.currentThread().setPriority(pty);
- return o;
- }
- }
-
- /** writes the given mapping to swap. this method doesn't need to be synchronized or overflow-protected otherwise it will cause a dead-lock.
- * @param pKey the key
- * @param pValue the associated object
- * @return true or false whether it has succeeded*/
- private boolean writeSwap(K key, Serializable value) {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- cleanup();
- notifyWrite(WRITE_STARTED);
- buffer(key);
- buffer(value);
- File f = null;
- RandomAccessFile raf = null;
- ObjectOutputStream oos;
- FileOutputStream fos;
- final boolean compress0 = compress;
- boolean interrupt_ = false;
- try {
- synchronized (readMonitor.getMonitor(false)) {
- while (reading) {
- readMonitor.waitOnMonitor(10);
- }
- writing = true;
- File dir = new File(cacheDisk_dir);
- dir.mkdirs();
- f = File.createTempFile(cacheDisk_prefix + hashCode(), key + "." + cacheDisk_ext, dir);
- buffer(f);
- raf = new RandomAccessFile(f, "rws");
- fos = new FileOutputStream(raf.getFD());
- //FileLock fl = fos.getChannel().lock();
- oos = new ObjectOutputStream(fos);
- buffer(oos);
- buffer(fos);
- oos.writeBoolean(compress0);
- if (compress0) {
- compress(value, fos);
- } else {
- oos.writeObject(value);
- }
- //fl.release();
- oos.close();
- cacheDisk.put(key, f);
- f.deleteOnExit();
- notifyWrite(WRITE_COMPLETED);
- oos = null;
- fos = null;
- value = null;
- Thread.currentThread().setPriority(pty);
- readMonitor.notifyOnMonitor();
- }
- writing = false;
- synchronized (writeMonitor.getMonitor(false)) {
- writeMonitor.notifyAllOnMonitor();
- }
- return true;
- } catch (Exception e) {
- if (e instanceof InterruptedException) {
- interrupt_ = true;
- }
- e.printStackTrace();
- lastError = e;
- notifyWrite(WRITE_ERROR);
- if (f != null) {
- f.delete();
- notifyWrite(WRITE_ABORTED);
- }
- writing = false;
- synchronized (writeMonitor.getMonitor(false)) {
- writeMonitor.notifyAllOnMonitor();
- }
- if (interrupt_) {
- Thread.currentThread().interrupt();
- }
- return false;
- }
- }
-
- /** reads swap. this method doesn't need to be synchronized or overflow-protected otherwise it will cause a dead-lock.
- * @param pKey the key to read from
- * @return the value-object read*/
- private V readSwap(K key) {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- cleanup();
- notifyRead(READ_STARTED);
- V value = null;
- File f = null;
- RandomAccessFile raf = null;
- ObjectInputStream ois;
- FileInputStream fis;
- boolean interrupt_ = false;
- try {
- synchronized (writeMonitor.getMonitor(false)) {
- while (writing) {
- writeMonitor.waitOnMonitor(10);
- }
- reading = true;
- if (cacheDisk.containsKey(key)) {
- debug("Swap: Found Key " + key + " ");
- f = (File) cacheDisk.get(key);
- debug(f.getCanonicalPath());
- raf = new RandomAccessFile(f, "r");
- fis = new FileInputStream(raf.getFD());
- //FileLock fl = fis.getChannel().lock(0L, Long.MAX_VALUE, true);
- debug("opening...");
- ois = new ObjectInputStream(fis);
- buffer(ois);
- buffer(fis);
- if (ois.readBoolean()) {
- value = (V) uncompress(fis);
- } else {
- value = (V) ois.readObject();
- }
- //fl.release();
- buffer(value);
- ois.close();
- }
- writeMonitor.notifyOnMonitor();
- }
- } catch (Exception e) {
- if (e instanceof OptionalDataException) {
- OptionalDataException opt = (OptionalDataException) e;
- debug("OPTIONAL DATA EXCEPTION : eof=" + opt.eof + " bytes=" + opt.length);
- } else if (e instanceof InterruptedException) {
- interrupt_ = true;
- }
- e.printStackTrace();
- lastError = e;
- notifyRead(READ_ERROR);
- } finally {
- ois = null;
- fis = null;
- if (value == null) {
- notifyRead(READ_ABORTED);
- } else {
- debug("Swap: reading done.");
- notifyRead(READ_COMPLETED);
- }
- Thread.currentThread().setPriority(pty);
- reading = false;
- synchronized (readMonitor.getMonitor(false)) {
- readMonitor.notifyAllOnMonitor();
- }
- if (interrupt_) {
- Thread.currentThread().interrupt();
- }
- return value;
- }
- }
-
- /** removes file swapping for the given key
- * @param key the key to remove file swap
- * @return true or false whether it has succeeded or not*/
- private boolean unswap(K key) {
- File f = cacheDisk.remove(key);
- buffer(f);
- try {
- if (f instanceof File) {
- f.delete();
- return true;
- } else {
- return false;
- }
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- /** enable compression of the cache entries
- * @param b compression dis/enabled*/
- public void setCompressionEnabled(boolean b) {
- compress = b;
- }
-
- /***/
- private Thread getShutdownHooker() {
- return new Thread(new Runnable() {
-
- public void run() {
- SpritesCacheManager.this.cleanFileSwap();
- }
- });
- }
-
- /** enable file swap to disk of the cache entries (recommended)
- * @param b swapping dis/enabled */
- public void setSwapDiskEnabled(boolean b) {
- if (b) {
- if (!swap) {
- Runtime.getRuntime().addShutdownHook(getShutdownHooker());
- }
- } else {
- Runtime.getRuntime().removeShutdownHook(getShutdownHooker());
- }
- swap = b;
- }
-
- /** checks whether swapping is enabled or not.
- * @see #setSwapDiskEnabled(boolean)
- * @return true or false*/
- public boolean isSwapDiskEnabled() {
- return swap;
- }
-
- /** current cache open size
- * @return size of the living cache (excluding file swap cache)
- * @see #cacheDisk
- * @see #cache*/
- public int size() {
- try {
- cleanup();
- } catch (Exception e) {
- } finally {
- return cache.size();
- }
- }
-
- /** memory heap allocation size in cache calculated in percentage of total capacity
- * @return the current memory allocation in percent of the lists capactity
- * @see #listCapacity*/
- public double allocSize() {
- double d = 100 * cache.size() / listCapacity;
- debug("alloc=" + d);
- return d;
- }
-
- /** adds the given mapping to cache
- * @see #setSwapDiskEnabled(boolean)
- * @param key the key
- * @param obj the associated object
- * @return the privous mapped object or null*/
- public V add(K key, V obj) {
- return add(key, obj, swap);
- }
-
- /***/
- private void updateSubMaps(boolean removal, K key, V value) {
- for (Iterator<SoftReference<Map<K, V>>> i = subMaps.iterator(); i.hasNext();) {
- SoftReference<Map<K, V>> ref = i.next();
- Map<K, V> subMap = (Map<K, V>) ref.get();
- if (subMap instanceof Map) {
- if (removal) {
- subMap.remove(key);
- } else {
- subMap.put(key, value);
- }
- }
- }
- }
-
- /***/
- private void updateSubListsK(boolean removal, K key) {
- for (Iterator<SoftReference<Set<K>>> i = keysSubLists.iterator(); i.hasNext();) {
- SoftReference<Set<K>> listRef = i.next();
- Set<K> subList = (Set<K>) listRef.get();
- if (subList instanceof Set) {
- if (removal) {
- subList.remove(key);
- } else {
- subList.add(key);
- }
- }
- }
- }
-
- /***/
- private void updateSubListsV(boolean removal, V value) {
- for (Iterator<SoftReference<Set<V>>> i = valuesSubLists.iterator(); i.hasNext();) {
- SoftReference<Set<V>> listRef = i.next();
- Set<V> subList = (Set<V>) listRef.get();
- if (subList instanceof Set) {
- if (removal) {
- subList.remove(value);
- } else {
- subList.add(value);
- }
- }
- }
- }
-
- /** adds the mapping to cache with the swap option
- * @param key the key
- * @param obj the associated object
- * @param swap option to dis/enabled swap to this key
- * @return the privous mapped object or null*/
- protected V add(K key, V obj, boolean swap) {
- cleanup();
- SoftValue<K, V> ancient = null;
- try {
- if (obj != null) {
- memorySensitiveUpdate(key, obj);
- ancient = (SoftValue<K, V>) cache.put(key, new SoftValue(obj, key, _cacheBack));
- buffer(ancient);
- updateSubMaps(false, key, obj);
- updateSubListsK(false, key);
- updateSubListsV(false, obj);
- if (ancient != null) {
- V v = (V) ancient.get();
- if (v != null) {
- if (!v.equals(obj)) {
- memorySensitiveClear((V) ancient.get());
- }
- }
- }
- if (swap) {
- if (!writeSwap(key, (Serializable) obj)) {
- throw new Exception("Unable to swap! " + key);
- }
- }
- }
- } catch (Exception e) {
- debug("CacheEntry: error while caching " + key + e.getMessage() + "\r\n");
- e.printStackTrace();
- } finally {
- return (ancient instanceof SoftReference) ? (V) ancient.get() : null;
- }
- }
-
- /** safely removes Object from cache (it can be either a key referencing an object or an object currently cached)
- * @param sp can be either a key or a value; if the key-class is the same as value-class then it will be used as a key
- * @return the removed object
- */
- public V remove(Object sp) {
- try {
- K key = (K) sp;
- SoftValue<K, V> ref = cache.remove(key);
- updateSubMaps(true, key, null);
- updateSubListsK(true, key);
- updateSubListsV(true, (V) ref.get());
- V value = (ref instanceof SoftValue) ? (V) ref.get() : null;
- synchronized (lruK) {
- Iterator<K> i = lruK.iterator();
- for (buffer(i); i.hasNext();) {
- if (i.next().equals(key)) {
- i.remove();
- }
- }
- }
- synchronized (mruK) {
- Iterator<K> i = mruK.iterator();
- for (buffer(i); i.hasNext();) {
- if (i.next().equals(key)) {
- i.remove();
- }
- }
- }
- lru.remove(key);
- mru.remove(key);
- if (swap) {
- unswap(key);
- }
- return value;
- } catch (ClassCastException e) {
- try {
- return memorySensitiveClear((V) sp);
- } catch (Throwable t) {
- t.printStackTrace();
- return null;
- }
- }
- }
-
- /** Returns the k-referenced object allocated in memory-cache
- * @param k the key that references the object
- * @return V the referenced object*/
- public V get(Object k) {
- cleanup();
- K key = (K) k;
- V value = null;
- if (cache.containsKey(key)) {
- debug("retrieving softValue referent");
- value = (V) cache.get(key).get();
- } else if (swap && cacheDisk.containsKey(key)) {
- debug("Get key " + key + " from swap...");
- value = readSwap(key);
- add(key, value, false);
- }
- if (value != null) {
- try {
- memorySensitiveUpdate(key, value);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- return value;
- }
-
- /** Checks for this key availability in memory
- * @param key the key to look for
- * @return true or false */
- public boolean has(K key) {
- cleanup();
- return (cache.containsKey(key)) ? true : ((swap) ? cacheDisk.containsKey(key) : false);
- }
-
- /***/
- public void clearMemorySwap() {
- if (swap) {
- Set<Map.Entry<K, File>> set = cacheDisk.entrySet();
- synchronized (cacheDisk) {
- for (Iterator<Map.Entry<K, File>> i = set.iterator(); i.hasNext();) {
- Map.Entry<K, File> entry = i.next();
- File f = entry.getValue();
- f.delete();
- cacheDisk.remove(entry.getKey());
- }
- }
- }
- }
-
- /***/
- private void clearSubMaps() {
- for (Iterator<SoftReference<Map<K, V>>> i = subMaps.iterator(); i.hasNext();) {
- SoftReference<Map<K, V>> mapRef = i.next();
- Map<K, V> subMap = (Map<K, V>) mapRef.get();
- if (subMap instanceof Map) {
- subMap.clear();
- }
- }
- }
-
- /***/
- private void clearSubListsK() {
- for (Iterator<SoftReference<Set<K>>> i = keysSubLists.iterator(); i.hasNext();) {
- SoftReference<Set<K>> listRef = i.next();
- Set<K> subList = (Set<K>) listRef.get();
- if (subList instanceof Set) {
- subList.clear();
- }
- }
- }
-
- /***/
- private void clearSubListsV() {
- for (Iterator<SoftReference<Set<V>>> i = valuesSubLists.iterator(); i.hasNext();) {
- SoftReference<Set<V>> listRef = i.next();
- Set<V> subList = (Set<V>) listRef.get();
- if (subList instanceof Set) {
- subList.clear();
- }
- }
- }
-
- /** clear memory allocation maps */
- public void clear() {
- clearSubMaps();
- clearSubListsK();
- clearSubListsV();
- clearMemory();
- cache.clear();
- cleanup();
- }
-
- /** safely cleans up the cache to clear unused referenced-objects */
- public void cleanup() {
- int currentPty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- Reference ref;
- while ((ref = _cacheBack.poll()) != null) {
- try {
- if (ref instanceof SoftValue) {
- K key = ((SoftValue<K, V>) ref).key;
- cache.remove(key);
- buffer.remove(key);
- }
- if (ref instanceof Reference) {
- ref.clear();
- }
- debug("SoftCache cleaned up");
- } catch (Exception e) {
- debug("SoftCache cleanup error :");
- e.printStackTrace();
- }
- }
- Thread.currentThread().setPriority(currentPty);
- }
-
- /** Enables cleanup of garbage queue originally done by the Garbage Collector (enabling this can cause a global thread overflow)
- * @see #cleanup()
- * @param b automatic cleanup dis/enabled */
- public void setAutoCleanupEnabled(boolean b) {
- if (timer != null) {
- timer.cancel();
- timer.purge();
- }
- if (b) {
- timer = new java.util.Timer("Timer-SpritesCacheManager-Cleanup");
- timer.scheduleAtFixedRate(new TimerTask() {
-
- public void run() {
- Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 1);
- cleanup();
- }
- }, 0L, 20);
- buffer(timer);
- } else {
- timer = null;
- }
- }
-
- /***/
- public void ensureListCapacity(int n) {
- listCapacity = Math.max(listCapacity, n);
- notifyEvent(CAPACITY_EXTENDED);
- }
-
- /** adjust cache to a given capacity (amount of objects that will stay alive unless they cause an heap overflow)
- * @param n amount of heap objects to keep alive*/
- public void trimCacheLive(int n) {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- trunk(mru, mruK, n);
- trunk(lru, lruK, n);
- listCapacity = n;
- notifyEvent(CAPACITY_EXTENDED);
- Thread.currentThread().setPriority(pty);
- }
-
- /** Callbacks to invoked function
- * @param method the method named to make the callback
- * @param target the targeted object by this method callback
- * @param args the Objects arguments of the method callback
- * @param clargs the Class arguments of the method callback
- * @return usual returned value of the called back method*/
- public static Object callback(String method, Object target, Object[] args, Class[] clargs) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
- System.err.println("callback on " + target.getClass().getName() + "." + method + "() : target.toString()=" + target);
- return target.getClass().getMethod(method, clargs).invoke(target, args);
- }
-
- /** Sets priority to invoked task
- * @see Thread#setPriority(int)
- * @param method the method named to make the callback
- * @param target the targeted object by this method callback
- * @param args the Objects arguments of the method callback
- * @param clargs the Class arguments of the method callback
- * @param level priority value from 0 to 9. (normal value is that one of Thread.NORM_PRIORITY)
- * @return usual returned value of the called back method
- * */
- public static Object doPriority(String method, Object target, Object[] args, Class[] clargs, int level) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
- int currentPty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(level);
- Object result = callback(method, target, args, clargs);
- Thread.currentThread().setPriority(currentPty);
- return result;
- }
-
- /** This is the second important function of this cache. It calls back to invoked method with memory sensitivity selfcare, i.e. memory will not cause a crash of the JVM due to the memory limit defined by the listCapacity value.
- * And eventually the callback will throw OutOfMemory to the cache, which is able to clear heap memory overflows. Least recently used elements will be discarded to clear memory space if they're not referenced elsewhere than in the cache.
- *Caution of use: you SHOULD NOT use a callback method with any of the present cache class-methods, because of synchronization issues with Java that locks the Map instance used by this cache.
- * @param method the method named to make the callback
- * @param target the targeted object by this method callback
- * @param args the Objects arguments of the method callback
- * @param clargs the Class arguments of the method callback
- * @return usual returned value of the called back method*/
- public Object memorySensitiveCallback(String method, Object target, Object[] args, Class[] clargs) throws Throwable {
- try {
- return callback(method, target, args, clargs);
- } catch (NoSuchMethodException ex) {
- ex.printStackTrace();
- return null;
- } catch (IllegalArgumentException ex) {
- ex.printStackTrace();
- return null;
- } catch (IllegalAccessException ex) {
- ex.printStackTrace();
- return null;
- } catch (InvocationTargetException ex) {
- Throwable t = ex.getTargetException();
- buffer(t);
- if (t instanceof OutOfMemoryError) {
- if (listCapacity == 0) {
- throw (OutOfMemoryError) t;
- } else if (listCapacity == 1) {
- clearMemory();
- } else {
- trimCacheLive((int) Math.floor((float)listCapacity / 2.0f));
- }
- cleanup();
- return memorySensitiveCallback(method, target, args, clargs);
- } else {
- throw t;
- }
- }
- }
-
- /** overriden finalization method that clears the cache and stops any running activity*/
- protected void finalize() throws Throwable {
- if (timer != null) {
- timer.cancel();
- timer = null;
- }
- clear();
- super.finalize();
- }
-
- /** cleans up the file swapping directory
- * @see #cacheDisk_dir*/
- public void cleanFileSwap() {
- File dir = null;
- dir = new File(cacheDisk_dir);
- ExampleFileFilter ff = new ExampleFileFilter();
- ff.addExtension(cacheDisk_ext);
- File[] swapFiles = null;
- if (dir.isDirectory()) {
- swapFiles = dir.listFiles((java.io.FileFilter) ff);
- }
- for (File f : swapFiles) {
- try {
- f.delete();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- /** Buffers the object to avoid memory freeze when it has to be cleared off the memory.
- * @discussion the buffer is saved into a synchronized WeakHashMap that refreshes itself through ReferenceQueue.poll()
- * @param obj the object to "buffer"
- * @see SoftValue
- */
- public void buffer(Object obj) {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- cleanup();
- if (obj != null) {
- buffer.put(obj.hashCode(), new SoftValue(obj, obj.hashCode(), _cacheBack));
- }
- Thread.currentThread().setPriority(pty);
- }
-
- /** checks for the cache if it's empty
- * @return true or false*/
- public boolean isEmpty() {
- return cache.isEmpty();
- }
-
- /** checks for a reference availability
- *@param key the key to check for
- * @return true or false */
- public boolean containsKey(Object key) {
- return has((K) key);
- }
-
- /** checks for a reference availability
- * @param object the object to check for
- * @return true or false*/
- public boolean containsValue(Object object) {
- Collection<SoftValue<K, V>> coll = cache.values();
- synchronized (cache) {
- for (Iterator<SoftValue<K, V>> i = coll.iterator(); i.hasNext();) {
- Object ref = i.next().get();
- if (ref != null) {
- if (ref.equals(object)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /** adds a new mapping
- * @see #add(Object, Object)
- * @param key the key
- * @param value the object
- * @return the previous mapped object or null*/
- public V put(K key, V value) {
- return add(key, value);
- }
-
- /** adds a complete map to the cache
- * @param map a map */
- public void putAll(Map<? extends K, ? extends V> map) {
- Map<? extends K, ? extends V> smap = Collections.synchronizedMap(map);
- Set<? extends K> set = smap.keySet();
- synchronized (smap) {
- for (Iterator<? extends K> i = set.iterator(); i.hasNext();) {
- K k = i.next();
- put(k, smap.get(k));
- }
- }
- }
-
- /** returns the key-set of the cache
- * @return the key-set*/
- public Set<K> keySet() {
- TreeSet<K> sortedKeys = new TreeSet<K>(comparator);
- Set<K> set = (swap) ? cacheDisk.keySet() : cache.keySet();
- synchronized ((swap) ? cacheDisk : cache) {
- for (Iterator<K> i = set.iterator(); i.hasNext();) {
- sortedKeys.add(i.next());
- }
- }
- keysSubLists.add(new SoftReference(sortedKeys, _cacheBack));
- return (Set<K>) sortedKeys;
- }
-
- /** retuns the cached objects collection
- * @return collection of objects that are currently cached*/
- public Collection<V> values() {
- HashSet<V> cached = new HashSet<V>();
- Set<K> set = (swap) ? cacheDisk.keySet() : cache.keySet();
- synchronized ((swap) ? cacheDisk : cache) {
- for (Iterator<K> i = set.iterator(); i.hasNext();) {
- K key = i.next();
- cached.add(get(key));
- }
- }
- valuesSubLists.add(new SoftReference(cached, _cacheBack));
- return (Collection<V>) cached;
- }
-
- /** the mapping entries
- * @return the mapping entries containing the cached objects*/
- public Set<Map.Entry<K, V>> entrySet() {
- SortedMap<K, V> m = new TreeMap<K, V>(comparator);
- Set<K> set = (swap) ? cacheDisk.keySet() : cache.keySet();
- synchronized ((swap) ? cacheDisk : cache) {
- for (Iterator<K> i = set.iterator(); i.hasNext();) {
- K key = i.next();
- m.put(key, get(key));
- }
- }
- subMaps.add(new SoftReference(m, _cacheBack));
- return m.entrySet();
- }
-
- /** writes the entire cache the the output.
- * @param out the output */
- private void writeObject(ObjectOutputStream out) throws IOException {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- out.defaultWriteObject();
- Set<Map.Entry<K, File>> set = cacheDisk.entrySet();
- synchronized (cacheDisk) {
- for (Iterator<Map.Entry<K, File>> files = set.iterator(); files.hasNext();) {
- Map.Entry<K, File> entry = files.next();
- buffer(entry);
- File f = entry.getValue();
- buffer(f);
- RandomAccessFile raf = new RandomAccessFile(f, "r");
- FileInputStream fis = new FileInputStream(raf.getFD());
- buffer(fis);
- buffer(raf);
- //FileLock fl = fis.getChannel().lock(0L, Long.MAX_VALUE, true);
- // write swap : file name to recover swap < file length < file
- out.writeObject(entry.getKey());
- out.writeLong(f.length());
- debug(f.length());
- try {
- byte[] b = new byte[(int) 512];
- int readBytes = 0;
- int writtenBytes = 0;
- while ((readBytes = fis.read(b)) != -1) {
- out.write(b, 0, readBytes);
- writtenBytes += readBytes;
- }
- debug(writtenBytes);
- // fl.release();
- fis.close();
- raf.close();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- fis = null;
- raf = null;
- cleanup();
- }
- }
- }
- Thread.currentThread().setPriority(pty);
- }
-
- /** reads the entire cache from the input.
- * @param in input*/
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- int pty = Thread.currentThread().getPriority();
- Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
- in.defaultReadObject();
- keysSubLists = new Vector<SoftReference<Set<K>>>();
- valuesSubLists = new Vector<SoftReference<Set<V>>>();
- subMaps = new Vector<SoftReference<Map<K, V>>>();
- writing = false;
- reading = false;
- setThreadGroup(new CoalescedThreadsMonitor(getClass().getName() + " TG"));
- listeners = Collections.synchronizedSet(new HashSet<SpritesCacheListener>());
- _cacheBack = new ReferenceQueue<Object>();
- buffer = Collections.synchronizedMap(new HashMap<Integer, SoftValue>());
- buffer(buffer);
- buffer(_cacheBack);
- cache = Collections.synchronizedMap(new WeakHashMap<K, SoftValue<K, V>>(listCapacity));
- buffer(cache);
- lru = Collections.synchronizedMap(new HashMap<K, V>());
- buffer(lru);
- lruK = Collections.synchronizedList(new Stack<K>());
- buffer(lruK);
- mru = Collections.synchronizedMap(new HashMap<K, V>());
- buffer(mru);
- mruK = Collections.synchronizedList(new Stack<K>());
- buffer(mruK);
- File file = null;
- boolean read = true;
- do {
- RandomAccessFile raf = null;
- ObjectOutputStream oos = null;
- FileOutputStream fos = null;
- try {
- file = cacheDisk.get((K) in.readObject());
- long len = in.readLong();
- buffer(len);
- if (file.exists()) {
- if (file.length() == len) {
- in.skip(len);
- continue;
- }
- }
- file.deleteOnExit();
- debug("read " + file);
- raf = new RandomAccessFile(file, "rws");
- raf.getChannel().truncate(0);
- fos = new FileOutputStream(raf.getFD());
- buffer(fos);
- buffer(raf);
- //FileLock fl = fos.getChannel().lock();
- byte[] b = new byte[(int) 512];
- int readBytes = 0;
- int rBytes;
- while ((rBytes = in.read(b)) != -1) {
- fos.write(b, 0, rBytes);
- readBytes += rBytes;
- }
- debug("bytes read: " + readBytes + " file length was: " + len);
- //fl.release();
- raf.close();
- fos.close();
- } catch (EOFException e) {
- e.printStackTrace();
- read = false;
- } catch (Exception e) {
- read = false;
- if (e instanceof OptionalDataException) {
- OptionalDataException opt = (OptionalDataException) e;
- in.skipBytes(opt.length);
- debug("skip" + opt.length);
- read = true;
- if (opt.eof) {
- read = false;
- }
- } else {
- e.printStackTrace();
- }
- } finally {
- oos = null;
- fos = null;
- raf = null;
- cleanup();
- }
- } while (read);
- debug("SpritesCacheManager read from DataStream!");
- Thread.currentThread().setPriority(pty);
- }
-
- /** adds a listener to the cache.
- * @param l the listener*/
- public void addSpritesCacheListener(SpritesCacheListener l) {
- listeners.add(l);
- }
-
- /** removes one listener from the cache
- * @param l the listener*/
- public void removeSpritesCacheListener(SpritesCacheListener l) {
- listeners.remove(l);
- }
-
- /** notifies all listeners of any write event
- * @param event the event
- * @see #WRITE_STARTED
- * @see #WRITE_ABORTED
- * @see #WRITE_COMPLETED
- * @see #WRITE_ERROR*/
- public void notifyWrite(int event) {
- switch (event) {
- case WRITE_STARTED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.writeStarted();
- }
- }
- }
- break;
- case WRITE_ABORTED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.writeAborted();
- }
- }
- }
- break;
- case WRITE_COMPLETED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.writeCompleted();
- }
- }
- }
- break;
- case WRITE_ERROR:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.writeError((lastError != null) ? lastError.getStackTrace() : null);
- }
- }
- }
- break;
- default:
- }
- }
-
- /** notifies the listeners of any read event
- * @param event the event
- * @see #READ_STARTED
- * @see #READ_ABORTED
- * @see #READ_COMPLETED
- * @see #READ_ERROR*/
- public void notifyRead(int event) {
- switch (event) {
- case READ_STARTED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.readStarted();
- }
- }
- }
- break;
- case READ_ABORTED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.readAborted();
- }
- }
- }
- break;
- case READ_COMPLETED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.readCompleted();
- }
- }
- }
- break;
- case READ_ERROR:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l != null) {
- l.readError((lastError != null) ? lastError.getStackTrace() : null);
- }
- }
- }
- break;
- default:
- }
- }
- /** a "read started" event */
- static final int READ_STARTED = 0;
- /** a "read aborted" event */
- static final int READ_ABORTED = 1;
- /** a "read completed" event */
- static final int READ_COMPLETED = 2;
- /** a "read error" event */
- static final int READ_ERROR = 3;
- /** a "write started" event */
- static final int WRITE_STARTED = 4;
- /** a "write aborted" event */
- static final int WRITE_ABORTED = 5;
- /** a "write completed" event */
- static final int WRITE_COMPLETED = 6;
- /** a "write error" event */
- static final int WRITE_ERROR = 7;
- /** a "capacity extended" event */
- static final int CAPACITY_EXTENDED = 8;
-
- /** notifies the listeners for any event
- * @see #notifyRead(int)
- * @see #notifyWrite(int) */
- public void notifyEvent(int event) {
- switch (event) {
- case READ_STARTED:
- case READ_ABORTED:
- case READ_COMPLETED:
- case READ_ERROR:
- notifyRead(event);
- break;
- case WRITE_STARTED:
- case WRITE_ABORTED:
- case WRITE_COMPLETED:
- case WRITE_ERROR:
- notifyWrite(event);
- break;
- case CAPACITY_EXTENDED:
- synchronized (listeners) {
- for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
- SpritesCacheListener l = i.next();
- if (l instanceof SpritesCacheListener) {
- l.capacityExtended(listCapacity);
- }
- }
- }
- break;
- default:
- }
- }
-
- /***/
- public static File makeKeyFile(Serializable key, File f, String fileDir, String keyFilename, boolean compress) {
- try {
- FileInputStream fis = new FileInputStream(new RandomAccessFile(f, "r").getFD());
- byte[] b = new byte[512];
- SpritesCacheManager<Integer, byte[]> fileContents = new SpritesCacheManager<Integer, byte[]>((int) f.length());
- fileContents.setSwapDiskEnabled(true);
- int i = 0;
- int readBytes;
- while ((readBytes = fis.read(b)) != -1) {
- byte[] rb = new byte[readBytes];
- for (int j = 0; j < rb.length; j++) {
- rb[j] = b[j];
- }
- fileContents.put(i++, rb);
- }
- fis.close();
- return makeKeyFile(new Serializable[]{key}, new SpritesCacheManager[]{fileContents}, fileDir, keyFilename, compress);
- } catch (FileNotFoundException ex) {
- ex.printStackTrace();
- return null;
- } catch (IOException ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
- /***/
- public static File makeKeyFile(Serializable[] key, Serializable[] serialData, String fileDir, String keyFilename, boolean compress) throws IndexOutOfBoundsException {
- if (key.length != serialData.length) {
- throw new IndexOutOfBoundsException("Keys and serial datas must be of the same length. there are " + key.length + " keys and " + serialData.length + " serial datas");
- }
- SpritesCacheManager<Serializable, Serializable> spm = new SpritesCacheManager<Serializable, Serializable>();
- spm.setSwapDiskEnabled(true);
- spm.setCompressionEnabled(compress);
- for (int i = 0; i < key.length; i++) {
- spm.put(key[i], serialData[i]);
- }
- new File(fileDir).mkdirs();
- spm.cacheDisk_dir = fileDir;
- File keyFile = new File(fileDir + File.separator + keyFilename);
- ObjectOutputStream out;
- try {
- out = new ObjectOutputStream(new FileOutputStream(new RandomAccessFile(keyFile, "rws").getFD()));
- out.writeObject(spm);
- out.close();
- return keyFile;
- } catch (FileNotFoundException ex) {
- ex.printStackTrace();
- return null;
- } catch (IOException ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
- /***/
- public static File extractKeyFile(File f, Serializable key, String extractFilename) {
- try {
- SpritesCacheManager<Integer, byte[]> serial = (SpritesCacheManager<Integer, byte[]>) extractKeyData(f, key);
- if (serial == null) {
- return null;
- }
- File xF = new File(extractFilename);
- FileOutputStream fos = new FileOutputStream(new RandomAccessFile(xF, "rws").getFD());
- for (int i = 0; i < serial.getSwapMap().size(); i++) {
- fos.write(serial.readSwap(i));
- }
- fos.close();
- return xF;
- } catch (FileNotFoundException ex) {
- ex.printStackTrace();
- return null;
- } catch (IOException ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
- /***/
- public static Serializable extractKeyData(File f, Serializable key) {
- ObjectInputStream in;
- try {
- in = new ObjectInputStream(new FileInputStream(new RandomAccessFile(f, "rws").getFD()));
- SpritesCacheManager<Serializable, Serializable> spm;
- spm = (SpritesCacheManager<Serializable, Serializable>) in.readObject();
- in.close();
- Serializable serialData = null;
- serialData = spm.readSwap(key);
- return serialData;
- } catch (FileNotFoundException ex) {
- ex.printStackTrace();
- return null;
- } catch (IOException ex) {
- ex.printStackTrace();
- return null;
- } catch (ClassNotFoundException ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
- /**
- the comparator doesn't get Serialized. use setComparator after a deserialization process!
- @see #setComparator(Comparator)*/
- public transient Comparator<? super K> comparator = null;
-
- /***/
- public void setComparator(Comparator<? super K> c) {
- comparator = c;
- }
-
- /***/
- public Comparator<? super K> getComparator() {
- return comparator();
- }
-
- /***/
- public Comparator<? super K> comparator() {
- return comparator;
- }
-
- /***/
- public SortedMap<K, V> subMap(K fromKey, K toKey) {
- Set<Map.Entry<K, V>> entries = entrySet();
- SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
- synchronized (this) {
- for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
- Map.Entry<K, V> entry = i.next();
- sortedMap.put(entry.getKey(), entry.getValue());
- }
- notify();
- }
- subMaps.add(new SoftReference(sortedMap, _cacheBack));
- return sortedMap.subMap(fromKey, toKey);
- }
-
- /***/
- public SortedMap<K, V> headMap(K headKey) {
- Set<Map.Entry<K, V>> entries = entrySet();
- SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
- synchronized (this) {
- for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
- Map.Entry<K, V> entry = i.next();
- sortedMap.put(entry.getKey(), entry.getValue());
- }
- notify();
- }
- subMaps.add(new SoftReference(sortedMap, _cacheBack));
- return sortedMap.headMap(headKey);
- }
-
- /***/
- public SortedMap<K, V> tailMap(K tailKey) {
- Set<Map.Entry<K, V>> entries = entrySet();
- SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
- synchronized (this) {
- for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
- Map.Entry<K, V> entry = i.next();
- sortedMap.put(entry.getKey(), entry.getValue());
- }
- notify();
- }
- subMaps.add(new SoftReference(sortedMap, _cacheBack));
- return sortedMap.tailMap(tailKey);
- }
-
- /***/
- public K firstKey() {
- Set<Map.Entry<K, V>> entries = entrySet();
- SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
- synchronized (this) {
- for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
- Map.Entry<K, V> entry = i.next();
- if (entry instanceof Map.Entry) {
- sortedMap.put(entry.getKey(), entry.getValue());
- }
- }
- notify();
- }
- subMaps.add(new SoftReference(sortedMap, _cacheBack));
- return sortedMap.firstKey();
- }
-
- /***/
- public void setListCapacity(int listCapacity) {
- this.listCapacity = listCapacity;
- notifyEvent(CAPACITY_EXTENDED);
- }
-
- /***/
- public int getListCapacity() {
- return listCapacity;
- }
-
- /***/
- public K lastKey() {
- Set<Map.Entry<K, V>> entries = entrySet();
- SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
- synchronized (this) {
- for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
- Map.Entry<K, V> entry = i.next();
- sortedMap.put(entry.getKey(), entry.getValue());
- }
- notify();
- }
- subMaps.add(new SoftReference(sortedMap, _cacheBack));
- return sortedMap.lastKey();
- }
-
- /***/
- public V putIfAbsent(K k, V v) {
- if (!has(k)) {
- return put(k, v);
- } else {
- return get(k);
- }
- }
-
- /***/
- public boolean remove(Object k, Object v) {
- V current;
- if ((current = get(k)) != null) {
- if (!current.equals(v)) {
- remove(k);
- return true;
- }
- }
- return false;
- }
-
- /***/
- public boolean replace(K key, V oldValue, V newValue) {
- V current = get(key);
- if (current != null) {
- if (current.equals(oldValue)) {
- put(key, newValue);
- return true;
- }
- }
- return false;
- }
-
- /***/
- public V replace(K key, V value) {
- if (has(key)) {
- return put(key, value);
- } else {
- return null;
- }
- }
-
- /** We define our own subclass of SoftReference which contains
- * not only the value but also the key to make it easier to find
- * the entry in the HashMap after it's been garbage collected. */
- class SoftValue<K, V> extends SoftReference<V> {
-
- final K key; // always make data member final
-
- /** Did you know that an outer class can access private data
- * members and methods of an inner class? I didn't know that!
- * I thought it was only the inner class who could access the
- * outer class's private information. An outer class can also
- * access private members of an inner class inside its inner
- * class. */
- SoftValue(V k, K key, ReferenceQueue<? super V> q) {
- super(k, q);
- this.key = key;
- }
- }
- }
package sf3.system;
import installer.ExampleFileFilter;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import sf3.system.Threaded;
/** Management of cache for many objects, thus the errors of java heap space are avoided at the time of the execution:)
* The listCapacity value will set the limit of elements simultaneously to be loaded into the heap memory space, then the default value 1 is the best choice if you enable swap (1 living element in the heap space while the other will be swapped into files). Other values will cause OutOfMemoryError when adding new elements if the heap memory space limit is over, then it would be recommended to use more frequentely the memorySensitiveCallback to load the elements before to add them.
*Implementation note: Unlike other Maps, the Collections-views Iterators returned by this Map don't support directly removals, or removals won't be reflected to the base Map. Use the keys to make the correct removals!
*/
public class SpritesCacheManager<K, V> implements ConcurrentMap<K, V>, SortedMap<K, V>, Serializable, Threaded {
/** serial version uid of this class */
private static final long serialVersionUID = 2323;
/** @see SpritesCacheListener
* instances associated to the cache */
private transient Set<SpritesCacheListener> listeners = Collections.synchronizedSet(new HashSet<SpritesCacheListener>());
/** last thrown error */
private Throwable lastError;
/** list capacity of the implemented maps*/
private int listCapacity;
/***/
private int initialListCapacity;
/** last recently used sprites */
private transient Map<K, V> lru = Collections.synchronizedMap(new HashMap<K, V>(listCapacity));
/** last recently used keys */
private transient List<K> lruK = Collections.synchronizedList(new Stack<K>());
/** most recently used sprites */
private transient Map<K, V> mru = Collections.synchronizedMap(new HashMap<K, V>(listCapacity));
/** most recentrly used keys */
private transient List<K> mruK = Collections.synchronizedList(new Stack<K>());
/** softly cached sprites (synchronizedMap)*/
private transient Map<K, SoftValue<K, V>> cache;
/** references queue to poll by the Garbage Collector*/
public transient ReferenceQueue<? super V> _cacheBack = new ReferenceQueue<Object>();
/** file cached sprites */
private SortedMap<K, File> cacheDisk;
/** compress switch @default false */
private boolean compress = false;
/** swap switch @default false*/
private boolean swap = false;
/** timer instance to use memory auto cleaning up*/
private java.util.Timer timer = null;
/** file swapping extension @default sp3*/
protected String cacheDisk_ext = "sp3";
/** disk directory of file swapping @default .cache*/
protected String cacheDisk_dir = "cache";
/** files prefix */
protected String cacheDisk_prefix = "_cache";
/***/
protected boolean debug = true;
/***/
protected transient boolean writing = false;
/***/
protected transient CoalescedThreadsMonitor writeMonitor;
/***/
protected transient boolean reading = false;
/***/
protected transient CoalescedThreadsMonitor readMonitor;
/***/
protected transient Vector<SoftReference<Map<K, V>>> subMaps = new Vector<SoftReference<Map<K, V>>>();
/***/
protected transient Vector<SoftReference<Set<K>>> keysSubLists = new Vector<SoftReference<Set<K>>>();
/***/
protected transient Vector<SoftReference<Set<V>>> valuesSubLists = new Vector<SoftReference<Set<V>>>();
/***/
public void setDebugEnabled(boolean b) {
debug = b;
}
/***/
public boolean isDebugEnabled() {
return debug;
}
/***/
private void debug(Object message) {
if (debug) {
System.err.println(message);
}
}
/**
* buffered values are referenced by Reference instances that are linked to the main ReferenceQueue so that the buffer is cleaned up every time cleanup() is called.* e.g. Object[] value = new Object[100];
* this.buffer(value);
* value = null; // then value is discarded within a short delay by the GarbageCollector.
*
* @see #_cacheBack
* @see #cleanup()
*/
private transient Map<Integer, SoftValue> buffer = Collections.synchronizedMap(new HashMap<Integer, SoftValue>());
/***/
public SpritesCacheManager(Comparator<? super K> c, int capacity) {
this(capacity);
comparator = c;
}
/***/
public SpritesCacheManager(Comparator<? super K> c) {
this(c, 1);
}
/** no arg contructor , memory will be limited to one living element in cache*/
public SpritesCacheManager() {
this(1);
}
/** Contructs the cache with initialized capacity and a WeakHashMap for the main living cache.
*@see #listCapacity
*@see WeakHashMap
* @param capacity cache memory init and sensitive limit
* @see #memorySensitiveCallback(Object, Object)*/
public SpritesCacheManager(int capacity) {
/*for(Method method : getClass().getMethods()) {
for(Type generic : method.getGenericParameterTypes()) {
for(Class<?> normal : method.getParameterTypes())
System.out.println(method.getName() + " : generics params : " + generic + " normal : " + normal);
}
}*/
File d;
if (!(d = new File(cacheDisk_dir)).isDirectory()) {
d.mkdirs();
}
listCapacity = initialListCapacity = capacity;
setThreadGroup(new CoalescedThreadsMonitor(getClass().getName() + " TG"));
cache = Collections.synchronizedMap(new WeakHashMap<K, SoftValue<K, V>>(capacity));
buffer(cache);
cacheDisk = Collections.synchronizedSortedMap(new TreeMap<K, File>());
buffer(cacheDisk);
}
/***/
public void setThreadGroup(ThreadGroup tg) {
if (writeMonitor instanceof ThreadGroup) {
writeMonitor.interrupt();
}
if (readMonitor instanceof ThreadGroup) {
readMonitor.interrupt();
}
if (tg instanceof ThreadGroup) {
writeMonitor = new CoalescedThreadsMonitor(tg, "TG-" + getClass().getName() + "-write");
readMonitor = new CoalescedThreadsMonitor(tg, "TG-" + getClass().getName() + "-read");
} else {
writeMonitor = new CoalescedThreadsMonitor("TG-" + getClass().getName() + "-write");
readMonitor = new CoalescedThreadsMonitor("TG-" + getClass().getName() + "-read");
}
writeMonitor.setCoalesceEnabled(false);
readMonitor.setCoalesceEnabled(false);
}
/***/
public ThreadGroup getThreadGroup() {
return writeMonitor.getParent();
}
/***/
public int getInitialListCapacity() {
return initialListCapacity;
}
/** trunks the cache maps to fit lists capacity
* @see #listCapacity
* @param ru recently used map
* @see #lru
* @param ruK recently used keys
* @see #lruK
* @param n desired trunk-size*/
private void trunk(Map<K, V> ru, List<K> ruK, int n) {
while (ru.size() > n) {
ru.remove(ruK.remove(ruK.size() - 1)); // fit to cache capacity, pop oldest added entry
}
cleanup();
}
/** the memory sensitive update to the cache Recently Used lists/maps. overflow-protected by a memory sensitive-calback
* @see #memorySensitiveCallback(Object, Object)
* @param key the key to update to
* @param sp the associated object to update to*/
private void memorySensitiveUpdate(K key, V sp) throws Throwable {
memorySensitiveCallback("memoryUpdate", this, new Object[]{key, sp}, new Class[]{Object.class, Object.class});
}
/** memory lists are updated to the given pair K,V
* outdated elements are removed. this method needs to be overflow-protected, so we use a memory sensitive-callback to avoid overflow.
* @see #memorySensitiveUpdate(Object, Object)
* @param key the key to update to
* @param sp the associated object to update to*/
public void memoryUpdate(K key, V sp) {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
if (lru.containsKey(key)) {
// if the same sprite has been found in last used items then it will be added to the most used items
mru.put(key, sp);
mruK.add(0, key);
trunk(mru, mruK, listCapacity);
}
// here we update last recently used list
lru.put(key, sp);
lruK.add(0, key);
trunk(lru, lruK, listCapacity);
Thread.currentThread().setPriority(currentPty);
}
/** removes strong references and key references to object , thus it can be garbage collected . overflow-protected by a memory sensitive-callback.
* @see #memorySensitiveCallback(Object, Object)
*@param sp the object to be cleared of memory
*@return the cleared object (same as the parameter)
*/
private V memorySensitiveClear(V sp) throws Throwable {
return (V) memorySensitiveCallback("memoryClear", this, new Object[]{sp}, new Class[]{Object.class});
}
/** removes all strong references to the given Object. not overflow-protected, we use a memory sensitive-callback.
* @see #memorySensitiveClear(Object)
*@param sp the object to be cleared of memory
*@return the cleared object (same as the parameter)*/
public V memoryClear(V sp) {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
if (lru.containsValue(sp)) {
Set<Map.Entry<K, V>> set = lru.entrySet();
synchronized (lru) {
Iterator<Map.Entry<K, V>> i0 = set.iterator();
for (buffer(i0); i0.hasNext();) {
// check for any value in LRU
Map.Entry<K, V> entry = i0.next();
buffer(entry);
V value = (entry != null) ? entry.getValue() : null;
buffer(value);
if (value != null) {
if (value.equals(sp)) {
synchronized (lruK) {
Iterator<K> i1 = lruK.iterator();
for (buffer(i1); i1.hasNext();) {
// also remove keys in stack
if (i1.next().equals(entry.getKey())) {
i1.remove();
}
}
i0.remove();
}
}
}
}
}
}
if (mru.containsValue(sp)) {
Set<Map.Entry<K, V>> set = mru.entrySet();
synchronized (mru) {
Iterator<Map.Entry<K, V>> i0 = set.iterator();
for (buffer(i0); i0.hasNext();) {
// check for any value in MRU
Map.Entry<K, V> entry = i0.next();
buffer(entry);
V value = (entry != null) ? entry.getValue() : null;
buffer(value);
if (value != null) {
if (value.equals(sp)) {
synchronized (mruK) {
Iterator<K> i1 = mruK.iterator();
for (buffer(i1); i1.hasNext();) {
// also remove keys in stack
if (i1.next().equals(entry.getKey())) {
i1.remove();
}
}
i0.remove();
}
}
}
}
}
}
Thread.currentThread().setPriority(currentPty);
return sp;
}
/** clears the mapping memory */
protected void clearMemory() {
mru.clear();
mruK.clear();
lru.clear();
lruK.clear();
}
/** compresses cache object
* @param pSp the object to compress
* @return compressed byte array of the argument */
private void compress(Serializable sp, OutputStream out) {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
cleanup();
buffer(sp);
buffer(out);
try {
ZipEntry ze = new ZipEntry("sp_" + sp.hashCode());
ZipOutputStream zip = new ZipOutputStream(out);
zip.putNextEntry(ze);
buffer(ze);
buffer(zip);
ObjectOutputStream oos = new ObjectOutputStream(zip);
oos.writeObject(sp);
zip.closeEntry();
zip.finish();
/*zip.close();
oos.close();*/
debug("Compressed Sprite caching " + ze.getCompressedSize() + " !");
} catch (IOException ex) {
ex.printStackTrace();
} finally {
Thread.currentThread().setPriority(pty);
}
}
/** return the synchronized map of file swapping
* @return file swapping map that has been synchronized */
public Map<K, File> getSwapMap() {
return cacheDisk;
}
/** uncompresses cache object
* @param b the compressed data to use
* @return the uncompressed object*/
private Serializable uncompress(InputStream in) throws NullPointerException {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
cleanup();
Serializable o = null;
ZipInputStream zis = new ZipInputStream(in);
buffer(zis);
ObjectInputStream ois;
try {
zis.getNextEntry();
ois = new ObjectInputStream(zis);
buffer(ois);
o = (Serializable) ois.readObject();
buffer(o);
zis.closeEntry();
/*zis.close();
ois.close();*/
} catch (EOFException e) {
debug("CacheEntry: uncompress: done.");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
zis = null;
ois = null;
if (o == null) {
throw new NullPointerException("Null CacheEntry: cannot uncompress!");
}
Thread.currentThread().setPriority(pty);
return o;
}
}
/** writes the given mapping to swap. this method doesn't need to be synchronized or overflow-protected otherwise it will cause a dead-lock.
* @param pKey the key
* @param pValue the associated object
* @return true or false whether it has succeeded*/
private boolean writeSwap(K key, Serializable value) {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
cleanup();
notifyWrite(WRITE_STARTED);
buffer(key);
buffer(value);
File f = null;
RandomAccessFile raf = null;
ObjectOutputStream oos;
FileOutputStream fos;
final boolean compress0 = compress;
boolean interrupt_ = false;
try {
synchronized (readMonitor.getMonitor(false)) {
while (reading) {
readMonitor.waitOnMonitor(10);
}
writing = true;
File dir = new File(cacheDisk_dir);
dir.mkdirs();
f = File.createTempFile(cacheDisk_prefix + hashCode(), key + "." + cacheDisk_ext, dir);
buffer(f);
raf = new RandomAccessFile(f, "rws");
fos = new FileOutputStream(raf.getFD());
//FileLock fl = fos.getChannel().lock();
oos = new ObjectOutputStream(fos);
buffer(oos);
buffer(fos);
oos.writeBoolean(compress0);
if (compress0) {
compress(value, fos);
} else {
oos.writeObject(value);
}
//fl.release();
oos.close();
cacheDisk.put(key, f);
f.deleteOnExit();
notifyWrite(WRITE_COMPLETED);
oos = null;
fos = null;
value = null;
Thread.currentThread().setPriority(pty);
readMonitor.notifyOnMonitor();
}
writing = false;
synchronized (writeMonitor.getMonitor(false)) {
writeMonitor.notifyAllOnMonitor();
}
return true;
} catch (Exception e) {
if (e instanceof InterruptedException) {
interrupt_ = true;
}
e.printStackTrace();
lastError = e;
notifyWrite(WRITE_ERROR);
if (f != null) {
f.delete();
notifyWrite(WRITE_ABORTED);
}
writing = false;
synchronized (writeMonitor.getMonitor(false)) {
writeMonitor.notifyAllOnMonitor();
}
if (interrupt_) {
Thread.currentThread().interrupt();
}
return false;
}
}
/** reads swap. this method doesn't need to be synchronized or overflow-protected otherwise it will cause a dead-lock.
* @param pKey the key to read from
* @return the value-object read*/
private V readSwap(K key) {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
cleanup();
notifyRead(READ_STARTED);
V value = null;
File f = null;
RandomAccessFile raf = null;
ObjectInputStream ois;
FileInputStream fis;
boolean interrupt_ = false;
try {
synchronized (writeMonitor.getMonitor(false)) {
while (writing) {
writeMonitor.waitOnMonitor(10);
}
reading = true;
if (cacheDisk.containsKey(key)) {
debug("Swap: Found Key " + key + " ");
f = (File) cacheDisk.get(key);
debug(f.getCanonicalPath());
raf = new RandomAccessFile(f, "r");
fis = new FileInputStream(raf.getFD());
//FileLock fl = fis.getChannel().lock(0L, Long.MAX_VALUE, true);
debug("opening...");
ois = new ObjectInputStream(fis);
buffer(ois);
buffer(fis);
if (ois.readBoolean()) {
value = (V) uncompress(fis);
} else {
value = (V) ois.readObject();
}
//fl.release();
buffer(value);
ois.close();
}
writeMonitor.notifyOnMonitor();
}
} catch (Exception e) {
if (e instanceof OptionalDataException) {
OptionalDataException opt = (OptionalDataException) e;
debug("OPTIONAL DATA EXCEPTION : eof=" + opt.eof + " bytes=" + opt.length);
} else if (e instanceof InterruptedException) {
interrupt_ = true;
}
e.printStackTrace();
lastError = e;
notifyRead(READ_ERROR);
} finally {
ois = null;
fis = null;
if (value == null) {
notifyRead(READ_ABORTED);
} else {
debug("Swap: reading done.");
notifyRead(READ_COMPLETED);
}
Thread.currentThread().setPriority(pty);
reading = false;
synchronized (readMonitor.getMonitor(false)) {
readMonitor.notifyAllOnMonitor();
}
if (interrupt_) {
Thread.currentThread().interrupt();
}
return value;
}
}
/** removes file swapping for the given key
* @param key the key to remove file swap
* @return true or false whether it has succeeded or not*/
private boolean unswap(K key) {
File f = cacheDisk.remove(key);
buffer(f);
try {
if (f instanceof File) {
f.delete();
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/** enable compression of the cache entries
* @param b compression dis/enabled*/
public void setCompressionEnabled(boolean b) {
compress = b;
}
/***/
private Thread getShutdownHooker() {
return new Thread(new Runnable() {
public void run() {
SpritesCacheManager.this.cleanFileSwap();
}
});
}
/** enable file swap to disk of the cache entries (recommended)
* @param b swapping dis/enabled */
public void setSwapDiskEnabled(boolean b) {
if (b) {
if (!swap) {
Runtime.getRuntime().addShutdownHook(getShutdownHooker());
}
} else {
Runtime.getRuntime().removeShutdownHook(getShutdownHooker());
}
swap = b;
}
/** checks whether swapping is enabled or not.
* @see #setSwapDiskEnabled(boolean)
* @return true or false*/
public boolean isSwapDiskEnabled() {
return swap;
}
/** current cache open size
* @return size of the living cache (excluding file swap cache)
* @see #cacheDisk
* @see #cache*/
public int size() {
try {
cleanup();
} catch (Exception e) {
} finally {
return cache.size();
}
}
/** memory heap allocation size in cache calculated in percentage of total capacity
* @return the current memory allocation in percent of the lists capactity
* @see #listCapacity*/
public double allocSize() {
double d = 100 * cache.size() / listCapacity;
debug("alloc=" + d);
return d;
}
/** adds the given mapping to cache
* @see #setSwapDiskEnabled(boolean)
* @param key the key
* @param obj the associated object
* @return the privous mapped object or null*/
public V add(K key, V obj) {
return add(key, obj, swap);
}
/***/
private void updateSubMaps(boolean removal, K key, V value) {
for (Iterator<SoftReference<Map<K, V>>> i = subMaps.iterator(); i.hasNext();) {
SoftReference<Map<K, V>> ref = i.next();
Map<K, V> subMap = (Map<K, V>) ref.get();
if (subMap instanceof Map) {
if (removal) {
subMap.remove(key);
} else {
subMap.put(key, value);
}
}
}
}
/***/
private void updateSubListsK(boolean removal, K key) {
for (Iterator<SoftReference<Set<K>>> i = keysSubLists.iterator(); i.hasNext();) {
SoftReference<Set<K>> listRef = i.next();
Set<K> subList = (Set<K>) listRef.get();
if (subList instanceof Set) {
if (removal) {
subList.remove(key);
} else {
subList.add(key);
}
}
}
}
/***/
private void updateSubListsV(boolean removal, V value) {
for (Iterator<SoftReference<Set<V>>> i = valuesSubLists.iterator(); i.hasNext();) {
SoftReference<Set<V>> listRef = i.next();
Set<V> subList = (Set<V>) listRef.get();
if (subList instanceof Set) {
if (removal) {
subList.remove(value);
} else {
subList.add(value);
}
}
}
}
/** adds the mapping to cache with the swap option
* @param key the key
* @param obj the associated object
* @param swap option to dis/enabled swap to this key
* @return the privous mapped object or null*/
protected V add(K key, V obj, boolean swap) {
cleanup();
SoftValue<K, V> ancient = null;
try {
if (obj != null) {
memorySensitiveUpdate(key, obj);
ancient = (SoftValue<K, V>) cache.put(key, new SoftValue(obj, key, _cacheBack));
buffer(ancient);
updateSubMaps(false, key, obj);
updateSubListsK(false, key);
updateSubListsV(false, obj);
if (ancient != null) {
V v = (V) ancient.get();
if (v != null) {
if (!v.equals(obj)) {
memorySensitiveClear((V) ancient.get());
}
}
}
if (swap) {
if (!writeSwap(key, (Serializable) obj)) {
throw new Exception("Unable to swap! " + key);
}
}
}
} catch (Exception e) {
debug("CacheEntry: error while caching " + key + e.getMessage() + "\r\n");
e.printStackTrace();
} finally {
return (ancient instanceof SoftReference) ? (V) ancient.get() : null;
}
}
/** safely removes Object from cache (it can be either a key referencing an object or an object currently cached)
* @param sp can be either a key or a value; if the key-class is the same as value-class then it will be used as a key
* @return the removed object
*/
public V remove(Object sp) {
try {
K key = (K) sp;
SoftValue<K, V> ref = cache.remove(key);
updateSubMaps(true, key, null);
updateSubListsK(true, key);
updateSubListsV(true, (V) ref.get());
V value = (ref instanceof SoftValue) ? (V) ref.get() : null;
synchronized (lruK) {
Iterator<K> i = lruK.iterator();
for (buffer(i); i.hasNext();) {
if (i.next().equals(key)) {
i.remove();
}
}
}
synchronized (mruK) {
Iterator<K> i = mruK.iterator();
for (buffer(i); i.hasNext();) {
if (i.next().equals(key)) {
i.remove();
}
}
}
lru.remove(key);
mru.remove(key);
if (swap) {
unswap(key);
}
return value;
} catch (ClassCastException e) {
try {
return memorySensitiveClear((V) sp);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
}
/** Returns the k-referenced object allocated in memory-cache
* @param k the key that references the object
* @return V the referenced object*/
public V get(Object k) {
cleanup();
K key = (K) k;
V value = null;
if (cache.containsKey(key)) {
debug("retrieving softValue referent");
value = (V) cache.get(key).get();
} else if (swap && cacheDisk.containsKey(key)) {
debug("Get key " + key + " from swap...");
value = readSwap(key);
add(key, value, false);
}
if (value != null) {
try {
memorySensitiveUpdate(key, value);
} catch (Throwable t) {
t.printStackTrace();
}
}
return value;
}
/** Checks for this key availability in memory
* @param key the key to look for
* @return true or false */
public boolean has(K key) {
cleanup();
return (cache.containsKey(key)) ? true : ((swap) ? cacheDisk.containsKey(key) : false);
}
/***/
public void clearMemorySwap() {
if (swap) {
Set<Map.Entry<K, File>> set = cacheDisk.entrySet();
synchronized (cacheDisk) {
for (Iterator<Map.Entry<K, File>> i = set.iterator(); i.hasNext();) {
Map.Entry<K, File> entry = i.next();
File f = entry.getValue();
f.delete();
cacheDisk.remove(entry.getKey());
}
}
}
}
/***/
private void clearSubMaps() {
for (Iterator<SoftReference<Map<K, V>>> i = subMaps.iterator(); i.hasNext();) {
SoftReference<Map<K, V>> mapRef = i.next();
Map<K, V> subMap = (Map<K, V>) mapRef.get();
if (subMap instanceof Map) {
subMap.clear();
}
}
}
/***/
private void clearSubListsK() {
for (Iterator<SoftReference<Set<K>>> i = keysSubLists.iterator(); i.hasNext();) {
SoftReference<Set<K>> listRef = i.next();
Set<K> subList = (Set<K>) listRef.get();
if (subList instanceof Set) {
subList.clear();
}
}
}
/***/
private void clearSubListsV() {
for (Iterator<SoftReference<Set<V>>> i = valuesSubLists.iterator(); i.hasNext();) {
SoftReference<Set<V>> listRef = i.next();
Set<V> subList = (Set<V>) listRef.get();
if (subList instanceof Set) {
subList.clear();
}
}
}
/** clear memory allocation maps */
public void clear() {
clearSubMaps();
clearSubListsK();
clearSubListsV();
clearMemory();
cache.clear();
cleanup();
}
/** safely cleans up the cache to clear unused referenced-objects */
public void cleanup() {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Reference ref;
while ((ref = _cacheBack.poll()) != null) {
try {
if (ref instanceof SoftValue) {
K key = ((SoftValue<K, V>) ref).key;
cache.remove(key);
buffer.remove(key);
}
if (ref instanceof Reference) {
ref.clear();
}
debug("SoftCache cleaned up");
} catch (Exception e) {
debug("SoftCache cleanup error :");
e.printStackTrace();
}
}
Thread.currentThread().setPriority(currentPty);
}
/** Enables cleanup of garbage queue originally done by the Garbage Collector (enabling this can cause a global thread overflow)
* @see #cleanup()
* @param b automatic cleanup dis/enabled */
public void setAutoCleanupEnabled(boolean b) {
if (timer != null) {
timer.cancel();
timer.purge();
}
if (b) {
timer = new java.util.Timer("Timer-SpritesCacheManager-Cleanup");
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 1);
cleanup();
}
}, 0L, 20);
buffer(timer);
} else {
timer = null;
}
}
/***/
public void ensureListCapacity(int n) {
listCapacity = Math.max(listCapacity, n);
notifyEvent(CAPACITY_EXTENDED);
}
/** adjust cache to a given capacity (amount of objects that will stay alive unless they cause an heap overflow)
* @param n amount of heap objects to keep alive*/
public void trimCacheLive(int n) {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
trunk(mru, mruK, n);
trunk(lru, lruK, n);
listCapacity = n;
notifyEvent(CAPACITY_EXTENDED);
Thread.currentThread().setPriority(pty);
}
/** Callbacks to invoked function
* @param method the method named to make the callback
* @param target the targeted object by this method callback
* @param args the Objects arguments of the method callback
* @param clargs the Class arguments of the method callback
* @return usual returned value of the called back method*/
public static Object callback(String method, Object target, Object[] args, Class[] clargs) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
System.err.println("callback on " + target.getClass().getName() + "." + method + "() : target.toString()=" + target);
return target.getClass().getMethod(method, clargs).invoke(target, args);
}
/** Sets priority to invoked task
* @see Thread#setPriority(int)
* @param method the method named to make the callback
* @param target the targeted object by this method callback
* @param args the Objects arguments of the method callback
* @param clargs the Class arguments of the method callback
* @param level priority value from 0 to 9. (normal value is that one of Thread.NORM_PRIORITY)
* @return usual returned value of the called back method
* */
public static Object doPriority(String method, Object target, Object[] args, Class[] clargs, int level) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
int currentPty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(level);
Object result = callback(method, target, args, clargs);
Thread.currentThread().setPriority(currentPty);
return result;
}
/** This is the second important function of this cache. It calls back to invoked method with memory sensitivity selfcare, i.e. memory will not cause a crash of the JVM due to the memory limit defined by the listCapacity value.
* And eventually the callback will throw OutOfMemory to the cache, which is able to clear heap memory overflows. Least recently used elements will be discarded to clear memory space if they're not referenced elsewhere than in the cache.
*Caution of use: you SHOULD NOT use a callback method with any of the present cache class-methods, because of synchronization issues with Java that locks the Map instance used by this cache.
* @param method the method named to make the callback
* @param target the targeted object by this method callback
* @param args the Objects arguments of the method callback
* @param clargs the Class arguments of the method callback
* @return usual returned value of the called back method*/
public Object memorySensitiveCallback(String method, Object target, Object[] args, Class[] clargs) throws Throwable {
try {
return callback(method, target, args, clargs);
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
return null;
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
return null;
} catch (IllegalAccessException ex) {
ex.printStackTrace();
return null;
} catch (InvocationTargetException ex) {
Throwable t = ex.getTargetException();
buffer(t);
if (t instanceof OutOfMemoryError) {
if (listCapacity == 0) {
throw (OutOfMemoryError) t;
} else if (listCapacity == 1) {
clearMemory();
} else {
trimCacheLive((int) Math.floor((float)listCapacity / 2.0f));
}
cleanup();
return memorySensitiveCallback(method, target, args, clargs);
} else {
throw t;
}
}
}
/** overriden finalization method that clears the cache and stops any running activity*/
protected void finalize() throws Throwable {
if (timer != null) {
timer.cancel();
timer = null;
}
clear();
super.finalize();
}
/** cleans up the file swapping directory
* @see #cacheDisk_dir*/
public void cleanFileSwap() {
File dir = null;
dir = new File(cacheDisk_dir);
ExampleFileFilter ff = new ExampleFileFilter();
ff.addExtension(cacheDisk_ext);
File[] swapFiles = null;
if (dir.isDirectory()) {
swapFiles = dir.listFiles((java.io.FileFilter) ff);
}
for (File f : swapFiles) {
try {
f.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/** Buffers the object to avoid memory freeze when it has to be cleared off the memory.
* @discussion the buffer is saved into a synchronized WeakHashMap that refreshes itself through ReferenceQueue.poll()
* @param obj the object to "buffer"
* @see SoftValue
*/
public void buffer(Object obj) {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
cleanup();
if (obj != null) {
buffer.put(obj.hashCode(), new SoftValue(obj, obj.hashCode(), _cacheBack));
}
Thread.currentThread().setPriority(pty);
}
/** checks for the cache if it's empty
* @return true or false*/
public boolean isEmpty() {
return cache.isEmpty();
}
/** checks for a reference availability
*@param key the key to check for
* @return true or false */
public boolean containsKey(Object key) {
return has((K) key);
}
/** checks for a reference availability
* @param object the object to check for
* @return true or false*/
public boolean containsValue(Object object) {
Collection<SoftValue<K, V>> coll = cache.values();
synchronized (cache) {
for (Iterator<SoftValue<K, V>> i = coll.iterator(); i.hasNext();) {
Object ref = i.next().get();
if (ref != null) {
if (ref.equals(object)) {
return true;
}
}
}
}
return false;
}
/** adds a new mapping
* @see #add(Object, Object)
* @param key the key
* @param value the object
* @return the previous mapped object or null*/
public V put(K key, V value) {
return add(key, value);
}
/** adds a complete map to the cache
* @param map a map */
public void putAll(Map<? extends K, ? extends V> map) {
Map<? extends K, ? extends V> smap = Collections.synchronizedMap(map);
Set<? extends K> set = smap.keySet();
synchronized (smap) {
for (Iterator<? extends K> i = set.iterator(); i.hasNext();) {
K k = i.next();
put(k, smap.get(k));
}
}
}
/** returns the key-set of the cache
* @return the key-set*/
public Set<K> keySet() {
TreeSet<K> sortedKeys = new TreeSet<K>(comparator);
Set<K> set = (swap) ? cacheDisk.keySet() : cache.keySet();
synchronized ((swap) ? cacheDisk : cache) {
for (Iterator<K> i = set.iterator(); i.hasNext();) {
sortedKeys.add(i.next());
}
}
keysSubLists.add(new SoftReference(sortedKeys, _cacheBack));
return (Set<K>) sortedKeys;
}
/** retuns the cached objects collection
* @return collection of objects that are currently cached*/
public Collection<V> values() {
HashSet<V> cached = new HashSet<V>();
Set<K> set = (swap) ? cacheDisk.keySet() : cache.keySet();
synchronized ((swap) ? cacheDisk : cache) {
for (Iterator<K> i = set.iterator(); i.hasNext();) {
K key = i.next();
cached.add(get(key));
}
}
valuesSubLists.add(new SoftReference(cached, _cacheBack));
return (Collection<V>) cached;
}
/** the mapping entries
* @return the mapping entries containing the cached objects*/
public Set<Map.Entry<K, V>> entrySet() {
SortedMap<K, V> m = new TreeMap<K, V>(comparator);
Set<K> set = (swap) ? cacheDisk.keySet() : cache.keySet();
synchronized ((swap) ? cacheDisk : cache) {
for (Iterator<K> i = set.iterator(); i.hasNext();) {
K key = i.next();
m.put(key, get(key));
}
}
subMaps.add(new SoftReference(m, _cacheBack));
return m.entrySet();
}
/** writes the entire cache the the output.
* @param out the output */
private void writeObject(ObjectOutputStream out) throws IOException {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
out.defaultWriteObject();
Set<Map.Entry<K, File>> set = cacheDisk.entrySet();
synchronized (cacheDisk) {
for (Iterator<Map.Entry<K, File>> files = set.iterator(); files.hasNext();) {
Map.Entry<K, File> entry = files.next();
buffer(entry);
File f = entry.getValue();
buffer(f);
RandomAccessFile raf = new RandomAccessFile(f, "r");
FileInputStream fis = new FileInputStream(raf.getFD());
buffer(fis);
buffer(raf);
//FileLock fl = fis.getChannel().lock(0L, Long.MAX_VALUE, true);
// write swap : file name to recover swap < file length < file
out.writeObject(entry.getKey());
out.writeLong(f.length());
debug(f.length());
try {
byte[] b = new byte[(int) 512];
int readBytes = 0;
int writtenBytes = 0;
while ((readBytes = fis.read(b)) != -1) {
out.write(b, 0, readBytes);
writtenBytes += readBytes;
}
debug(writtenBytes);
// fl.release();
fis.close();
raf.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
fis = null;
raf = null;
cleanup();
}
}
}
Thread.currentThread().setPriority(pty);
}
/** reads the entire cache from the input.
* @param in input*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
int pty = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
in.defaultReadObject();
keysSubLists = new Vector<SoftReference<Set<K>>>();
valuesSubLists = new Vector<SoftReference<Set<V>>>();
subMaps = new Vector<SoftReference<Map<K, V>>>();
writing = false;
reading = false;
setThreadGroup(new CoalescedThreadsMonitor(getClass().getName() + " TG"));
listeners = Collections.synchronizedSet(new HashSet<SpritesCacheListener>());
_cacheBack = new ReferenceQueue<Object>();
buffer = Collections.synchronizedMap(new HashMap<Integer, SoftValue>());
buffer(buffer);
buffer(_cacheBack);
cache = Collections.synchronizedMap(new WeakHashMap<K, SoftValue<K, V>>(listCapacity));
buffer(cache);
lru = Collections.synchronizedMap(new HashMap<K, V>());
buffer(lru);
lruK = Collections.synchronizedList(new Stack<K>());
buffer(lruK);
mru = Collections.synchronizedMap(new HashMap<K, V>());
buffer(mru);
mruK = Collections.synchronizedList(new Stack<K>());
buffer(mruK);
File file = null;
boolean read = true;
do {
RandomAccessFile raf = null;
ObjectOutputStream oos = null;
FileOutputStream fos = null;
try {
file = cacheDisk.get((K) in.readObject());
long len = in.readLong();
buffer(len);
if (file.exists()) {
if (file.length() == len) {
in.skip(len);
continue;
}
}
file.deleteOnExit();
debug("read " + file);
raf = new RandomAccessFile(file, "rws");
raf.getChannel().truncate(0);
fos = new FileOutputStream(raf.getFD());
buffer(fos);
buffer(raf);
//FileLock fl = fos.getChannel().lock();
byte[] b = new byte[(int) 512];
int readBytes = 0;
int rBytes;
while ((rBytes = in.read(b)) != -1) {
fos.write(b, 0, rBytes);
readBytes += rBytes;
}
debug("bytes read: " + readBytes + " file length was: " + len);
//fl.release();
raf.close();
fos.close();
} catch (EOFException e) {
e.printStackTrace();
read = false;
} catch (Exception e) {
read = false;
if (e instanceof OptionalDataException) {
OptionalDataException opt = (OptionalDataException) e;
in.skipBytes(opt.length);
debug("skip" + opt.length);
read = true;
if (opt.eof) {
read = false;
}
} else {
e.printStackTrace();
}
} finally {
oos = null;
fos = null;
raf = null;
cleanup();
}
} while (read);
debug("SpritesCacheManager read from DataStream!");
Thread.currentThread().setPriority(pty);
}
/** adds a listener to the cache.
* @param l the listener*/
public void addSpritesCacheListener(SpritesCacheListener l) {
listeners.add(l);
}
/** removes one listener from the cache
* @param l the listener*/
public void removeSpritesCacheListener(SpritesCacheListener l) {
listeners.remove(l);
}
/** notifies all listeners of any write event
* @param event the event
* @see #WRITE_STARTED
* @see #WRITE_ABORTED
* @see #WRITE_COMPLETED
* @see #WRITE_ERROR*/
public void notifyWrite(int event) {
switch (event) {
case WRITE_STARTED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.writeStarted();
}
}
}
break;
case WRITE_ABORTED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.writeAborted();
}
}
}
break;
case WRITE_COMPLETED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.writeCompleted();
}
}
}
break;
case WRITE_ERROR:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.writeError((lastError != null) ? lastError.getStackTrace() : null);
}
}
}
break;
default:
}
}
/** notifies the listeners of any read event
* @param event the event
* @see #READ_STARTED
* @see #READ_ABORTED
* @see #READ_COMPLETED
* @see #READ_ERROR*/
public void notifyRead(int event) {
switch (event) {
case READ_STARTED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.readStarted();
}
}
}
break;
case READ_ABORTED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.readAborted();
}
}
}
break;
case READ_COMPLETED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.readCompleted();
}
}
}
break;
case READ_ERROR:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l != null) {
l.readError((lastError != null) ? lastError.getStackTrace() : null);
}
}
}
break;
default:
}
}
/** a "read started" event */
static final int READ_STARTED = 0;
/** a "read aborted" event */
static final int READ_ABORTED = 1;
/** a "read completed" event */
static final int READ_COMPLETED = 2;
/** a "read error" event */
static final int READ_ERROR = 3;
/** a "write started" event */
static final int WRITE_STARTED = 4;
/** a "write aborted" event */
static final int WRITE_ABORTED = 5;
/** a "write completed" event */
static final int WRITE_COMPLETED = 6;
/** a "write error" event */
static final int WRITE_ERROR = 7;
/** a "capacity extended" event */
static final int CAPACITY_EXTENDED = 8;
/** notifies the listeners for any event
* @see #notifyRead(int)
* @see #notifyWrite(int) */
public void notifyEvent(int event) {
switch (event) {
case READ_STARTED:
case READ_ABORTED:
case READ_COMPLETED:
case READ_ERROR:
notifyRead(event);
break;
case WRITE_STARTED:
case WRITE_ABORTED:
case WRITE_COMPLETED:
case WRITE_ERROR:
notifyWrite(event);
break;
case CAPACITY_EXTENDED:
synchronized (listeners) {
for (Iterator<SpritesCacheListener> i = listeners.iterator(); i.hasNext();) {
SpritesCacheListener l = i.next();
if (l instanceof SpritesCacheListener) {
l.capacityExtended(listCapacity);
}
}
}
break;
default:
}
}
/***/
public static File makeKeyFile(Serializable key, File f, String fileDir, String keyFilename, boolean compress) {
try {
FileInputStream fis = new FileInputStream(new RandomAccessFile(f, "r").getFD());
byte[] b = new byte[512];
SpritesCacheManager<Integer, byte[]> fileContents = new SpritesCacheManager<Integer, byte[]>((int) f.length());
fileContents.setSwapDiskEnabled(true);
int i = 0;
int readBytes;
while ((readBytes = fis.read(b)) != -1) {
byte[] rb = new byte[readBytes];
for (int j = 0; j < rb.length; j++) {
rb[j] = b[j];
}
fileContents.put(i++, rb);
}
fis.close();
return makeKeyFile(new Serializable[]{key}, new SpritesCacheManager[]{fileContents}, fileDir, keyFilename, compress);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
return null;
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
/***/
public static File makeKeyFile(Serializable[] key, Serializable[] serialData, String fileDir, String keyFilename, boolean compress) throws IndexOutOfBoundsException {
if (key.length != serialData.length) {
throw new IndexOutOfBoundsException("Keys and serial datas must be of the same length. there are " + key.length + " keys and " + serialData.length + " serial datas");
}
SpritesCacheManager<Serializable, Serializable> spm = new SpritesCacheManager<Serializable, Serializable>();
spm.setSwapDiskEnabled(true);
spm.setCompressionEnabled(compress);
for (int i = 0; i < key.length; i++) {
spm.put(key[i], serialData[i]);
}
new File(fileDir).mkdirs();
spm.cacheDisk_dir = fileDir;
File keyFile = new File(fileDir + File.separator + keyFilename);
ObjectOutputStream out;
try {
out = new ObjectOutputStream(new FileOutputStream(new RandomAccessFile(keyFile, "rws").getFD()));
out.writeObject(spm);
out.close();
return keyFile;
} catch (FileNotFoundException ex) {
ex.printStackTrace();
return null;
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
/***/
public static File extractKeyFile(File f, Serializable key, String extractFilename) {
try {
SpritesCacheManager<Integer, byte[]> serial = (SpritesCacheManager<Integer, byte[]>) extractKeyData(f, key);
if (serial == null) {
return null;
}
File xF = new File(extractFilename);
FileOutputStream fos = new FileOutputStream(new RandomAccessFile(xF, "rws").getFD());
for (int i = 0; i < serial.getSwapMap().size(); i++) {
fos.write(serial.readSwap(i));
}
fos.close();
return xF;
} catch (FileNotFoundException ex) {
ex.printStackTrace();
return null;
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
/***/
public static Serializable extractKeyData(File f, Serializable key) {
ObjectInputStream in;
try {
in = new ObjectInputStream(new FileInputStream(new RandomAccessFile(f, "rws").getFD()));
SpritesCacheManager<Serializable, Serializable> spm;
spm = (SpritesCacheManager<Serializable, Serializable>) in.readObject();
in.close();
Serializable serialData = null;
serialData = spm.readSwap(key);
return serialData;
} catch (FileNotFoundException ex) {
ex.printStackTrace();
return null;
} catch (IOException ex) {
ex.printStackTrace();
return null;
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}
/**
the comparator doesn't get Serialized. use setComparator after a deserialization process!
@see #setComparator(Comparator)*/
public transient Comparator<? super K> comparator = null;
/***/
public void setComparator(Comparator<? super K> c) {
comparator = c;
}
/***/
public Comparator<? super K> getComparator() {
return comparator();
}
/***/
public Comparator<? super K> comparator() {
return comparator;
}
/***/
public SortedMap<K, V> subMap(K fromKey, K toKey) {
Set<Map.Entry<K, V>> entries = entrySet();
SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
synchronized (this) {
for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
Map.Entry<K, V> entry = i.next();
sortedMap.put(entry.getKey(), entry.getValue());
}
notify();
}
subMaps.add(new SoftReference(sortedMap, _cacheBack));
return sortedMap.subMap(fromKey, toKey);
}
/***/
public SortedMap<K, V> headMap(K headKey) {
Set<Map.Entry<K, V>> entries = entrySet();
SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
synchronized (this) {
for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
Map.Entry<K, V> entry = i.next();
sortedMap.put(entry.getKey(), entry.getValue());
}
notify();
}
subMaps.add(new SoftReference(sortedMap, _cacheBack));
return sortedMap.headMap(headKey);
}
/***/
public SortedMap<K, V> tailMap(K tailKey) {
Set<Map.Entry<K, V>> entries = entrySet();
SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
synchronized (this) {
for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
Map.Entry<K, V> entry = i.next();
sortedMap.put(entry.getKey(), entry.getValue());
}
notify();
}
subMaps.add(new SoftReference(sortedMap, _cacheBack));
return sortedMap.tailMap(tailKey);
}
/***/
public K firstKey() {
Set<Map.Entry<K, V>> entries = entrySet();
SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
synchronized (this) {
for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
Map.Entry<K, V> entry = i.next();
if (entry instanceof Map.Entry) {
sortedMap.put(entry.getKey(), entry.getValue());
}
}
notify();
}
subMaps.add(new SoftReference(sortedMap, _cacheBack));
return sortedMap.firstKey();
}
/***/
public void setListCapacity(int listCapacity) {
this.listCapacity = listCapacity;
notifyEvent(CAPACITY_EXTENDED);
}
/***/
public int getListCapacity() {
return listCapacity;
}
/***/
public K lastKey() {
Set<Map.Entry<K, V>> entries = entrySet();
SortedMap<K, V> sortedMap = new TreeMap<K, V>(comparator);
synchronized (this) {
for (Iterator<Map.Entry<K, V>> i = entries.iterator(); i.hasNext();) {
Map.Entry<K, V> entry = i.next();
sortedMap.put(entry.getKey(), entry.getValue());
}
notify();
}
subMaps.add(new SoftReference(sortedMap, _cacheBack));
return sortedMap.lastKey();
}
/***/
public V putIfAbsent(K k, V v) {
if (!has(k)) {
return put(k, v);
} else {
return get(k);
}
}
/***/
public boolean remove(Object k, Object v) {
V current;
if ((current = get(k)) != null) {
if (!current.equals(v)) {
remove(k);
return true;
}
}
return false;
}
/***/
public boolean replace(K key, V oldValue, V newValue) {
V current = get(key);
if (current != null) {
if (current.equals(oldValue)) {
put(key, newValue);
return true;
}
}
return false;
}
/***/
public V replace(K key, V value) {
if (has(key)) {
return put(key, value);
} else {
return null;
}
}
/** We define our own subclass of SoftReference which contains
* not only the value but also the key to make it easier to find
* the entry in the HashMap after it's been garbage collected. */
class SoftValue<K, V> extends SoftReference<V> {
final K key; // always make data member final
/** Did you know that an outer class can access private data
* members and methods of an inner class? I didn't know that!
* I thought it was only the inner class who could access the
* outer class's private information. An outer class can also
* access private members of an inner class inside its inner
* class. */
SoftValue(V k, K key, ReferenceQueue<? super V> q) {
super(k, q);
this.key = key;
}
}
}
Conclusion
devx.com ou ibm.com peuvent être utiles... Sinon le schéma est arrangé pour le besoin de récupération des references en cache lorsque le Garbage collector passe effacer les données reférencées pour libérer la mémoire allouée.... Utiliser un Timer pour régulièrement "nettoyer" le cache (cleanup()) et recharger (add(K,V)) avec les objets utilisés. Des objets déjà en cache ne seront pas effacés s'il sont "solidement" référencés ailleurs (strong reference à l'inverse d'une soft reference). www.developpez.com propose un tutoriel sur les Reference en JAVA. (un moteur de recherche et un forum sont disponibles gratuitement)
Fichier Zip
Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !
Télécharger le zip
Historique
- 17 août 2006 21:27:43 :
- minor comment fix
- 19 août 2006 06:35:22 :
- Commentaires ajoutés.
- 19 août 2006 06:35:37 :
- Commentaires ajoutés.
- 06 septembre 2006 19:37:41 :
- discrimination répétée de la part des moderateurs, twinuts, etc.
- 17 octobre 2006 08:18:51 :
- cache mémoire pour gestion du java heap space
- 21 octobre 2006 01:45:57 :
- zip compression mode added
- 21 octobre 2006 02:30:17 :
- try finally...
- 21 octobre 2006 05:23:57 :
- Zip compression added now working!!!
- 30 octobre 2006 05:51:22 :
- meilleure lisibilité, bug fix
- 22 novembre 2006 01:33:25 :
- fix memorysensitivecallback
- 22 novembre 2006 01:35:57 :
- memorysensitive fix
- 13 décembre 2006 00:49:40 :
- fix, new zip demo
- 18 décembre 2006 02:10:09 :
- swap disk fix
- 21 décembre 2006 06:11:32 :
- synch fix
- 12 février 2007 02:24:14 :
- synchronize fix and demo 3.
- 17 février 2007 01:22:38 :
- phantomRef
- 19 février 2007 00:24:39 :
- little fix to cleanup method! enjoy!
- 05 mars 2007 11:30:54 :
- Threads priority extension
- 05 mars 2007 11:33:18 :
- Threads priority extension
- 13 mars 2007 12:30:45 :
- UML
- 23 mars 2007 17:02:45 :
- synch doPriority()
- 25 mars 2007 16:16:42 :
- deamon fix
- 23 avril 2007 01:57:34 :
- added nio to improvements
- 23 avril 2007 01:59:06 :
- added nio to improvements
- 23 avril 2007 08:20:42 :
- NIO OK SERIALIZE OK
- 14 mai 2007 00:43:40 :
- mapping fix et commentaires!
- 18 juin 2007 10:14:02 :
- compression fixed ok, key file i/o feature
- 11 juillet 2007 21:25:50 :
- fix pour la fermeture des Streams (testé avec 3000 images pour une centaine d'animations, sans problème)
- 15 juillet 2007 00:00:59 :
- serialize multi-clefs
- 15 juillet 2007 00:06:15 :
- serial
- 15 juillet 2007 00:32:51 :
- un/compress option
- 28 septembre 2007 03:23:57 :
- Collections views synchronize on the base cache. Iterating over the different views becomes safe.
Sources du même auteur
Sources de la même categorie
Sources en rapport avec celle ci
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
animation et threads [ par Aldee ]
N'y at-il que par un thread (que j'endors pendant N msec) que je peux controler une animation?Même pour une malheureuse loupiote qui clignote jaune/ro
Thread et bouton [ par Aldee ]
Salut, Je voudrais cree un Bouton qui, quand on le click, fasse D'ABORD une animation (une fleche apparait sur le bouton) et ENSUITE fait ce qu'on lui
Animation d'un carrefour [ par hdao ]
Bonjour,Je suis débutant et j'ai besoin d'aide. Je dois faire animer un carrefour. Si vous avez un source, envoyez moi s'il vous plait !
"Pragma","no-cache" [ par jmg02001 ]
Il Parrait que l'on peut paramettrer 1 pragma no cachemoi ai response.setHeader("Cache-Control","no-cache"); //HTTP 1.1response.setHeader("Pragma","no
Image dans JPanel cache mes boutons [ par Sw1tch ]
Bonjour,ca doit surement être tout bete, mais j'ai un petit problème.J'ai créé un JLabel qui à la meme taille que mon JPanel. J'y ai affecté un ImageI
Animation [ par molezi0 ]
Bonjour , ca serai sympa d'aider un débutant en java !! (désolé c'est un peu long)La structure de mon animation est :class ??? extends JFrame implemen
Probleme avec applet et cache classloader [ par lesaixol ]
Bonjour a tout le monde,j'ai une applet qui permet entre autre de communiquer avec une servlet pour telecharger des fichiers images afin de les affich
exporter une resultset sous Excel dans JSP [ par cargal ]
Bonjour,J'essaie d'exporter le resultat d un resultset dasn un eJSP sous excel sans succés.Je passe par un fichier cvs mais il ne souvre pas automatiq
java applet son image animation [ par ophelie688 ]
Bonjourj'aimerais créer un applet pour vendre une maisonCe que contient l'applet:Haut écriture A VENDRE et 10 images jpeg en animations(diapo genre)l'
[cache] URLConnection setUseCaches? [ par bidani ]
salut,j'aimerais savoir ce que la methode setUseCaches(BOOL) de la classe URLConnection fait exactement dans le cadre d'une requete http (GET)(client
|
Téléchargements
Logiciels à télécharger sur le même thème :
Comparez les prix Nouvelle version
|