이것이 발생할 수있는 또 다른 코너 케이스 : a를 통해 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();
try (InputStream i = c.getInputStream()) {
byte[] first16 = new byte[16];
i.read(first16);
System.out.println(new String(first16));
}
System.out.println(f.delete());
이유는 Java의 내부 JAR 파일 처리 논리가 JarFile
항목 을 캐시하는 경향이 있기 때문입니다 .
class JarURLInputStream extends FilterInputStream {
JarURLInputStream(InputStream var2) {
super(var2);
}
public void close() throws IOException {
try {
super.close();
} finally {
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 파일 캐싱을 비활성화 할 수 있습니다 . 현재 JVM 세션의 현재 URLConnection
또는 모든 미래 URLConnection
(전역)에 대해
URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();
c.setUseCaches(false);
c.setDefaultUseCaches(false);
[HACK WARNING!]JarFile
작업이 끝나면 캐시에서 수동으로 제거 할 수 있습니다 . 캐시 관리자 sun.net.www.protocol.jar.JarFileFactory
는 패키지 전용이지만 일부 반영 마법으로 작업을 수행 할 수 있습니다.
class JarBridge {
static void closeJar(URL url) throws Exception {
Class<?> jarFactoryClazz = Class.forName("sun.net.www.protocol.jar.JarFileFactory");
Method getInstance = jarFactoryClazz.getMethod("getInstance");
getInstance.setAccessible(true);
Object jarFactory = getInstance.invoke(jarFactoryClazz);
Method get = jarFactoryClazz.getMethod("get", URL.class);
get.setAccessible(true);
Object jarFile = get.invoke(jarFactory, url);
Method close = jarFactoryClazz.getMethod("close", JarFile.class);
close.setAccessible(true);
close.invoke(jarFactory, jarFile);
((JarFile) jarFile).close();
}
}
JarBridge.closeJar(j);
System.out.println(f.delete());
참고 :이 모든 것은 Java 8 코드베이스 ( 1.8.0_144
); 다른 / 이후 버전에서는 작동하지 않을 수 있습니다.