Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-7183373

URLClassloader.close() does not close JAR files whose resources have been loaded via getResource()

    XMLWordPrintable

Details

    • Bug
    • Status: Closed
    • P3
    • Resolution: Fixed
    • 7
    • 8
    • core-libs
    • b75
    • generic
    • generic
    • Verified

    Description

      SYNOPSIS
      --------
      URLClassloader.close() does not close JAR files whose resources have been loaded via getResource()

      OPERATING SYSTEM
      ----------------
      All

      FULL JDK VERSION
      ----------------
      Java 7 onwards
      Reproducible with latest Java 8 build (b46)

      PROBLEM DESCRIPTION
      -------------------
      Closeables is a new feature introduced in Java7. The documentation states that all JAR files and other Closeable resources opened by a class loader will be closed when the class loader is itself closed. However, when a resource is loaded from a JAR file using getResource(), the JAR file is NOT closed when the class loader is closed.

      The attached testcase does the following:

      1. Creates a JAR file
      2. Places a resource (a small text file) inside the JAR
      3. Creates a URLClassloader and uses it to load the resource from the JAR
      4. Calls close() on the URLClassloader
      5. Attempts to delete the JAR file

      Step 5 should be successful, as all references to it should have been closed by the call to URLClassLoader.close(), but it fails.

      REPRODUCTION INSTRUCTIONS
      -------------------------
      1. Compile and run the testcase
      2. Observe the output

      Expected output:
      Test PASSED

      Observed output:
      Test FAILED: Closeables failed to close

      WORKAROUND
      ----------
      Close the JAR file manually. One strategy can be seen in the testcase, which uses a workaround after the initial attempt to delete the file fails.

      TESTCASE
      --------
      import java.io.*;
      import java.net.*;
      import java.util.zip.*;

      public class TestURLClassLoader {
          public static void main(String[] args) throws Exception {
              // Create a JAR file
              File f = new File("urlcl" + 1 + ".jar");
              ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f));

              // add a file
              zos.putNextEntry(new ZipEntry("TestResource"));
              byte[] b = new String("This is a test resource").getBytes();
              zos.write(b, 0, b.length);
              zos.close();

              // Load the file using cl.getResource()
              URLClassLoader cl = new URLClassLoader(new URL[] { new URL("jar:" + f.toURI().toURL() + "!/")}, null);
              cl.getResource("TestResource");

              // Close the class loader - this should free up all of its Closeables, including the JAR file
              cl.close();

              // Try to delete the JAR file
              f.delete();

              // Check to see if the file was deleted
              if (f.exists()) {
                  System.out.println("Test FAILED: Closeables failed to close");
                  // Delete the jar using a workaround
                  for (URL u : cl.getURLs()) {
                      if (u.getProtocol().equals("jar")) {
                          ((JarURLConnection)u.openConnection()).getJarFile().close();
                      }
                      f.delete();
                  }
              } else {
                  System.out.println("Test PASSED");
              }
          }
      }

      SUGGESTED FIX
      -------------
      These diffs are relative to the 7u6 codebase.

      --- 7u6\URLClassLoader.java 2012-06-26 22:08:54.000000000 +0100
      +++ new\URLClassLoader.java 2012-07-11 13:00:00.205574000 +0100
      @@ -552,6 +552,21 @@
                       }
                   }, acc);

      + if (url != null) {
      + if (url.getProtocol().equals("jar")) {
      + try {
      + JarFile jar = ((JarURLConnection)url.openConnection()).getJarFile();
      + synchronized (closeables) {
      + if (!closeables.containsKey(jar)) {
      + closeables.put(jar, null);
      + }
      + }
      + } catch (java.io.IOException ioe) {
      + ioe.printStackTrace();
      + }
      + }
      + }
      +
               return url != null ? ucp.checkURL(url) : null;
           }

      Attachments

        Issue Links

          Activity

            People

              michaelm Michael McMahon
              dkorbel David Korbel (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: