package com.ecollege.android.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Calendar; import java.util.Iterator; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.commons.io.FileUtils; import android.content.Context; import com.ecollege.api.ECollegeHttpResponseCache; import com.ecollege.api.ECollegeHttpResponseHandler; public class FileCacheManager implements ECollegeHttpResponseCache { private final File cacheDir; private final long expirationInMillis; // in ms /** * FileCacheManager implements a cache system with the application cache directory on android. * * @param context * @param expiration A single expiration length (in ms) used for each cache file */ public FileCacheManager(Context context, long expirationInMillis) { cacheDir = context.getCacheDir(); this.expirationInMillis = expirationInMillis; } public CacheEntry getIfNewerThan(String cacheScope, String cacheKey, long maxMillisOld) { return getIfNewerThan(cacheScope, cacheKey, maxMillisOld, false); } public CacheEntry get(String cacheScope, String cacheKey) { return getIfNewerThan(cacheScope, cacheKey, expirationInMillis, true); } protected CacheEntry getIfNewerThan(String cacheScope, String cacheKey, long maxMillisOld, boolean deleteIfOlderThanMax) { File cacheFile = null; long lastModified = Calendar.getInstance().getTimeInMillis(); synchronized (cacheDir) { File cacheScopeDirectory = findOrCreateDirectoryForScope(cacheScope); cacheFile = new File(cacheScopeDirectory, cacheKey); synchronized (cacheScopeDirectory) { if (cacheFile.exists()) { lastModified = cacheFile.lastModified(); long now = Calendar.getInstance().getTimeInMillis(); if (now - lastModified > maxMillisOld) { if (deleteIfOlderThanMax) { invalidateCacheEntry(cacheFile); deleteCacheSubdirectoryIfEmpty(cacheScopeDirectory); } cacheFile = null; } } } } if (cacheFile != null && cacheFile.exists()){ GZIPInputStream fin = null; try { fin = new GZIPInputStream(new FileInputStream(cacheFile)); String cacheData = ECollegeHttpResponseHandler.streamToString(fin, null); return new CacheEntry(cacheData, lastModified); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fin != null) { try { fin.close(); } catch (IOException e) { e.printStackTrace(); } } } } return null; } public void put(String cacheScope, String cacheKey, String responseContent) { synchronized (cacheDir) { File cacheScopeDirectory = findOrCreateDirectoryForScope(cacheScope); synchronized (cacheScopeDirectory) { File cacheFile = new File(cacheScopeDirectory, cacheKey); GZIPOutputStream out = null; try { out = new GZIPOutputStream(new FileOutputStream(cacheFile)); out.write(responseContent.getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null) { out.finish(); out.close(); } } catch (IOException e) { e.printStackTrace(); } } } } } public void invalidateCacheScope(String cacheScope) { synchronized (cacheDir) { File cacheScopeDirectory = new File(cacheDir, cacheScope); synchronized (cacheScopeDirectory) { if (cacheScopeDirectory.exists()) { FileUtils.deleteQuietly(cacheScopeDirectory); cacheScopeDirectory = null; } } } } public void invalidateCacheKey(String cacheScope, String cacheKey) { synchronized (cacheDir) { File cacheScopeDirectory = new File(cacheDir, cacheScope); synchronized (cacheScopeDirectory) { if (cacheScopeDirectory.exists()) { File cacheFile = new File(cacheScopeDirectory, cacheKey); invalidateCacheEntry(cacheFile); } } } } public Integer removeInvalidEntries() { File entryFile; long lastModified; long now = Calendar.getInstance().getTimeInMillis(); int counter = 0; synchronized (cacheDir) { Iterator<File> cacheFileList = Arrays.asList(cacheDir.listFiles()).iterator(); while (cacheFileList.hasNext()) { entryFile = cacheFileList.next(); lastModified = entryFile.lastModified(); if (now - lastModified > expirationInMillis) { invalidateCacheEntry(entryFile); counter++; } } return counter; } } protected void invalidateCacheEntry(File file) { synchronized (cacheDir) { File parentDirectory = file.getParentFile(); file.delete(); file = null; // if the cache entry's parent directory is not the cacheDirectory itself (which was the case in an old implementation) if (!cacheDir.getAbsolutePath().equals(parentDirectory.getAbsolutePath())) { // delete the parent if empty deleteCacheSubdirectoryIfEmpty(parentDirectory); } } } protected File findOrCreateDirectoryForScope(String cacheScope) { synchronized (cacheDir) { File cacheScopeDir = new File(cacheDir, cacheScope); if (!cacheScopeDir.exists()) { try { cacheScopeDir.mkdir(); } catch (Exception e) { e.printStackTrace(); } } return cacheScopeDir; } } protected void deleteCacheSubdirectoryIfEmpty(File directory) { synchronized (cacheDir) { synchronized (directory) { if (directory.isDirectory()) { String[] paths = directory.list(); if (paths.length == 0) { FileUtils.deleteQuietly(directory); directory = null; } } } } } }