Merge branch 'feature/JAL-3187linkedFeatures' into feature/JAL-3251biotypedMappings
[jalview.git] / getdown / src / getdown / core / src / main / java / com / threerings / getdown / cache / GarbageCollector.java
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java
new file mode 100644 (file)
index 0000000..67ea645
--- /dev/null
@@ -0,0 +1,99 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.cache;
+
+import java.io.File;
+import com.threerings.getdown.util.FileUtil;
+
+/**
+ * Collects elements in the {@link ResourceCache cache} which became unused and deletes them
+ * afterwards.
+ */
+public class GarbageCollector
+{
+    /**
+     * Collect and delete the garbage in the cache.
+     */
+    public static void collect (File cacheDir, final long retentionPeriodMillis)
+    {
+        FileUtil.walkTree(cacheDir, new FileUtil.Visitor() {
+            @Override public void visit (File file) {
+                File cachedFile = getCachedFile(file);
+                File lastAccessedFile = getLastAccessedFile(file);
+                if (!cachedFile.exists() || !lastAccessedFile.exists()) {
+                    if (cachedFile.exists()) {
+                        FileUtil.deleteHarder(cachedFile);
+                    } else {
+                        FileUtil.deleteHarder(lastAccessedFile);
+                    }
+                } else if (shouldDelete(lastAccessedFile, retentionPeriodMillis)) {
+                    FileUtil.deleteHarder(lastAccessedFile);
+                    FileUtil.deleteHarder(cachedFile);
+                }
+
+                File folder = file.getParentFile();
+                if (folder != null) {
+                    String[] children = folder.list();
+                    if (children != null && children.length == 0) {
+                        FileUtil.deleteHarder(folder);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Collect and delete garbage in the native cache. It tries to find a jar file with a matching
+     * last modified file, and deletes the entire directory accordingly.
+     */
+    public static void collectNative (File cacheDir, final long retentionPeriodMillis)
+    {
+        File[] subdirs = cacheDir.listFiles();
+        if (subdirs != null) {
+            for (File dir : subdirs) {
+                if (dir.isDirectory()) {
+                    // Get all the native jars in the directory (there should only be one)
+                    for (File file : dir.listFiles()) {
+                        if (!file.getName().endsWith(".jar")) {
+                            continue;
+                        }
+                        File cachedFile = getCachedFile(file);
+                        File lastAccessedFile = getLastAccessedFile(file);
+                        if (!cachedFile.exists() || !lastAccessedFile.exists() ||
+                            shouldDelete(lastAccessedFile, retentionPeriodMillis)) {
+                            FileUtil.deleteDirHarder(dir);
+                        }
+                    }
+                } else {
+                    // @TODO There shouldn't be any loose files in native/ but if there are then
+                    // what? Delete them? file.delete();
+                }
+            }
+        }
+    }
+
+    private static boolean shouldDelete (File lastAccessedFile, long retentionMillis)
+    {
+        return System.currentTimeMillis() - lastAccessedFile.lastModified() > retentionMillis;
+    }
+
+    private static File getLastAccessedFile (File file)
+    {
+        return isLastAccessedFile(file) ? file : new File(
+            file.getParentFile(), file.getName() + ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
+    }
+
+    private static boolean isLastAccessedFile (File file)
+    {
+        return file.getName().endsWith(ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
+    }
+
+    private static File getCachedFile (File file)
+    {
+        return !isLastAccessedFile(file) ? file : new File(
+            file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf(".")));
+    }
+}