changeset 254:b65ab2ba6cd8

Bug 3457: Object.finalize() should not be used Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/122
author Yasumasa Suenaga <yasuenag@gmail.com>
date Tue, 31 Oct 2017 16:22:46 +0900
parents 1885f234b75b
children 7c3bea2249ab
files ChangeLog analyzer/core/src/main/java/jp/co/ntt/oss/heapstats/container/log/ArchiveData.java
diffstat 2 files changed, 73 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Oct 31 16:21:48 2017 +0900
+++ b/ChangeLog	Tue Oct 31 16:22:46 2017 +0900
@@ -1,6 +1,7 @@
 2017-10-31 Yasumasa Suenaga <yasuenag@gmail.com>
 
 	* Bug 3456: Build warnings w/ GCC 7.2.1
+	* Bug 3457: Object.finalize() should not be used
 
 2017-10-10 KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
 
--- a/analyzer/core/src/main/java/jp/co/ntt/oss/heapstats/container/log/ArchiveData.java	Tue Oct 31 16:21:48 2017 +0900
+++ b/analyzer/core/src/main/java/jp/co/ntt/oss/heapstats/container/log/ArchiveData.java	Tue Oct 31 16:22:46 2017 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Yasumasa Suenaga
+ * Copyright (C) 2014-2017 Yasumasa Suenaga
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,27 +18,16 @@
 
 package jp.co.ntt.oss.heapstats.container.log;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
+import java.io.*;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.StringJoiner;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.zip.ZipEntry;
@@ -82,7 +71,66 @@
     private List<String> sockOwner;
     
     private boolean parsed;
-    
+
+    private static ReferenceQueue<ArchiveData> refQueue;
+
+    private static class PhantomRefWrapper extends PhantomReference<ArchiveData> {
+
+        private final Path deleteTarget;
+
+        public PhantomRefWrapper(ArchiveData ref) {
+            super(ref, refQueue);
+            Objects.requireNonNull(ref.extractPath, "extractPath must not be null.");
+            this.deleteTarget = ref.extractPath.toPath();
+        }
+
+        public void clean() {
+            try {
+                Files.walkFileTree(deleteTarget, 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 e) throws IOException {
+                        if (e == null) {
+                            Files.delete(dir);
+                            return FileVisitResult.CONTINUE;
+                        } else {
+                            throw e;
+                        }
+                    }
+                });
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+    }
+
+    private static Set<PhantomRefWrapper> refSet;
+
+    private static void cleaner() {
+        while (true) {
+            try {
+                PhantomRefWrapper ref = (PhantomRefWrapper) refQueue.remove();
+                ref.clean();
+                refSet.remove(ref);
+            } catch (InterruptedException e) {
+                // Do nothing.
+            }
+        }
+    }
+
+    static {
+        refQueue = new ReferenceQueue<>();
+        refSet = Collections.synchronizedSet(new HashSet<>());
+        Thread th = new Thread(ArchiveData::cleaner, "ArchiveData cleaner");
+        th.setDaemon(true);
+        th.start();
+    }
+
     /**
      * Constructor of ArchiveData.
      * 
@@ -93,6 +141,7 @@
         this(log, null);
         extractPath = Files.createTempDirectory("heapstats_archive").toFile();
         extractPath.deleteOnExit();
+        refSet.add(new PhantomRefWrapper(this));
     }
     
     /**
@@ -106,6 +155,10 @@
         archivePath = log.getArchivePath();
         extractPath = location;
         parsed = false;
+
+        if (extractPath != null) {
+            refSet.add(new PhantomRefWrapper(this));
+        }
     }
     
     /**
@@ -342,18 +395,6 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            extractPath.delete();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
      * Getter of this date this archive is created.
      * @return LocalDateTime this archive is created.
      */