This is an automated email from the ASF dual-hosted git repository.

lkishalmi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new fcab7a1923 Rework Janitor, try to use real version numbers, in the 
future
fcab7a1923 is described below

commit fcab7a1923361bc2db40ef1a6f85bcef183b2311
Author: Laszlo Kishalmi <laszlo.kisha...@gmail.com>
AuthorDate: Mon Jul 3 18:19:51 2023 -0700

    Rework Janitor, try to use real version numbers, in the future
---
 .../src/org/netbeans/modules/janitor/Janitor.java  | 392 +++++++++++++--------
 .../modules/janitor/JanitorOptionsPanel.java       |   6 +-
 2 files changed, 254 insertions(+), 144 deletions(-)

diff --git a/platform/janitor/src/org/netbeans/modules/janitor/Janitor.java 
b/platform/janitor/src/org/netbeans/modules/janitor/Janitor.java
index 0fa2e56456..91763d194a 100644
--- a/platform/janitor/src/org/netbeans/modules/janitor/Janitor.java
+++ b/platform/janitor/src/org/netbeans/modules/janitor/Janitor.java
@@ -20,7 +20,9 @@ package org.netbeans.modules.janitor;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
@@ -29,7 +31,6 @@ import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.time.Duration;
 import java.time.Instant;
-import java.time.temporal.ChronoUnit;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -37,6 +38,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.prefs.Preferences;
 import javax.swing.Icon;
 import org.netbeans.api.annotations.common.StaticResource;
@@ -58,36 +61,31 @@ import org.openide.windows.OnShowing;
     "# {1} - the days of abandonement",
     "# {2} - the disk space can be reclaimed (in megabytes)",
     "TIT_ABANDONED_USERDIR=NetBeans {0} was last used {1} days ago.",
-
     "# {0} - the name of the abandoned cache dir.",
     "# {1} - the disk space can be reclaimed (in megabytes)",
     "TIT_ABANDONED_CACHEDIR=NetBeans cache directory {0} seems to be 
abandoned.",
-
     "# {0} - is the user directory name",
     "# {1} - the days of abandonement",
     "# {2} - the disk space can be reclaimed (in megabytes)",
     "DESC_ABANDONED_USERDIR=Remove unused data and cache directories of 
NetBeans {0}. "
-            + "Free up {2} MB of disk space.",
-
+    + "Free up {2} MB of disk space.",
     "# {0} - is the cache directory name",
     "# {1} - the disk space can be reclaimed (in megabytes)",
     "DESC_ABANDONED_CACHEDIR=NetBeans could not find a user dir for cache dir 
{0}, so it is probably abandoned. "
-            + "Remove abandoned cache dir, "
-            + "free up {1} MB of disk space.",
-
+    + "Remove abandoned cache dir, "
+    + "free up {1} MB of disk space.",
     "TIT_CONFIRM_CLEANUP=Confirm Cleanup",
-
     "# {0} - the dirname to be cleaned up",
     "TXT_CONFIRM_CLEANUP=Remove user and cache data for NetBeans {0}?",
-
-        "# {0} - the dirname to be cleaned up",
+    "# {0} - the dirname to be cleaned up",
     "TXT_CONFIRM_CACHE_CLEANUP=Remove abandoned cache dir?",
