package quickutils.core.cache; import org.apache.commons.io.IOUtils; import java.io.BufferedOutputStream; import java.io.File; import java.io.FilterOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import quickutils.core.cache.disklrucache.DiskLruCache; class SimpleDiskCache { private static final int VALUE_IDX = 0; private static final int METADATA_IDX = 1; private static final List<File> usedDirs = new ArrayList<File>(); private final DiskLruCache diskLruCache; private SimpleDiskCache(File dir, int appVersion, long maxSize) throws IOException { diskLruCache = DiskLruCache.open(dir, appVersion, 2, maxSize); } static synchronized SimpleDiskCache open(File dir, int appVersion, long maxSize) throws IOException { if (usedDirs.contains(dir)) { throw new IllegalStateException("Cache dir " + dir.getAbsolutePath() + " was used " + "before."); } usedDirs.add(dir); return new SimpleDiskCache(dir, appVersion, maxSize); } void wipeData() throws IOException { diskLruCache.wipeAllContent(); } StringEntry getString(String key) throws IOException { DiskLruCache.Snapshot snapshot = diskLruCache.get(toInternalKey(key)); if (snapshot == null) return null; try { return new StringEntry(snapshot.getString(VALUE_IDX)); } finally { snapshot.close(); } } boolean contains(String key) throws IOException { DiskLruCache.Snapshot snapshot = diskLruCache.get(toInternalKey(key)); if (snapshot == null) return false; snapshot.close(); return true; } private OutputStream openStream(String key, Map<String, ? extends Serializable> metadata) throws IOException { DiskLruCache.Editor editor = diskLruCache.edit(toInternalKey(key)); try { writeMetadata(metadata, editor); BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX)); return new CacheOutputStream(bos, editor); } catch (IOException e) { editor.abort(); throw e; } } void put(String key, String value) throws IOException { put(key, value, new HashMap<String, Serializable>()); } void delete(String key) throws IOException { put(key, "", new HashMap<String, Serializable>()); } private void put(String key, String value, Map<String, ? extends Serializable> annotations) throws IOException { OutputStream cos = null; try { cos = openStream(key, annotations); cos.write(value.getBytes()); } finally { if (cos != null) cos.close(); } } private void writeMetadata(Map<String, ? extends Serializable> metadata, DiskLruCache.Editor editor) throws IOException { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new BufferedOutputStream(editor.newOutputStream (METADATA_IDX))); oos.writeObject(metadata); } finally { IOUtils.closeQuietly(oos); } } private String toInternalKey(String key) { return md5(key); } private String md5(String s) { try { MessageDigest m = MessageDigest.getInstance("MD5"); m.update(s.getBytes("UTF-8")); byte[] digest = m.digest(); BigInteger bigInt = new BigInteger(1, digest); return bigInt.toString(16); } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } catch (UnsupportedEncodingException e) { throw new AssertionError(); } } private class CacheOutputStream extends FilterOutputStream { private final DiskLruCache.Editor editor; private boolean failed = false; private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) { super(os); this.editor = editor; } @Override public void close() throws IOException { IOException closeException = null; try { super.close(); } catch (IOException e) { closeException = e; } if (failed) { editor.abort(); } else { editor.commit(); } if (closeException != null) throw closeException; } @Override public void flush() throws IOException { try { super.flush(); } catch (IOException e) { failed = true; throw e; } } @Override public void write(int oneByte) throws IOException { try { super.write(oneByte); } catch (IOException e) { failed = true; throw e; } } @Override public void write(byte[] buffer) throws IOException { try { super.write(buffer); } catch (IOException e) { failed = true; throw e; } } @Override public void write(byte[] buffer, int offset, int length) throws IOException { try { super.write(buffer, offset, length); } catch (IOException e) { failed = true; throw e; } } } static class StringEntry { private final String string; StringEntry(String string) { this.string = string; } String getString() { return string; } } }