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;
}
--------
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
- relates to
-
JDK-6829919 URLClassLoader.close() doesn't close resource file if getResourceAsStream(...) was called before
- Closed
-
JDK-6839348 URLClassLoader.close() method gives abiltiy to to close System and extension class loader
- Closed