mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-122914 (unclosed zip files)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,7 +59,7 @@ public class JarVersionDetectionUtil {
|
||||
}
|
||||
|
||||
/** @deprecated use {@link JarUtil#getJarAttribute(File, Attributes.Name)} (to remove in IDEA 15) */
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
|
||||
public static String detectJarVersion(@Nullable com.intellij.openapi.vfs.JarFile zipFile) {
|
||||
if (zipFile == null) {
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,9 +22,7 @@ import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Encapsulates operations with .jar file
|
||||
*/
|
||||
/** @deprecated causes ZipFile leaks, do not use (to be removed in IDEA 15) */
|
||||
public interface JarFile {
|
||||
interface JarEntry {
|
||||
String getName();
|
||||
|
||||
@@ -17,16 +17,12 @@ package com.intellij.openapi.vfs.impl.jar;
|
||||
|
||||
import com.intellij.openapi.diagnostic.LogUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
|
||||
import com.intellij.openapi.util.io.FileAttributes;
|
||||
import com.intellij.openapi.util.io.FileSystemUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFile;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.TimedReference;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -45,10 +41,9 @@ public class JarHandlerBase {
|
||||
protected static final long DEFAULT_LENGTH = 0L;
|
||||
protected static final long DEFAULT_TIMESTAMP = -1L;
|
||||
|
||||
private final TimedReference<JarFile> myJarFile = new TimedReference<JarFile>(null);
|
||||
private final Object myLock = new Object();
|
||||
private volatile Reference<Map<String, EntryInfo>> myRelPathsToEntries = new SoftReference<Map<String, EntryInfo>>(null);
|
||||
private boolean myCorruptedJar = false;
|
||||
private final Object myLock = new Object();
|
||||
|
||||
protected final String myBasePath;
|
||||
|
||||
@@ -76,34 +71,17 @@ public class JarHandlerBase {
|
||||
return originalFile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/** @deprecated to be removed in IDEA 15 */
|
||||
@SuppressWarnings("deprecation")
|
||||
public JarFile getJar() {
|
||||
JarFile jar = myJarFile.get();
|
||||
if (jar == null) {
|
||||
synchronized (myLock) {
|
||||
if (myCorruptedJar) {
|
||||
return null;
|
||||
}
|
||||
jar = myJarFile.get();
|
||||
if (jar == null) {
|
||||
try {
|
||||
jar = createJarFile();
|
||||
myJarFile.set(jar);
|
||||
}
|
||||
catch (IOException e) {
|
||||
myCorruptedJar = true;
|
||||
LOG.warn(e.getMessage() + ": " + myBasePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
File mirror = getMirrorFile(getOriginalFile());
|
||||
return new MyJarFile(new ZipFile(mirror));
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e.getMessage() + ": " + myBasePath, e);
|
||||
return null;
|
||||
}
|
||||
return jar;
|
||||
}
|
||||
|
||||
private JarFile createJarFile() throws IOException {
|
||||
File mirrorFile = getMirrorFile(getOriginalFile());
|
||||
return new MyJarFile(new ZipFile(mirrorFile));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -146,25 +124,34 @@ public class JarHandlerBase {
|
||||
map = SoftReference.dereference(myRelPathsToEntries);
|
||||
|
||||
if (map == null) {
|
||||
JarFile zip = getJar();
|
||||
if (zip != null) {
|
||||
if (myCorruptedJar) {
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
else {
|
||||
LogUtil.debug(LOG, "mapping %s", myBasePath);
|
||||
|
||||
map = new THashMap<String, EntryInfo>();
|
||||
map.put("", new EntryInfo(null, "", true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP));
|
||||
|
||||
Enumeration<? extends JarFile.JarEntry> entries = zip.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarFile.JarEntry entry = entries.nextElement();
|
||||
if (entry == null) break; // corrupted .jar
|
||||
getOrCreate(entry, map, zip);
|
||||
try {
|
||||
ZipFile zip = getZipFile();
|
||||
try {
|
||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
getOrCreate(entries.nextElement(), map, zip);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
ZipFileCache.release(zip);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
myCorruptedJar = true;
|
||||
LOG.warn(e.getMessage() + ": " + myBasePath, e);
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
myRelPathsToEntries = new SoftReference<Map<String, EntryInfo>>(Collections.unmodifiableMap(map));
|
||||
}
|
||||
else {
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
myRelPathsToEntries = new SoftReference<Map<String, EntryInfo>>(Collections.unmodifiableMap(map));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +159,13 @@ public class JarHandlerBase {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EntryInfo getOrCreate(JarFile.JarEntry entry, Map<String, EntryInfo> map, JarFile zip) {
|
||||
private ZipFile getZipFile() throws IOException {
|
||||
File mirror = getMirrorFile(getOriginalFile());
|
||||
return ZipFileCache.acquire(mirror.getPath());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EntryInfo getOrCreate(ZipEntry entry, Map<String, EntryInfo> map, ZipFile zip) {
|
||||
boolean isDirectory = entry.isDirectory();
|
||||
String entryName = entry.getName();
|
||||
if (StringUtil.endsWithChar(entryName, '/')) {
|
||||
@@ -199,11 +192,11 @@ public class JarHandlerBase {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EntryInfo getOrCreate(String entryName, Map<String, EntryInfo> map, JarFile zip) {
|
||||
private static EntryInfo getOrCreate(String entryName, Map<String, EntryInfo> map, ZipFile zip) {
|
||||
EntryInfo info = map.get(entryName);
|
||||
|
||||
if (info == null) {
|
||||
JarFile.JarEntry entry = zip.getEntry(entryName + "/");
|
||||
ZipEntry entry = zip.getEntry(entryName + "/");
|
||||
if (entry != null) {
|
||||
return getOrCreate(entry, map, zip);
|
||||
}
|
||||
@@ -218,8 +211,7 @@ public class JarHandlerBase {
|
||||
}
|
||||
|
||||
if (!info.isDirectory) {
|
||||
//noinspection ConstantConditions
|
||||
LOG.info(zip.getZipFile().getName() + ": " + entryName + " should be a directory");
|
||||
LOG.info(zip.getName() + ": " + entryName + " should be a directory");
|
||||
info = new EntryInfo(info.parent, info.shortName, true, info.length, info.timestamp);
|
||||
map.put(entryName, info);
|
||||
}
|
||||
@@ -246,11 +238,9 @@ public class JarHandlerBase {
|
||||
}
|
||||
|
||||
public boolean exists(@NotNull VirtualFile file) {
|
||||
if (file.getParent() == null) {
|
||||
return myJarFile.get() != null || getOriginalFile().exists();
|
||||
}
|
||||
|
||||
return getEntryInfo(file) != null;
|
||||
if (file.getParent() == null) return getOriginalFile().exists();
|
||||
EntryInfo info = getEntryInfo(file);
|
||||
return info != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -271,11 +261,11 @@ public class JarHandlerBase {
|
||||
|
||||
@NotNull
|
||||
public byte[] contentsToByteArray(@NotNull VirtualFile file) throws IOException {
|
||||
JarFile jar = getJar();
|
||||
if (jar != null) {
|
||||
JarFile.JarEntry entry = jar.getEntry(getRelativePath(file));
|
||||
ZipFile zip = getZipFile();
|
||||
try {
|
||||
ZipEntry entry = zip.getEntry(getRelativePath(file));
|
||||
if (entry != null) {
|
||||
InputStream stream = jar.getInputStream(entry);
|
||||
InputStream stream = zip.getInputStream(entry);
|
||||
if (stream != null) {
|
||||
try {
|
||||
return FileUtil.loadBytes(stream, (int)entry.getSize());
|
||||
@@ -286,9 +276,13 @@ public class JarHandlerBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
ZipFileCache.release(zip);
|
||||
}
|
||||
return ArrayUtil.EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static class MyJarFile implements JarFile {
|
||||
private static class MyJarEntry implements JarFile.JarEntry {
|
||||
private final ZipEntry myEntry;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,8 @@ public abstract class JarFileSystem extends NewVirtualFileSystem implements JarC
|
||||
@Nullable
|
||||
public abstract VirtualFile getVirtualFileForJar(@Nullable VirtualFile entryVFile);
|
||||
|
||||
/** @deprecated do not use (leaks file handles), to remove in IDEA 15 */
|
||||
/** @deprecated to be removed in IDEA 15 */
|
||||
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
|
||||
public abstract JarFile getJarFile(@NotNull VirtualFile entryVFile) throws IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -215,20 +215,19 @@ public class FileContentQueue {
|
||||
if (isValidFile(virtualFileToLoad)) {
|
||||
try {
|
||||
content.getBytes();
|
||||
return content;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(virtualFileToLoad + ": " + e);
|
||||
}
|
||||
catch (InvalidVirtualFileAccessException e) {
|
||||
LOG.info(virtualFileToLoad + ": " + e);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (t instanceof IOException || t instanceof InvalidVirtualFileAccessException) {
|
||||
LOG.info(t);
|
||||
}
|
||||
else {
|
||||
LOG.error(t);
|
||||
}
|
||||
content.setEmptyContent();
|
||||
LOG.error(virtualFileToLoad + ": " + t);
|
||||
}
|
||||
}
|
||||
else {
|
||||
content.setEmptyContent();
|
||||
}
|
||||
content.setEmptyContent();
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -22,6 +22,7 @@ import com.intellij.openapi.components.ApplicationComponent;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileAttributes;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.ZipFileCache;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.openapi.vfs.newvfs.*;
|
||||
@@ -39,7 +40,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -64,7 +64,7 @@ public class JarFileSystemImpl extends JarFileSystem implements ApplicationCompo
|
||||
bus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
List<VirtualFile> rootsToRefresh = new ArrayList<VirtualFile>();
|
||||
List<VirtualFile> rootsToRefresh = ContainerUtil.newSmartList();
|
||||
|
||||
for (VFileEvent event : events) {
|
||||
if (event.getFileSystem() instanceof LocalFileSystem) {
|
||||
@@ -91,6 +91,14 @@ public class JarFileSystemImpl extends JarFileSystem implements ApplicationCompo
|
||||
}
|
||||
|
||||
if (!rootsToRefresh.isEmpty()) {
|
||||
List<String> pathsToReset = ContainerUtil.newArrayListWithCapacity(rootsToRefresh.size());
|
||||
for (VirtualFile root : rootsToRefresh) {
|
||||
String rootPath = root.getPath();
|
||||
String jarPath = rootPath.substring(0, rootPath.length() - 2);
|
||||
pathsToReset.add(FileUtil.toSystemDependentName(jarPath));
|
||||
}
|
||||
ZipFileCache.reset(pathsToReset);
|
||||
|
||||
boolean async = !ApplicationManager.getApplication().isUnitTestMode();
|
||||
RefreshQueue.getInstance().refresh(async, true, null, rootsToRefresh);
|
||||
}
|
||||
@@ -144,7 +152,9 @@ public class JarFileSystemImpl extends JarFileSystem implements ApplicationCompo
|
||||
return StandardFileSystems.getVirtualFileForJar(entryVFile);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
/** @deprecated to be removed in IDEA 15 */
|
||||
public JarFile getJarFile(@NotNull VirtualFile entryVFile) throws IOException {
|
||||
return getHandler(entryVFile).getJar();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.util.io;
|
||||
|
||||
import com.intellij.openapi.diagnostic.LogUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* <p>Utility class which tries to keep frequently requested .zip files open
|
||||
* to avoid time loss on closing/reopening ZipFile instances.</p>
|
||||
*
|
||||
* <p>Clients obtain a file by calling {@link #acquire(String)}
|
||||
* and indicate the loss of interest to it via {@link #release(ZipFile)}.
|
||||
* Released files are closed after some period of time (about 30 seconds),
|
||||
* unless requested again within the period.</p>
|
||||
*
|
||||
* <p>Since ZipFiles are read-only objects allowing concurrent access,
|
||||
* a same instance may be returned to a different threads requesting a same path.
|
||||
* A file may be closed only after being released by all applicants.</p>
|
||||
*
|
||||
* <p>The class does not expect .zip files on a disk to be changed,
|
||||
* so it may return an outdated instance of ZipFile (reading from it
|
||||
* may return inaccurate data or even cause an exceptions to happen).
|
||||
* It's a clients' responsibility to keep a track of .zip files
|
||||
* and call the {@link #reset(Collection)} method for a paths
|
||||
* which are possibly changed. Reset paths are removed from the cache
|
||||
* and are closed immediately after being released.</p>
|
||||
*/
|
||||
public class ZipFileCache {
|
||||
private static final int PERIOD = 10000; // disposer schedule, ms
|
||||
private static final int TIMEOUT = 30000; // released file close delay, ms
|
||||
|
||||
private static class CacheRecord {
|
||||
private final String path;
|
||||
private final ZipFile file;
|
||||
private int count = 1;
|
||||
private long released = 0;
|
||||
|
||||
private CacheRecord(@NotNull String path, @NotNull ZipFile file) throws IOException {
|
||||
this.path = path;
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object ourLock = new Object();
|
||||
private static final Map<String, CacheRecord> ourPathCache = ContainerUtil.newTroveMap(FileUtil.PATH_HASHING_STRATEGY);
|
||||
private static final Map<ZipFile, CacheRecord> ourFileCache = ContainerUtil.newHashMap();
|
||||
private static final Map<ZipFile, Integer> ourQueue = ContainerUtil.newHashMap();
|
||||
|
||||
static {
|
||||
ConcurrencyUtil.newSingleScheduledThreadExecutor("ZipFileCache Dispose", Thread.MIN_PRIORITY).scheduleWithFixedDelay(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<ZipFile> toClose = getFilesToClose(0, System.currentTimeMillis() - TIMEOUT);
|
||||
if (toClose != null) {
|
||||
close(toClose);
|
||||
}
|
||||
}
|
||||
}, PERIOD, PERIOD, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ZipFile acquire(@NotNull String path) throws IOException {
|
||||
path = FileUtil.toCanonicalPath(path);
|
||||
|
||||
synchronized (ourLock) {
|
||||
CacheRecord record = ourPathCache.get(path);
|
||||
if (record != null) {
|
||||
record.count++;
|
||||
return record.file;
|
||||
}
|
||||
}
|
||||
|
||||
CacheRecord record;
|
||||
ZipFile file = tryOpen(path);
|
||||
|
||||
synchronized (ourLock) {
|
||||
record = ourPathCache.get(path);
|
||||
if (record == null) {
|
||||
record = new CacheRecord(path, file);
|
||||
ourPathCache.put(path, record);
|
||||
ourFileCache.put(file, record);
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
record.count++;
|
||||
}
|
||||
}
|
||||
|
||||
close(file);
|
||||
return record.file;
|
||||
}
|
||||
|
||||
private static ZipFile tryOpen(String path) throws IOException {
|
||||
path = FileUtil.toSystemDependentName(path);
|
||||
debug("opening %s", path);
|
||||
try {
|
||||
return new ZipFile(path);
|
||||
}
|
||||
catch (IOException e) {
|
||||
String reason = e.getMessage();
|
||||
if ("too many open files".equalsIgnoreCase(reason) && tryCloseFiles() > 0) {
|
||||
return new ZipFile(path);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int tryCloseFiles() {
|
||||
List<ZipFile> toClose = getFilesToClose(5, 0);
|
||||
if (toClose == null) return 0;
|
||||
close(toClose);
|
||||
logger().warn("too many open files, closed: " + toClose.size());
|
||||
return toClose.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<ZipFile> getFilesToClose(int limit, long timeout) {
|
||||
List<ZipFile> toClose = null;
|
||||
|
||||
synchronized (ourLock) {
|
||||
Iterator<CacheRecord> i = ourPathCache.values().iterator();
|
||||
while (i.hasNext() && (limit == 0 || toClose == null || toClose.size() < limit)) {
|
||||
CacheRecord record = i.next();
|
||||
if (record.count <= 0 && (timeout == 0 || record.released <= timeout)) {
|
||||
i.remove();
|
||||
ourFileCache.remove(record.file);
|
||||
if (toClose == null) toClose = ContainerUtil.newArrayList();
|
||||
toClose.add(record.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return toClose;
|
||||
}
|
||||
|
||||
public static void release(@NotNull ZipFile file) {
|
||||
synchronized (ourLock) {
|
||||
CacheRecord record = ourFileCache.get(file);
|
||||
if (record != null) {
|
||||
record.count--;
|
||||
record.released = System.currentTimeMillis();
|
||||
logger().assertTrue(record.count >= 0, record.path);
|
||||
return;
|
||||
}
|
||||
|
||||
Integer count = ourQueue.get(file);
|
||||
if (count != null) {
|
||||
count--;
|
||||
if (count == 0) {
|
||||
ourQueue.remove(file);
|
||||
close(file);
|
||||
}
|
||||
else {
|
||||
ourQueue.put(file, count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
logger().warn(new IllegalArgumentException("stray file: " + file.getName()));
|
||||
close(file);
|
||||
}
|
||||
|
||||
public static void reset(@NotNull Collection<String> paths) {
|
||||
debug("resetting %s", paths);
|
||||
|
||||
List<ZipFile> toClose = ContainerUtil.newSmartList();
|
||||
|
||||
synchronized (ourLock) {
|
||||
for (String path : paths) {
|
||||
CacheRecord record = ourPathCache.remove(path);
|
||||
if (record != null) {
|
||||
ourFileCache.remove(record.file);
|
||||
if (record.count > 0) {
|
||||
ourQueue.put(record.file, record.count);
|
||||
}
|
||||
else {
|
||||
toClose.add(record.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(toClose);
|
||||
}
|
||||
|
||||
private static void close(@NotNull List<ZipFile> files) {
|
||||
for (ZipFile file : files) {
|
||||
close(file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void close(@NotNull ZipFile file) {
|
||||
debug("closing %s", file.getName());
|
||||
try {
|
||||
file.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger().info(file.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Logger logger() {
|
||||
return Logger.getInstance(ZipFileCache.class);
|
||||
}
|
||||
|
||||
private static void debug(@NotNull String format, Object... args) {
|
||||
LogUtil.debug(logger(), format, args);
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,12 @@ package com.intellij.util.lang;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.TimedComputable;
|
||||
import com.intellij.openapi.util.io.ZipFileCache;
|
||||
import com.intellij.util.io.URLUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.misc.Resource;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
@@ -35,21 +33,8 @@ class JarLoader extends Loader {
|
||||
private static final Logger LOG = Logger.getInstance(JarLoader.class);
|
||||
|
||||
private final URL myURL;
|
||||
private SoftReference<JarMemoryLoader> myMemoryLoader;
|
||||
private final boolean myCanLockJar;
|
||||
|
||||
private final TimedComputable<ZipFile> myZipFileRef = new TimedComputable<ZipFile>(null) {
|
||||
@Override
|
||||
@NotNull
|
||||
protected ZipFile calc() {
|
||||
try {
|
||||
return doGetZipFile();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
private SoftReference<JarMemoryLoader> myMemoryLoader;
|
||||
|
||||
JarLoader(URL url, boolean canLockJar, int index) throws IOException {
|
||||
super(new URL(URLUtil.JAR_PROTOCOL, "", -1, url + "!/"), index);
|
||||
@@ -57,61 +42,44 @@ class JarLoader extends Loader {
|
||||
myCanLockJar = canLockJar;
|
||||
}
|
||||
|
||||
void preloadClasses() {
|
||||
ZipFile zipFile = null;
|
||||
try {
|
||||
zipFile = acquireZipFile();
|
||||
if (zipFile == null) return;
|
||||
try {
|
||||
File file = new File(zipFile.getName());
|
||||
myMemoryLoader = new SoftReference<JarMemoryLoader>(JarMemoryLoader.load(file, getBaseURL()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// it happens :) eg tools.jar under MacOS
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
releaseZipFile(zipFile);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ZipFile acquireZipFile() throws IOException {
|
||||
if (myCanLockJar) {
|
||||
return myZipFileRef.acquire();
|
||||
}
|
||||
return doGetZipFile();
|
||||
String path = FileUtil.unquote(myURL.getFile());
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
return myCanLockJar ? ZipFileCache.acquire(path) : new ZipFile(path);
|
||||
}
|
||||
|
||||
private void releaseZipFile(final ZipFile zipFile) throws IOException {
|
||||
private void releaseZipFile(ZipFile zipFile) throws IOException {
|
||||
if (myCanLockJar) {
|
||||
myZipFileRef.release();
|
||||
ZipFileCache.release(zipFile);
|
||||
}
|
||||
else if (zipFile != null) {
|
||||
zipFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
private ZipFile doGetZipFile() throws IOException {
|
||||
return new ZipFile(FileUtil.unquote(myURL.getFile()));
|
||||
void preloadClasses() {
|
||||
try {
|
||||
ZipFile zipFile = acquireZipFile();
|
||||
try {
|
||||
JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL());
|
||||
if (loader != null) {
|
||||
myMemoryLoader = new SoftReference<JarMemoryLoader>(loader);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
releaseZipFile(zipFile);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void buildCache(final ClasspathCache cache) throws IOException {
|
||||
ZipFile zipFile = null;
|
||||
void buildCache(ClasspathCache cache) throws IOException {
|
||||
ZipFile zipFile = acquireZipFile();
|
||||
try {
|
||||
zipFile = acquireZipFile();
|
||||
if (zipFile == null) return;
|
||||
|
||||
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry zipEntry = entries.nextElement();
|
||||
String name = zipEntry.getName();
|
||||
@@ -133,101 +101,26 @@ class JarLoader extends Loader {
|
||||
if (resource != null) return resource;
|
||||
}
|
||||
|
||||
ZipFile file = null;
|
||||
try {
|
||||
file = acquireZipFile();
|
||||
if (file == null) return null;
|
||||
ZipEntry entry = file.getEntry(name);
|
||||
if (entry != null) {
|
||||
return new MyResource(entry, new URL(getBaseURL(), name));
|
||||
ZipFile file = acquireZipFile();
|
||||
try {
|
||||
ZipEntry entry = file.getEntry(name);
|
||||
if (entry != null) {
|
||||
return MemoryResource.load(getBaseURL(), file, entry);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
releaseZipFile(file);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
releaseZipFile(file);
|
||||
}
|
||||
catch (IOException ignored) { }
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class MyResource extends Resource {
|
||||
private final ZipEntry myEntry;
|
||||
private final URL myUrl;
|
||||
|
||||
public MyResource(ZipEntry name, URL url) {
|
||||
myEntry = name;
|
||||
myUrl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return myEntry.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return myUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeSourceURL() {
|
||||
return myURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public InputStream getInputStream() throws IOException {
|
||||
final boolean[] wasReleased = {false};
|
||||
ZipFile file = null;
|
||||
|
||||
try {
|
||||
file = acquireZipFile();
|
||||
if (file == null) {
|
||||
releaseZipFile(file);
|
||||
return null;
|
||||
}
|
||||
|
||||
final InputStream inputStream = file.getInputStream(myEntry);
|
||||
if (inputStream == null) {
|
||||
releaseZipFile(file);
|
||||
return null; // if entry was not found
|
||||
}
|
||||
|
||||
final ZipFile finalFile = file;
|
||||
return new FilterInputStream(inputStream) {
|
||||
private boolean myClosed = false;
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
if (!myClosed) {
|
||||
releaseZipFile(finalFile);
|
||||
}
|
||||
myClosed = true;
|
||||
wasReleased[0] = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
releaseZipFile(file);
|
||||
assert !wasReleased[0];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContentLength() {
|
||||
return (int)myEntry.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JarLoader [" + myURL + "]";
|
||||
}
|
||||
|
||||
@@ -16,21 +16,22 @@
|
||||
package com.intellij.util.lang;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.io.UnsyncByteArrayInputStream;
|
||||
import com.intellij.util.io.zip.ZipShort;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.misc.Resource;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
* Date: 7/12/11
|
||||
* @since 12/07/2011
|
||||
*/
|
||||
public class JarMemoryLoader {
|
||||
public static final String SIZE_ENTRY = "META-INF/jb/$$size$$";
|
||||
@@ -45,73 +46,32 @@ public class JarMemoryLoader {
|
||||
|
||||
@Nullable
|
||||
public static JarMemoryLoader load(File file, URL baseUrl) throws IOException {
|
||||
FileInputStream inputStream = new FileInputStream(file);
|
||||
ZipFile zipFile = new ZipFile(file);
|
||||
try {
|
||||
return load(inputStream, baseUrl);
|
||||
return load(zipFile, baseUrl);
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
zipFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JarMemoryLoader load(InputStream inputStream, URL baseUrl) throws IOException {
|
||||
ZipInputStream zipStream = new ZipInputStream(inputStream);
|
||||
try {
|
||||
ZipEntry sizeEntry = zipStream.getNextEntry();
|
||||
if (sizeEntry == null || !sizeEntry.getName().equals(SIZE_ENTRY)) return null;
|
||||
byte[] bytes = FileUtil.loadBytes(zipStream, 2);
|
||||
int size = ZipShort.getValue(bytes);
|
||||
public static JarMemoryLoader load(ZipFile zipFile, URL baseUrl) throws IOException {
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
if (!entries.hasMoreElements()) return null;
|
||||
|
||||
JarMemoryLoader loader = new JarMemoryLoader();
|
||||
for (int i = 0; i < size; i++) {
|
||||
ZipEntry entry = zipStream.getNextEntry();
|
||||
if (entry == null) return loader;
|
||||
byte[] content = FileUtil.loadBytes(zipStream, (int)entry.getSize());
|
||||
MyResource resource = new MyResource(entry.getName(), new URL(baseUrl, entry.getName()), content);
|
||||
loader.myResources.put(entry.getName(), resource);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
finally {
|
||||
zipStream.close();
|
||||
}
|
||||
}
|
||||
ZipEntry sizeEntry = entries.nextElement();
|
||||
if (sizeEntry == null || !sizeEntry.getName().equals(SIZE_ENTRY)) return null;
|
||||
|
||||
private static class MyResource extends Resource {
|
||||
private String myName;
|
||||
private URL myUrl;
|
||||
private final byte[] myContent;
|
||||
byte[] bytes = FileUtil.loadBytes(zipFile.getInputStream(sizeEntry), 2);
|
||||
int size = ZipShort.getValue(bytes);
|
||||
|
||||
public MyResource(String name, URL url, byte[] content) {
|
||||
myName = name;
|
||||
myUrl = url;
|
||||
myContent = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return myUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeSourceURL() {
|
||||
return myUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new UnsyncByteArrayInputStream(myContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContentLength() throws IOException {
|
||||
return myContent.length;
|
||||
JarMemoryLoader loader = new JarMemoryLoader();
|
||||
for (int i = 0; i < size; i++) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
MemoryResource resource = MemoryResource.load(baseUrl, zipFile, entry);
|
||||
loader.myResources.put(entry.getName(), resource);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.util.lang;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.io.UnsyncByteArrayInputStream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sun.misc.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
class MemoryResource extends Resource {
|
||||
private String myName;
|
||||
private URL myUrl;
|
||||
private final byte[] myContent;
|
||||
|
||||
public MemoryResource(String name, URL url, byte[] content) {
|
||||
myName = name;
|
||||
myUrl = url;
|
||||
myContent = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return myUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeSourceURL() {
|
||||
return myUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new UnsyncByteArrayInputStream(myContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContentLength() throws IOException {
|
||||
return myContent.length;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static MemoryResource load(URL baseUrl, @NotNull ZipFile zipFile, @NotNull ZipEntry entry) throws IOException {
|
||||
String name = entry.getName();
|
||||
URL url = new URL(baseUrl, name);
|
||||
|
||||
byte[] content = ArrayUtil.EMPTY_BYTE_ARRAY;
|
||||
InputStream stream = zipFile.getInputStream(entry);
|
||||
if (stream != null) {
|
||||
try {
|
||||
content = FileUtil.loadBytes(stream, (int)entry.getSize());
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
return new MemoryResource(name, url, content);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user