Created
February 4, 2025 23:56
-
-
Save swankjesse/effae2750f11e0b5bf188dfdf12319a9 to your computer and use it in GitHub Desktop.
ClassLoader close bug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.net.URL; | |
import java.net.URLClassLoader; | |
import java.util.jar.JarOutputStream; | |
import java.util.zip.ZipEntry; | |
import static java.nio.charset.StandardCharsets.UTF_8; | |
public class Main { | |
public static void main(String[] args) throws Exception { | |
boolean skipClose = args.length > 0 && args[0].equals("skip"); | |
// Build resources.jar, whose file 'hello.txt' contains 'hello world'. | |
File jarFile = new File("resources.jar"); | |
String resourceName = "hello.txt"; | |
String resourceContent = "hello world"; | |
try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jarFile))) { | |
out.putNextEntry(new ZipEntry(resourceName)); | |
out.write(resourceContent.getBytes(UTF_8)); | |
} | |
URL[] jarFileUrls = { | |
jarFile.toURI().toURL() | |
}; | |
// Create a class loader B and start reading a resource with it. | |
URLClassLoader loaderB = URLClassLoader.newInstance(jarFileUrls); | |
InputStream inB = loaderB.getResourceAsStream(resourceName); | |
// Create class loader A that reads the same jar file. | |
URLClassLoader loaderA = URLClassLoader.newInstance(jarFileUrls); | |
try ( | |
InputStream inA = loaderA.getResourceAsStream(resourceName); | |
BufferedReader readerA = new BufferedReader(new InputStreamReader(inA)) | |
) { | |
if (readerA.readLine().equals(resourceContent)) { | |
System.out.println("READ A SUCCESS"); | |
} | |
} | |
if (!skipClose) { | |
loaderA.close(); | |
} | |
// Finish reading the file with class loader B. | |
try (BufferedReader readerB = new BufferedReader(new InputStreamReader(inB))) { | |
if (readerB.readLine().equals(resourceContent)) { | |
System.out.println("READ B SUCCESS"); | |
} | |
} | |
} | |
} |
If you pass the skip
argument to the function to skip closing loaderA
, it doesn’t crash:
$ javac Main.java && java Main skip
READ A SUCCESS
READ B SUCCESS
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This crashes attempting to read from
inB
afterloaderA.close()
.