8212129: Remove finalize methods from java.util.zip.ZipFIle/Inflator/Deflator
authorlancea
Fri, 26 Oct 2018 14:02:31 -0400
changeset 52305 52a97e06a5e3
parent 52304 72f2fc52ef85
child 52306 3a767a000aab
8212129: Remove finalize methods from java.util.zip.ZipFIle/Inflator/Deflator Reviewed-by: rriggs, sherman, alanb, clanger
src/java.base/share/classes/java/util/zip/Deflater.java
src/java.base/share/classes/java/util/zip/Inflater.java
src/java.base/share/classes/java/util/zip/ZipFile.java
test/jdk/java/util/zip/ZipFile/FinalizeZipFile.java
test/jdk/java/util/zip/ZipFile/TestCleaner.java
--- a/src/java.base/share/classes/java/util/zip/Deflater.java	Fri Oct 26 10:47:05 2018 -0500
+++ b/src/java.base/share/classes/java/util/zip/Deflater.java	Fri Oct 26 14:02:31 2018 -0400
@@ -89,13 +89,6 @@
  * to perform cleanup should be modified to use alternative cleanup mechanisms such
  * as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
  *
- * @implSpec
- * If this {@code Deflater} has been subclassed and the {@code end} method has been
- * overridden, the {@code end} method will be called by the finalization when the
- * deflater is unreachable. But the subclasses should not depend on this specific
- * implementation; the finalization is not reliable and the {@code finalize} method
- * is deprecated to be removed.
- *
  * @see         Inflater
  * @author      David Connelly
  * @since 1.1
@@ -204,8 +197,8 @@
     public Deflater(int level, boolean nowrap) {
         this.level = level;
         this.strategy = DEFAULT_STRATEGY;
-        this.zsRef = DeflaterZStreamRef.get(this,
-                                    init(level, DEFAULT_STRATEGY, nowrap));
+        this.zsRef = new DeflaterZStreamRef(this,
+                init(level, DEFAULT_STRATEGY, nowrap));
     }
 
     /**
@@ -901,21 +894,6 @@
         }
     }
 
-    /**
-     * Closes the compressor when garbage is collected.
-     *
-     * @deprecated The {@code finalize} method has been deprecated and will be
-     *     removed. It is implemented as a no-op. Subclasses that override
-     *     {@code finalize} in order to perform cleanup should be modified to use
-     *     alternative cleanup mechanisms and to remove the overriding {@code finalize}
-     *     method. The recommended cleanup for compressor is to explicitly call
-     *     {@code end} method when it is no longer in use. If the {@code end} is
-     *     not invoked explicitly the resource of the compressor will be released
-     *     when the instance becomes unreachable.
-     */
-    @Deprecated(since="9", forRemoval=true)
-    protected void finalize() {}
-
     private void ensureOpen() {
         assert Thread.holdsLock(zsRef);
         if (zsRef.address() == 0)
@@ -977,43 +955,5 @@
             }
         }
 
-        /*
-         * If {@code Deflater} has been subclassed and the {@code end} method is
-         * overridden, uses {@code finalizer} mechanism for resource cleanup. So
-         * {@code end} method can be called when the {@code Deflater} is unreachable.
-         * This mechanism will be removed when the {@code finalize} method is
-         * removed from {@code Deflater}.
-         */
-        static DeflaterZStreamRef get(Deflater owner, long addr) {
-            Class<?> clz = owner.getClass();
-            while (clz != Deflater.class) {
-                try {
-                    clz.getDeclaredMethod("end");
-                    return new FinalizableZStreamRef(owner, addr);
-                } catch (NoSuchMethodException nsme) {}
-                clz = clz.getSuperclass();
-            }
-            return new DeflaterZStreamRef(owner, addr);
-        }
-
-        private static class FinalizableZStreamRef extends DeflaterZStreamRef {
-            final Deflater owner;
-
-            FinalizableZStreamRef (Deflater owner, long addr) {
-                super(null, addr);
-                this.owner = owner;
-            }
-
-            @Override
-            void clean() {
-                run();
-            }
-
-            @Override
-            @SuppressWarnings("deprecation")
-            protected void finalize() {
-                owner.end();
-            }
-        }
     }
 }
--- a/src/java.base/share/classes/java/util/zip/Inflater.java	Fri Oct 26 10:47:05 2018 -0500
+++ b/src/java.base/share/classes/java/util/zip/Inflater.java	Fri Oct 26 14:02:31 2018 -0400
@@ -87,13 +87,6 @@
  * to perform cleanup should be modified to use alternative cleanup mechanisms such
  * as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
  *
