filebot/source/net/filebot/CacheManager.java

176 lines
4.6 KiB
Java

package net.filebot;
import static java.nio.charset.StandardCharsets.*;
import static net.filebot.Logging.*;
import static net.filebot.Settings.*;
import static net.filebot.util.FileUtilities.*;
import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.StandardOpenOption;
import java.util.Scanner;
import java.util.logging.Level;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.DiskStoreConfiguration;
public class CacheManager {
private static final CacheManager instance = new CacheManager();
public static CacheManager getInstance() {
return instance;
}
private final File diskStore;
private final net.sf.ehcache.CacheManager manager;
public CacheManager() {
try {
this.diskStore = acquireDiskStore();
this.manager = net.sf.ehcache.CacheManager.create(new Configuration().diskStore(new DiskStoreConfiguration().path(diskStore.getPath())));
} catch (IOException e) {
throw new CacheException(e);
}
}
public synchronized Cache getCache(String name, CacheType type) {
if (!manager.cacheExists(name)) {
manager.addCache(new net.sf.ehcache.Cache(type.getConfiguration(name)));
}
return new Cache(manager.getCache(name), type);
}
public void flushAll() {
for (String n : manager.getCacheNames()) {
try {
manager.getCache(n).flush();
} catch (Exception e) {
debug.warning(e::toString);
}
}
}
public void clearAll() {
for (String n : manager.getCacheNames()) {
try {
manager.getCache(n).removeAll();
} catch (Exception e) {
debug.warning(e::toString);
}
}
}
public synchronized void shutdown() {
manager.shutdown();
}
private void clearDiskStore(File cache) {
getChildren(cache, FILES).stream().filter(f -> !f.getName().startsWith(".")).forEach(f -> {
try {
delete(f);
} catch (Exception e) {
debug.warning(format("Failed to delete cache: %s => %s", f, e));
}
});
}
private File acquireDiskStore() throws IOException {
for (int i = 0; i < 10; i++) {
File cache = ApplicationFolder.Cache.resolve(String.valueOf(i));
// make sure cache is readable and writable
createFolders(cache);
File lockFile = new File(cache, ".lock");
boolean isNewCache = !lockFile.exists();
FileChannel channel = FileChannel.open(lockFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
FileLock lock = channel.tryLock();
if (lock != null) {
debug.fine(format("Using persistent disk cache %s", cache));
int applicationRevision = getApplicationRevisionNumber();
int cacheRevision = 0;
if (channel.size() > 0) {
try {
cacheRevision = new Scanner(channel, "UTF-8").nextInt();
} catch (Exception e) {
debug.log(Level.WARNING, e, e::toString);
}
}
if (cacheRevision != applicationRevision && applicationRevision > 0 && !isNewCache) {
debug.warning(format("Current application revision (r%d) does not match cache revision (r%d): reset cache", applicationRevision, cacheRevision));
// tag cache with new revision number
isNewCache = true;
// delete all files related to previous cache instances
clearDiskStore(cache);
}
if (isNewCache) {
// set new cache revision
channel.position(0);
channel.write(UTF_8.encode(String.valueOf(applicationRevision)));
channel.truncate(channel.position());
}
// make sure to orderly shutdown cache
Runtime.getRuntime().addShutdownHook(new ShutdownHook(this, channel, lock));
// cache for this application instance is successfully set up and locked
return cache;
}
// try next lock file
channel.close();
}
// serious error, abort
throw new IOException("Unable to acquire cache lock: " + ApplicationFolder.Cache.get().getAbsolutePath());
}
private static class ShutdownHook extends Thread {
private final CacheManager manager;
private final FileChannel channel;
private final FileLock lock;
public ShutdownHook(CacheManager manager, FileChannel channel, FileLock lock) {
this.manager = manager;
this.channel = channel;
this.lock = lock;
}
@Override
public void run() {
try {
manager.shutdown();
} catch (Exception e) {
debug.log(Level.WARNING, "Shutdown hook failed: shutdown", e);
}
try {
lock.release();
} catch (Exception e) {
debug.log(Level.WARNING, "Shutdown hook failed: release", e);
}
try {
channel.close();
} catch (Exception e) {
debug.log(Level.WARNING, "Shutdown hook failed: close", e);
}
}
}
}