Add option to tolerate unexpected problem with jar file (IDEA-163705)

This commit is contained in:
Maxim.Mossienko
2018-01-25 21:31:41 +01:00
parent 217cb82645
commit 5ed73135f9
3 changed files with 30 additions and 17 deletions
@@ -20,12 +20,14 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import java.util.HashMap;
import com.intellij.util.containers.Stack;
import com.intellij.util.io.URLUtil;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -47,13 +49,14 @@ public class ClassPath {
private final Map<URL, Loader> myLoadersMap = new HashMap<URL, Loader>();
private final ClasspathCache myCache = new ClasspathCache();
private final boolean myCanLockJars;
final boolean myCanLockJars; // true implies that the .jar file will not be modified in the lifetime of the JarLoader
private final boolean myCanUseCache;
private final boolean myAcceptUnescapedUrls;
private final boolean myPreloadJarContents;
private final boolean myCanHavePersistentIndex;
final boolean myPreloadJarContents;
final boolean myCanHavePersistentIndex;
@Nullable private final CachePoolImpl myCachePool;
@Nullable private final UrlClassLoader.CachingCondition myCachingCondition;
final boolean myLogErrorOnMissingJar;
public ClassPath(List<URL> urls,
boolean canLockJars,
@@ -62,7 +65,8 @@ public class ClassPath {
boolean preloadJarContents,
boolean canHavePersistentIndex,
@Nullable CachePoolImpl cachePool,
@Nullable UrlClassLoader.CachingCondition cachingCondition) {
@Nullable UrlClassLoader.CachingCondition cachingCondition,
boolean logErrorOnMissingJar) {
myCanLockJars = canLockJars;
myCanUseCache = canUseCache;
myAcceptUnescapedUrls = acceptUnescapedUrls;
@@ -70,6 +74,7 @@ public class ClassPath {
myCachePool = cachePool;
myCachingCondition = cachingCondition;
myCanHavePersistentIndex = canHavePersistentIndex;
myLogErrorOnMissingJar = logErrorOnMissingJar;
push(urls);
}
@@ -199,7 +204,7 @@ public class ClassPath {
return new FileLoader(url, index, myCanHavePersistentIndex);
}
if (file.isFile()) {
Loader loader = new JarLoader(url, myCanLockJars, index, myPreloadJarContents, this);
Loader loader = new JarLoader(url, index, this);
if (processRecursively) {
String[] referencedJars = loadManifestClasspath((JarLoader)loader);
if (referencedJars != null) {
@@ -44,32 +44,32 @@ class JarLoader extends Loader {
pair(Resource.Attribute.IMPL_VENDOR, Attributes.Name.IMPLEMENTATION_VENDOR));
private final String myFilePath;
private final boolean myCanLockJar; // true implies that the .jar file will not be modified in the lifetime of the JarLoader
private final ClassPath myConfiguration;
private SoftReference<JarMemoryLoader> myMemoryLoader;
private volatile SoftReference<ZipFile> myZipFileSoftReference; // Used only when myCanLockJar==true
private final Map<Resource.Attribute, String> myAttributes;
private volatile SoftReference<Attributes> myCachedManifestAttributes;
JarLoader(URL url, boolean canLockJar, int index, boolean preloadJarContents, ClassPath classPath) throws IOException {
JarLoader(URL url, int index, ClassPath configuration) throws IOException {
super(new URL("jar", "", -1, url + "!/"), index);
myFilePath = urlToFilePath(url);
myCanLockJar = canLockJar;
myConfiguration = configuration;
ZipFile zipFile = getZipFile(); // IOException from opening is propagated to caller if zip file isn't valid,
try {
Attributes manifestAttributes = classPath.getManifestData(url);
Attributes manifestAttributes = configuration.getManifestData(url);
if (manifestAttributes == null) {
ZipEntry entry = zipFile.getEntry(JarFile.MANIFEST_NAME);
manifestAttributes = loadManifestAttributes(entry != null ? zipFile.getInputStream(entry) : null);
if (manifestAttributes == null) manifestAttributes = new Attributes(0);
classPath.cacheManifestData(url, manifestAttributes);
configuration.cacheManifestData(url, manifestAttributes);
}
myAttributes = getAttributes(manifestAttributes);
myCachedManifestAttributes = new SoftReference<Attributes>(manifestAttributes);
if (preloadJarContents) {
if (configuration.myPreloadJarContents) {
JarMemoryLoader loader = JarMemoryLoader.load(zipFile, getBaseURL(), myAttributes);
if (loader != null) {
myMemoryLoader = new SoftReference<JarMemoryLoader>(loader);
@@ -211,7 +211,12 @@ class JarLoader extends Loader {
}
protected void error(String message, Throwable t) {
Logger.getInstance(JarLoader.class).error(message, t);
if (myConfiguration.myLogErrorOnMissingJar) {
Logger.getInstance(JarLoader.class).error(message, t);
}
else {
Logger.getInstance(JarLoader.class).warn(message, t);
}
}
private static final Object ourLock = new Object();
@@ -220,7 +225,7 @@ class JarLoader extends Loader {
private ZipFile getZipFile() throws IOException {
// This code is executed at least 100K times (O(number of classes needed to load)) and it takes considerable time to open ZipFile's
// such number of times so we store reference to ZipFile if we allowed to lock the file (assume it isn't changed)
if (myCanLockJar) {
if (myConfiguration.myCanLockJars) {
ZipFile zipFile = SoftReference.dereference(myZipFileSoftReference);
if (zipFile != null) return zipFile;
@@ -241,7 +246,7 @@ class JarLoader extends Loader {
private void releaseZipFile(ZipFile zipFile) throws IOException {
// Closing of zip file when myCanLockJar=true happens in ZipFile.finalize
if (!myCanLockJar) {
if (!myConfiguration.myCanLockJars) {
zipFile.close();
}
}
@@ -92,6 +92,7 @@ public class UrlClassLoader extends ClassLoader {
private boolean myAcceptUnescaped;
private boolean myPreload = true;
private boolean myAllowBootstrapResources;
private boolean myErrorOnMissingJar = true;
@Nullable private CachePoolImpl myCachePool;
@Nullable private CachingCondition myCachingCondition;
@@ -137,6 +138,7 @@ public class UrlClassLoader extends ClassLoader {
public Builder allowUnescaped() { myAcceptUnescaped = true; return this; }
public Builder noPreload() { myPreload = false; return this; }
public Builder allowBootstrapResources() { myAllowBootstrapResources = true; return this; }
public Builder setLogErrorOnMissingJar(boolean log) {myErrorOnMissingJar = log; return this; }
/** @deprecated use {@link #allowUnescaped()} (to be removed in IDEA 2018) */
public Builder allowUnescaped(boolean acceptUnescaped) { myAcceptUnescaped = acceptUnescaped; return this; }
@@ -177,7 +179,8 @@ public class UrlClassLoader extends ClassLoader {
@NotNull
protected final ClassPath createClassPath(@NotNull Builder builder) {
return new ClassPath(myURLs, builder.myLockJars, builder.myUseCache, builder.myAcceptUnescaped, builder.myPreload,
builder.myUsePersistentClasspathIndex, builder.myCachePool, builder.myCachingCondition);
builder.myUsePersistentClasspathIndex, builder.myCachePool, builder.myCachingCondition,
builder.myErrorOnMissingJar);
}
public static URL internProtocol(@NotNull URL url) {