- * @implSpec
- * If this {@code Inflater} has been subclassed and the {@code end} method has been
- * overridden, the {@code end} method will be called by the finalization when the
- * inflater is unreachable. But the subclasses should not depend on this specific
- * implementation; the finalization is not reliable and the {@code finalize} method
- * is deprecated to be removed.
- *
  * @see         Deflater
  * @author      David Connelly
  * @since 1.1
@@ -135,7 +128,7 @@
      * @param nowrap if true then support GZIP compatible compression
      */
     public Inflater(boolean nowrap) {
-        this.zsRef = InflaterZStreamRef.get(this, init(nowrap));
+        this.zsRef = new InflaterZStreamRef(this, init(nowrap));
     }
 
     /**
@@ -714,25 +707,6 @@
         }
     }
 
-    /**
-     * Closes the decompressor when garbage is collected.
-     *
-     * @implSpec
-     * If this {@code Inflater} has been subclassed and the {@code end} method
-     * has been overridden, the {@code end} method will be called when the
-     * inflater is unreachable.
-     *
-     * @deprecated The {@code finalize} method has been deprecated and will be
-     *     removed. It is implemented as a no-op. Subclasses that override
-     *     {@code finalize} in order to perform cleanup should be modified to use
-     *     alternative cleanup mechanisms and remove the overriding {@code finalize}
-     *     method. The recommended cleanup for compressor is to explicitly call
-     *     {@code end} method when it is no longer in use. If the {@code end} is
-     *     not invoked explicitly the resource of the compressor will be released
-     *     when the instance becomes unreachable,
-     */
-    @Deprecated(since="9", forRemoval=true)
-    protected void finalize() {}
 
     private void ensureOpen () {
         assert Thread.holdsLock(zsRef);
@@ -792,43 +766,5 @@
             }
         }
 
-        /*
-         * If {@code Inflater} has been subclassed and the {@code end} method is
-         * overridden, uses {@code finalizer} mechanism for resource cleanup. So
-         * {@code end} method can be called when the {@code Inflater} is unreachable.
-         * This mechanism will be removed when the {@code finalize} method is
-         * removed from {@code Inflater}.
-         */
-        static InflaterZStreamRef get(Inflater owner, long addr) {
-            Class<?> clz = owner.getClass();
-            while (clz != Inflater.class) {
-                try {
-                    clz.getDeclaredMethod("end");
-                    return new FinalizableZStreamRef(owner, addr);
-                } catch (NoSuchMethodException nsme) {}
-                clz = clz.getSuperclass();
-            }
-            return new InflaterZStreamRef(owner, addr);
-        }
-
-        private static class FinalizableZStreamRef extends InflaterZStreamRef {
-            final Inflater owner;
-
-            FinalizableZStreamRef(Inflater owner, long addr) {
-                super(null, addr);
-                this.owner = owner;
-            }
-
-            @Override
-            void clean() {
-                run();
-            }
-
-            @Override
-            @SuppressWarnings("deprecation")
-            protected void finalize() {
-                owner.end();
-            }
-        }
     }
 }
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java	Fri Oct 26 10:47:05 2018 -0500
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java	Fri Oct 26 14:02:31 2018 -0400
@@ -56,7 +56,6 @@
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 import jdk.internal.misc.JavaLangAccess;
@@ -85,13 +84,6 @@
  * cleanup mechanisms such as {@link java.lang.ref.Cleaner} and remove the overriding
  * {@code finalize} method.
  *
- * @implSpec
- * If this {@code ZipFile} has been subclassed and the {@code close} method has
- * been overridden, the {@code close} method will be called by the finalization
- * when {@code ZipFile} is unreachable. But the subclasses should not depend on
- * this specific implementation; the finalization is not reliable and the
- * {@code finalize} method is deprecated to be removed.
- *
  * @author      David Connelly
  * @since 1.1
  */
@@ -244,7 +236,7 @@
         this.name = name;
         long t0 = System.nanoTime();
 
-        this.res = CleanableResource.get(this, file, mode);
+        this.res = new CleanableResource(this, file, mode);
 
         PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
         PerfCounter.getZipFileCount().increment();
@@ -825,44 +817,6 @@
             this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0);
         }
 
