एक और कोने का मामला जो यह हो सकता है: यदि आप एक JAR फ़ाइल को एक के माध्यम से पढ़ते / लिखते हैं URL
और बाद में उसी JVM सत्र में उसी फ़ाइल को हटाने का प्रयास करते हैं।
File f = new File("/tmp/foo.jar");
URL j = f.toURI().toURL();
URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();
// open a Jar entry in auto-closing manner
try (InputStream i = c.getInputStream()) {
// just read some stuff; for demonstration purposes only
byte[] first16 = new byte[16];
i.read(first16);
System.out.println(new String(first16));
}
// ...
// i is now closed, so we should be good to delete the jar; but...
System.out.println(f.delete()); // says false!
कारण यह है कि जावा की आंतरिक JAR फ़ाइल हैंडलिंग लॉजिक, JarFile
प्रविष्टियों को कैश करता है :
// inner class of `JarURLConnection` that wraps the actual stream returned by `getInputStream()`
class JarURLInputStream extends FilterInputStream {
JarURLInputStream(InputStream var2) {
super(var2);
}
public void close() throws IOException {
try {
super.close();
} finally {
// if `getUseCaches()` is set, `jarFile` won't get closed!
if (!JarURLConnection.this.getUseCaches()) {
JarURLConnection.this.jarFile.close();
}
}
}
}
और प्रत्येक JarFile
(बल्कि, अंतर्निहित ZipFile
संरचना) फ़ाइल के लिए एक हैंडल पकड़, निर्माण के समय से सही है जब तक close()
लागू किया गया है:
public ZipFile(File file, int mode, Charset charset) throws IOException {
// ...
jzfile = open(name, mode, file.lastModified(), usemmap);
// ...
}
// ...
private static native long open(String name, int mode, long lastModified,
boolean usemmap) throws IOException;
इस NetBeans मुद्दे पर एक अच्छी व्याख्या है ।
जाहिरा तौर पर इसे "ठीक" करने के दो तरीके हैं:
आप JAR फ़ाइल कैशिंग को निष्क्रिय कर सकते हैं - वर्तमान के लिए URLConnection
, या URLConnection
वर्तमान JVM सत्र में भविष्य के सभी (विश्व स्तर पर) के लिए:
URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();
// for only c
c.setUseCaches(false);
// globally; for some reason this method is not static,
// so we still need to access it through a URLConnection instance :(
c.setDefaultUseCaches(false);
[हैकिंग!]JarFile
जब आप इसके साथ कर रहे हैं तो आप कैश से मैन्युअल रूप से शुद्ध कर सकते हैं। कैश प्रबंधक sun.net.www.protocol.jar.JarFileFactory
पैकेज-निजी है, लेकिन कुछ प्रतिबिंब जादू आपके लिए काम कर सकते हैं:
class JarBridge {
static void closeJar(URL url) throws Exception {
// JarFileFactory jarFactory = JarFileFactory.getInstance();
Class<?> jarFactoryClazz = Class.forName("sun.net.www.protocol.jar.JarFileFactory");
Method getInstance = jarFactoryClazz.getMethod("getInstance");
getInstance.setAccessible(true);
Object jarFactory = getInstance.invoke(jarFactoryClazz);
// JarFile jarFile = jarFactory.get(url);
Method get = jarFactoryClazz.getMethod("get", URL.class);
get.setAccessible(true);
Object jarFile = get.invoke(jarFactory, url);
// jarFactory.close(jarFile);
Method close = jarFactoryClazz.getMethod("close", JarFile.class);
close.setAccessible(true);
//noinspection JavaReflectionInvocation
close.invoke(jarFactory, jarFile);
// jarFile.close();
((JarFile) jarFile).close();
}
}
// and in your code:
// i is now closed, so we should be good to delete the jar
JarBridge.closeJar(j);
System.out.println(f.delete()); // says true, phew.
कृपया ध्यान दें: यह सब जावा 8 कोडबेस ( 1.8.0_144
) पर आधारित है ; वे अन्य / बाद के संस्करणों के साथ काम नहीं कर सकते हैं।