-
     "# {0} - the dirname to be cleaned up",
     "LBL_CLEANUP=Removing unused/abandoned user and/or cache dirs."
 })
 public class Janitor {
 
+    private static final Logger LOG = 
Logger.getLogger(Janitor.class.getName());
+    
     private static final int UNUSED_DAYS = 30;
 
     public static final String PROP_JANITOR_ENABLED = "janitorEnabled"; 
//NOI18N
@@ -96,39 +94,60 @@ public class Janitor {
 
     private static final String LOGFILE_NAME = "var/log/messages.log"; //NOI18N
     private static final String ALL_CHECKSUM_NAME = 
"lastModified/all-checksum.txt"; //NOI18N
+    private static final String LAST_VERSION_NAME = ".lastUsedVersion"; 
//NOI18N
+
+    private static final String NB_VERSION;
+
     @StaticResource
     private static final String CLEAN_ICON = 
"org/netbeans/modules/janitor/resources/clean.gif"; //NOI18N
 
     static final RequestProcessor JANITOR_RP = new RequestProcessor("janitor", 
1); //NOI18N
     static final Map<ActionListener, Notification> CLEANUP_TASKS = new 
WeakHashMap<>();
 
+    static {
+        String version = System.getProperty("netbeans.buildnumber"); //NOI18N
+        if (version != null) {
+            // remove git hash from the build number
+            int dash = version.lastIndexOf('-');
+            if (dash + 41 == version.length()) { // 40 chars for git SHA sum, 
1 for the dash
+                version = version.substring(0, dash);
+            }
+        }
+        NB_VERSION = version;
+    }
+
     static void scanForJunk() {
         // Remove previously opened notifications
         CLEANUP_TASKS.values().forEach((nf) -> nf.clear());
         CLEANUP_TASKS.clear();
 
         Icon clean = ImageUtilities.loadImageIcon(CLEAN_ICON, false);
-        List<Pair<String, Integer>> otherVersions = getOtherVersions();
-
-        for (Pair<String, Integer> ver : otherVersions) {
-            String name = ver.first();
-            Integer age = ver.second();
-            long toFree = size(getUserDir(name)) + size(getCacheDir(name));
-            toFree = toFree / (1_000_000) + 1;
-            if (getUserDir(name) != null) {
-                ActionListener cleanupListener = cleanupAction(name, 
Bundle.TXT_CONFIRM_CLEANUP(name));
-                Notification nf = NotificationDisplayer.getDefault().notify(
-                        Bundle.TIT_ABANDONED_USERDIR(name, age, toFree),
-                        clean,
-                        Bundle.DESC_ABANDONED_USERDIR(name, age, toFree),
-                        cleanupListener);
-
-                CLEANUP_TASKS.put(cleanupListener, nf);
+        List<CleanupPair> candidates = getCandidates();
+
+        Instant now = Instant.now();
+        int maxUnused = getUnusedDays();
+        
+        for (CleanupPair candidate : candidates) {
+            int age = candidate.age(now);
+            int toFree = candidate.size();
+            String name = candidate.getName();
+            if (candidate.userdir != null) {
+                if (age > maxUnused) {
+                    ActionListener cleanupListener = cleanupAction(candidate, 
Bundle.TXT_CONFIRM_CLEANUP(name));
+                    Notification nf = 
NotificationDisplayer.getDefault().notify(
+                            Bundle.TIT_ABANDONED_USERDIR(name, age, toFree),
+                            clean,
+                            Bundle.DESC_ABANDONED_USERDIR(name, age, toFree),
+                            cleanupListener);
+
+                    CLEANUP_TASKS.put(cleanupListener, nf);
+                }
             } else {
-                if (isAutoRemoveAbanconedCache()) {
-                    JANITOR_RP.post(() -> cleanup(name));
+                if (isAutoRemoveAbandonedCache()) {
+                    LOG.log(Level.INFO, "Janitor autoremove abandoned cache: " 
+ candidate.cachedir.dir);
+                    JANITOR_RP.post(() -> cleanup(candidate));
                 } else {
-                    ActionListener cleanupListener = cleanupAction(name, 
Bundle.TXT_CONFIRM_CACHE_CLEANUP(name));
+                    ActionListener cleanupListener = cleanupAction(candidate, 
Bundle.TXT_CONFIRM_CACHE_CLEANUP(name));
                     Notification nf = 
NotificationDisplayer.getDefault().notify(
                             Bundle.TIT_ABANDONED_CACHEDIR(name, toFree),
                             clean,
@@ -137,10 +156,10 @@ public class Janitor {
                     CLEANUP_TASKS.put(cleanupListener, nf);
                 }
             }
-        }        
+        }
     }
-    
-    static ActionListener cleanupAction(String name, String label) {
+
+    static ActionListener cleanupAction(CleanupPair cp, String label) {
         return new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent evt) {
@@ -154,7 +173,7 @@ public class Janitor {
                         null
                 );
                 if (DialogDescriptor.YES_OPTION == 
DialogDisplayer.getDefault().notify(descriptor)) {
-                    JANITOR_RP.post(() -> cleanup(name));
+                    JANITOR_RP.post(() -> cleanup(cp));
                 }
                 Janitor.setEnabled(panel.isEnabledOnStartup());
                 Notification nf = CLEANUP_TASKS.get(this);
@@ -165,14 +184,13 @@ public class Janitor {
         };
     }
 
-    static void cleanup(String name) {
-        try (ProgressHandle handle = 
ProgressHandle.createHandle(Bundle.LBL_CLEANUP(name))){
+    static void cleanup(CleanupPair cp) {
+        try (ProgressHandle handle = 
ProgressHandle.createHandle(Bundle.LBL_CLEANUP(cp.getName()))) {
             handle.start();
-            deleteDir(getUserDir(name));
-            deleteDir(getCacheDir(name));
-        }        
+            cp.delete();
+        }
     }
-    
+
     public static final Preferences getPreferences() {
         return NbPreferences.forModule(Janitor.class);
     }
@@ -182,6 +200,7 @@ public class Janitor {
 
         @Override
         public void run() {
+            JANITOR_RP.post(Janitor::markUserCacheDirs, 5_000);
             if (isEnabled()) {
                 // Starting delayed, not to interfere with other startup IO 
operations
                 JANITOR_RP.post(Janitor::scanForJunk, 60_000);
@@ -190,127 +209,59 @@ public class Janitor {
 
     }
 
-    static void runNow() {
-        JANITOR_RP.post(Janitor::scanForJunk);
-    }
-
-    static File getUserDir(String version) {
-        File ret = null;
-        File userDir = Places.getUserDirectory();
-        if (userDir != null) {
-            ret = new File(userDir.getParentFile(), version);
-            ret = ret.isDirectory() ? ret : null;
-        }
-
-        return ret;
-    }
-
-    static File getCacheDir(String version) {
-        File ret = null;
-        File cacheDir = Places.getCacheDirectory();
-        if (cacheDir != null) {
-            ret = new File(cacheDir.getParentFile(), version);
-            ret = ret.isDirectory() ? ret : null;
-        }
-        return ret;
+    static void markUserCacheDirs() {
+        writeVersion(Places.getCacheDirectory());
+        writeVersion(Places.getUserDirectory());
     }
 
-    static void deleteDir(File dir) {
-        if ((dir == null) || !dir.exists()) return;
-        Path path = dir.toPath();
-        try {
-            Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
-                @Override
-                public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) throws IOException {
-                    Files.delete(file);
-                    return FileVisitResult.CONTINUE;
-                }
-
-                @Override
-                public FileVisitResult postVisitDirectory(Path dir, 
IOException exc) throws IOException {
-                    Files.delete(dir);
-                    return FileVisitResult.CONTINUE;
-                }
-            });
-        } catch(IOException ex) {
-            // Well we've tried
-        }
+    static void runNow() {
+        JANITOR_RP.post(Janitor::scanForJunk);
     }
 
-    public static long size(File f) {
 
-        if (f == null) {
-            return 0;
-        }
-        final Path path = f.toPath();
-        final AtomicLong size = new AtomicLong(0);
-
-        try {
-            Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
-                @Override
-                public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) {
-                    size.addAndGet(attrs.size());
-                    return FileVisitResult.CONTINUE;
-                }
-
-                @Override
-                public FileVisitResult visitFileFailed(Path file, IOException 
exc) {
-                    return FileVisitResult.CONTINUE;
-                }
-
-                @Override
-                public FileVisitResult postVisitDirectory(Path dir, 
IOException exc) {
-                    return FileVisitResult.CONTINUE;
-                }
-            });
-        } catch (IOException e) {
+    static void writeVersion(File baseDir) {
+        File lastUsedVersion = new File(baseDir, LAST_VERSION_NAME);
+        if (NB_VERSION != null) {
+            try (FileWriter fw = new FileWriter(lastUsedVersion)) {
+                fw.write(NB_VERSION);
+            } catch (IOException ex) {
+                // do nothing we've tried...
+                LOG.log(Level.FINE, "Could not write version info." , ex); 
//NOI18N
+            }
         }
-
-        return size.get();
     }
 
-    static List<Pair<String, Integer>> getOtherVersions() {
+    static List<CleanupPair> getCandidates() {
+        Set<String> names = new HashSet<>();
         File userDir = Places.getUserDirectory();
-        List<Pair<String, Integer>> ret = new LinkedList<>();
-        Set<String> availableUserDirs = new HashSet<>();
-        Instant now = Instant.now();
         if (userDir != null) {
             File userParent = userDir.getParentFile();
             for (File f : userParent.listFiles()) {
-                availableUserDirs.add(f.getName());
-                Path logFile = new File(f, LOGFILE_NAME).toPath();
-                if (!f.equals(userDir) && Files.isRegularFile(logFile)) {
-                    try {
-                        Instant lastModified = 
Files.getLastModifiedTime(logFile).toInstant();
-                        Integer age = (int) Duration.between(lastModified, 
now).toDays();
-                        if (lastModified.plus(getUnusedDays(), 
ChronoUnit.DAYS).isBefore(now)) {
-                                ret.add(Pair.of(f.getName(), age));
-                        }
-                    } catch (IOException ex) {
-                        //Just ignore what we can't process
-                    }
+                if (f.isDirectory() && !f.equals(userDir)) {
+                    names.add(f.getName());
                 }
             }
         }
 
-        //Search for abandoned cache dirs (cache dirs with no user dir)
         File cacheDir = Places.getCacheDirectory();
         if (cacheDir != null) {
             File cacheParent = cacheDir.getParentFile();
             for (File f : cacheParent.listFiles()) {
-                if (f.isDirectory() && 
!availableUserDirs.contains(f.getName())) {
-                    if (new File(f, ALL_CHECKSUM_NAME).exists() && 
!cacheDir.equals(f)) {
-                        try {
-                            Instant lastModified = 
Files.getLastModifiedTime(f.toPath()).toInstant();
-                            Integer age = (int) Duration.between(lastModified, 
now).toDays();
-                            ret.add(Pair.of(f.getName(), age));
-                        } catch (IOException ex) {
-                            //Just ignore what we can't process
-                        }
-                    }
+                if (f.isDirectory() && !f.equals(userDir)) {
+                    names.add(f.getName());
                 }
             }
         }
+        
+        List<CleanupPair> ret = new LinkedList<>();
+        for (String name : names) {
+            CleanupDir user = CleanupDir.get(CleanupDir.Kind.USERDIR, name);
+            CleanupDir cache = CleanupDir.get(CleanupDir.Kind.CACHEDIR, name);
+            if (user != null || cache != null) {
+                
+                ret.add(new CleanupPair(user, cache));
+            }
+        }
         return ret;
     }
 
@@ -330,12 +281,171 @@ public class Janitor {
         return getPreferences().getInt(PROP_UNUSED_DAYS, UNUSED_DAYS);
     }
 
-    static boolean isAutoRemoveAbanconedCache() {
+    static boolean isAutoRemoveAbandonedCache() {
         return getPreferences().getBoolean(PROP_AUTO_REMOVE_ABANDONED_CACHE, 
true);
     }
 
-    static void setAutoRemoveAbanconedCache(boolean b) {
+    static void setAutoRemoveAbandonedCache(boolean b) {
         getPreferences().putBoolean(PROP_AUTO_REMOVE_ABANDONED_CACHE, b);
     }
 
+    private static class CleanupDir {
+
+        enum Kind {USERDIR, CACHEDIR};
+        
+        private final Path dir;
+        private final Kind kind; 
+
+        private CleanupDir(Path dir, Kind kind) {
+            this.dir = dir;
+            this.kind = kind;
+        }
+
+        static CleanupDir get(CleanupDir.Kind kind, String version) {
+            Path dir = kind == CleanupDir.Kind.USERDIR ? 
Places.getUserDirectory().toPath() : Places.getCacheDirectory().toPath();
+            Path f = dir.getParent().resolve(version);
+            Path test = f.resolve((kind == CleanupDir.Kind.USERDIR) ? 
LOGFILE_NAME : ALL_CHECKSUM_NAME);
+            return !f.equals(dir) && Files.isDirectory(f) && 
Files.isRegularFile(test) ? new CleanupDir(f, kind) : null;
+        }
+
+        public long size() {
+
+            if (dir == null) {
+                return 0;
+            }
+            final AtomicLong size = new AtomicLong(0);
+
+            try {
+                Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+                    @Override
+                    public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) {
+                        size.addAndGet(attrs.size());
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    @Override
+                    public FileVisitResult visitFileFailed(Path file, 
IOException exc) {
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    @Override
+                    public FileVisitResult postVisitDirectory(Path dir, 
IOException exc) {
+                        return FileVisitResult.CONTINUE;
+                    }
+                });
+            } catch (IOException e) {
+                LOG.log(Level.FINE, "Something went wrong calculating the size 
of " + dir, e); //NOI18N
+            }
+
+            return size.get();
+        }
+
+        public String getName() {
+            String name = dir.getFileName().toString();
+            Path f = dir.resolve(LAST_VERSION_NAME);
+            if (Files.isRegularFile(f)) {
+                try {
+                    if (Files.size(f) < 100) {
+                        try (BufferedReader br = Files.newBufferedReader(f)) {
+                            name = br.readLine();
+                        }
+                    } else{
+                        LOG.log(Level.WARNING, "Skipped version file " + f + " 
as it is suspiciously large."); //NOI18N
+                    }
+                } catch (IOException ex) {
+                    // Could not read the file, stick to the dirname
+                }
+            } else {
+                LOG.log(Level.INFO, f.toString() + " is missing fallback to 
dirname: " + name); //NOI18N
+                switch (name) { // Map a few elder Snap release revision to 
IDE version number, these could be removed in NetBeans 21/22
+                    case "80":
+                        return "18";
+                    case "76":
+                        return "17";
+                    case "74":
+                        return "16";
+                    case "69":
+                        return "15";
+                }
+            }
+            return name;
+        }
+        
+        public int age(Instant now) {
+            int ret = -1;
+            Path f;
+            switch (kind) {
+                case CACHEDIR:
+                    f = dir.resolve(ALL_CHECKSUM_NAME);
+                    break;
+                default:
+                    f = dir.resolve(LOGFILE_NAME);
+            }
+            if (Files.isRegularFile(f)) {
+                    try {
+                        Instant lastModified = 
Files.getLastModifiedTime(f).toInstant();
+                        ret = (int) Duration.between(lastModified, 
now).toDays();
+                    } catch (IOException ex) {
+                        //Just ignore what we can't process
+                    }                
+            }
+            return ret;
+        }
+        
+        public void delete() {
+            try {
+                Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+                    @Override
+                    public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) throws IOException {
+                        Files.delete(file);
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    @Override
+                    public FileVisitResult postVisitDirectory(Path dir, 
IOException exc) throws IOException {
+                        Files.delete(dir);
+                        return FileVisitResult.CONTINUE;
+                    }
+                });
+            } catch (IOException ex) {
+                // Well we've tried
+                LOG.log(Level.INFO, "Janitor couldn't remove " + 
dir.toString(), ex); //NOI18N
+            }
+        }
+    }
+
+    private static class CleanupPair {
+        final CleanupDir userdir;
+        final CleanupDir cachedir;
+
+        public CleanupPair(CleanupDir userdir, CleanupDir cachedir) {
+            this.userdir = userdir;
+            this.cachedir = cachedir;
+            if ((userdir == null) && (cachedir == null)) {
+                throw new IllegalArgumentException("Both user and cache dirs 
cannot be null!"); //NOI18N
+            }
+        }
+        
+        public String getName() {
+            return userdir != null ? userdir.getName() : cachedir.getName();
+        }
+
+        public int age(Instant now) {
+            return userdir != null ? userdir.age(now) : cachedir.age(now);
+        }
+        
+        public int size() {
+            int sum = 0;
+            sum += userdir != null ? userdir.size() : 0;
+            sum += cachedir != null ? cachedir.size() : 0;
+            return sum / (1_000_000) + 1;
+        }
+        
+        public void delete() {
+            if (userdir != null) userdir.delete();
+            if (cachedir != null) cachedir.delete();
+        }
+        
+    }    
+
 }
diff --git 
a/platform/janitor/src/org/netbeans/modules/janitor/JanitorOptionsPanel.java 
b/platform/janitor/src/org/netbeans/modules/janitor/JanitorOptionsPanel.java
index 78f8091a14..504fab8fce 100644
--- a/platform/janitor/src/org/netbeans/modules/janitor/JanitorOptionsPanel.java
+++ b/platform/janitor/src/org/netbeans/modules/janitor/JanitorOptionsPanel.java
@@ -39,14 +39,14 @@ public class JanitorOptionsPanel extends javax.swing.JPanel 
{
 
     void loadValues() {
         cbEnabled.setSelected(Janitor.isEnabled());
-        cbAutoRemove.setSelected(Janitor.isAutoRemoveAbanconedCache());
+        cbAutoRemove.setSelected(Janitor.isAutoRemoveAbandonedCache());
         spinnerModel.setValue(Janitor.getUnusedDays());
         resetRunNow();
     }
 
     void saveValues() {
         Janitor.setEnabled(cbEnabled.isSelected());
-        Janitor.setAutoRemoveAbanconedCache(cbAutoRemove.isSelected());
+        Janitor.setAutoRemoveAbandonedCache(cbAutoRemove.isSelected());
         Janitor.setUnusedDays(spinnerModel.getNumber().intValue());
         resetRunNow();
     }
@@ -54,7 +54,7 @@ public class JanitorOptionsPanel extends javax.swing.JPanel {
     boolean isChanged() {
         boolean changed = false;
         changed |= cbEnabled.isSelected() != Janitor.isEnabled();
-        changed |= cbAutoRemove.isSelected() != 
Janitor.isAutoRemoveAbanconedCache();
+        changed |= cbAutoRemove.isSelected() != 
Janitor.isAutoRemoveAbandonedCache();
         changed |= spinnerModel.getNumber().intValue() != 
Janitor.getUnusedDays();
         changed |= !btRunNow.isEnabled();
         return changed;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to