-        /*
-         * If {@code ZipFile} has been subclassed and the {@code close} method is
-         * overridden, uses the {@code finalizer} mechanism for resource cleanup.
-         * So {@code close} method can be called when the the {@code ZipFile} is
-         * unreachable. This mechanism will be removed when {@code finalize} method
-         * is removed from {@code ZipFile}.
-         */
-        static CleanableResource get(ZipFile zf, File file, int mode)
-            throws IOException {
-            Class<?> clz = zf.getClass();
-            while (clz != ZipFile.class && clz != JarFile.class) {
-                if (JLA.getDeclaredPublicMethods(clz, "close").size() != 0) {
-                    return new FinalizableResource(zf, file, mode);
-                }
-                clz = clz.getSuperclass();
-            }
-            return new CleanableResource(zf, file, mode);
-        }
-
-        static class FinalizableResource extends CleanableResource {
-            ZipFile zf;
-            FinalizableResource(ZipFile zf, File file, int mode)
-                throws IOException {
-                super(file, mode);
-                this.zf = zf;
-            }
-
-            @Override
-            void clean() {
-                run();
-            }
-
-            @Override
-            @SuppressWarnings("deprecation")
-            protected void finalize() throws IOException {
-                zf.close();
-            }
-        }
     }
 
     /**
@@ -891,25 +845,6 @@
         }
     }
 
-    /**
-     * Ensures that the system resources held by this ZipFile object are
-     * released when there are no more references to it.
-     *
-     * @deprecated The {@code finalize} method has been deprecated and will be
-     *     removed. It is implemented as a no-op. Subclasses that override
-     *     {@code finalize} in order to perform cleanup should be modified to
-     *     use alternative cleanup mechanisms and to remove the overriding
-     *     {@code finalize} method. The recommended cleanup for ZipFile object
-     *     is to explicitly invoke {@code close} method when it is no longer in
-     *     use, or use try-with-resources. If the {@code close} is not invoked
-     *     explicitly the resources held by this object will be released when
-     *     the instance becomes unreachable.
-     *
-     * @throws IOException if an I/O error has occurred
-     */
-    @Deprecated(since="9", forRemoval=true)
-    protected void finalize() throws IOException {}
-
     private void ensureOpen() {
         if (closeRequested) {
             throw new IllegalStateException("zip file closed");
--- a/test/jdk/java/util/zip/ZipFile/FinalizeZipFile.java	Fri Oct 26 10:47:05 2018 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/* @test
- * @bug 7007609 7009618
- * @summary Check that ZipFile objects are always collected
- * @key randomness
- */
-
-import java.io.*;
-import java.util.Random;
-import java.util.zip.*;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class FinalizeZipFile {
-
-    private static final CountDownLatch finalizersDone = new CountDownLatch(3);
-
-    private static class InstrumentedZipFile extends ZipFile {
-
-        public InstrumentedZipFile(File f) throws Exception {
-            super(f);
-            System.out.printf("Using %s%n", f.getPath());
-        }
-        @Override
-        protected void finalize() throws IOException {
-            System.out.printf("Killing %s%n", getName());
-            super.finalize();
-            finalizersDone.countDown();
-        }
-    }
-
-    private static void makeGarbage() throws Throwable {
-        final Random rnd = new Random();
-        // Create some ZipFiles.
-        // Find some .jar files in test directory.
-        final File testdir = new File(System.getProperty("test.src", "."));
-        check(testdir.isDirectory());
-        final File[] jars = testdir.listFiles(
-            new FilenameFilter() {
-                public boolean accept(File dir, String name) {
-                    return name.endsWith(".jar");}});
-        check(jars.length > 1);
-
-        new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]).close();
-        new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]).close();
-
-        // Create a ZipFile and get an input stream from it
-        for (int i = 0; i < jars.length + 10; i++) {
-            ZipFile zf = new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]);
-            ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
-            if (ze != null) {
-                InputStream is = zf.getInputStream(ze);
-                break;
-            }
-        }
-    }
-
-    public static void realMain(String[] args) throws Throwable {
-        makeGarbage();
-        while (!finalizersDone.await(10, TimeUnit.MILLISECONDS)) {
-            System.gc();
-        }
-        // Not all ZipFiles were collected?
-        equal(finalizersDone.getCount(), 0L);
-    }
-
-    //--------------------- Infrastructure ---------------------------
-    static volatile int passed = 0, failed = 0;
-    static void pass() {passed++;}
-    static void fail() {failed++; Thread.dumpStack();}
-    static void fail(String msg) {System.out.println(msg); fail();}
-    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
-    static void check(boolean cond) {if (cond) pass(); else fail();}
-    static void equal(Object x, Object y) {
-        if (x == null ? y == null : x.equals(y)) pass();
-        else fail(x + " not equal to " + y);}
-    public static void main(String[] args) throws Throwable {
-        try {realMain(args);} catch (Throwable t) {unexpected(t);}
-        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
-        if (failed > 0) throw new AssertionError("Some tests failed");}
-}
--- a/test/jdk/java/util/zip/ZipFile/TestCleaner.java	Fri Oct 26 10:47:05 2018 -0500
+++ b/test/jdk/java/util/zip/ZipFile/TestCleaner.java	Fri Oct 26 14:02:31 2018 -0400
@@ -31,8 +31,6 @@
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.zip.*;
 import jdk.internal.vm.annotation.DontInline;
 import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -56,34 +54,6 @@
         }
     }
 
-    private static class SubclassedInflater extends Inflater {
-        CountDownLatch endCountDown;
-
-        SubclassedInflater(CountDownLatch endCountDown) {
-            this.endCountDown = endCountDown;
-        }
-
-        @Override
-        public void end() {
-            super.end();
-            endCountDown.countDown();
-        }
-    }
-
-    private static class SubclassedDeflater extends Deflater {
-        CountDownLatch endCountDown;
-
-        SubclassedDeflater(CountDownLatch endCountDown) {
-            this.endCountDown = endCountDown;
-        }
-
-        @Override
-        public void end() {
-            super.end();
-            endCountDown.countDown();
-        }
-    }
-
     // verify the "native resource" of In/Deflater has been cleaned
     private static void testDeInflater() throws Throwable {
         Field zsRefDef = Deflater.class.getDeclaredField("zsRef");
@@ -124,44 +94,6 @@
         if (cnt != 0)
             throw new RuntimeException("cleaner failed to clean : " + cnt);
 
-        // test subclassed Deflater/Inflater, for behavioral compatibility.
-        // should be removed if the finalize() method is finally removed.
-        var endCountDown = new CountDownLatch(20);
-        for (int i = 0; i < 10; i++) {
-            var def = new SubclassedDeflater(endCountDown);
-            def.setInput("hello".getBytes());
-            def.finish();
-            n = def.deflate(buf1);
-
-            var inf = new SubclassedInflater(endCountDown);
-            inf.setInput(buf1, 0, n);
-            n = inf.inflate(buf2);
-            if (!"hello".equals(new String(buf2, 0, n))) {
-                throw new RuntimeException("compression/decompression failed");
-            }
-        }
-        while (!endCountDown.await(10, TimeUnit.MILLISECONDS)) {
-            System.gc();
-        }
-        if (endCountDown.getCount() != 0)
-            throw new RuntimeException("finalizer failed to clean : " +
-                endCountDown.getCount());
-    }
-
-    private static class SubclassedZipFile extends ZipFile {
-        CountDownLatch closeCountDown;
-
-        SubclassedZipFile(File f, CountDownLatch closeCountDown)
-            throws IOException {
-            super(f);
-            this.closeCountDown = closeCountDown;
-        }
-
-        @Override
-        public void close() throws IOException {
-            closeCountDown.countDown();
-            super.close();
-        }
     }
 
     @DontInline
@@ -198,27 +130,6 @@
         }
     }
 
-    @DontInline
-    private static void openAndCloseSubZipFile(File zip, CountDownLatch closeCountDown)
-        throws Throwable {
-        try {
-            try (var fos = new FileOutputStream(zip);
-                 var zos = new ZipOutputStream(fos)) {
-                zos.putNextEntry(new ZipEntry("hello"));
-                zos.write("hello".getBytes(US_ASCII));
-                zos.closeEntry();
-            }
-            var zf = new SubclassedZipFile(zip, closeCountDown);
-            var es = zf.entries();
-            while (es.hasMoreElements()) {
-                zf.getInputStream(es.nextElement()).read();
-            }
-            es = null;
-            zf = null;
-        } finally {
-            zip.delete();
-        }
-    }
 
     private static void testZipFile() throws Throwable {
         File dir = new File(System.getProperty("test.dir", "."));
@@ -241,16 +152,5 @@
                 throw new RuntimeException("cleaner failed to clean zipfile.");
             }
         }
-
-        // test subclassed ZipFile, for behavioral compatibility.
-        // should be removed if the finalize() method is finally removed.
-        var closeCountDown = new CountDownLatch(1);
-        openAndCloseSubZipFile(zip, closeCountDown);
-        while (!closeCountDown.await(10, TimeUnit.MILLISECONDS)) {
-            System.gc();
-        }
-        if (closeCountDown.getCount() != 0)
-            throw new RuntimeException("finalizer failed to clean : " +
-                closeCountDown.getCount());
     }
 }