aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/launch/lombok/launch/ShadowClassLoader.java134
1 files changed, 126 insertions, 8 deletions
diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java
index 37c479ee..70c58fb6 100644
--- a/src/launch/lombok/launch/ShadowClassLoader.java
+++ b/src/launch/lombok/launch/ShadowClassLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2015 The Project Lombok Authors.
+ * Copyright (C) 2014-2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,14 +21,19 @@
*/
package lombok.launch;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
@@ -36,14 +41,16 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.util.WeakHashMap;
-import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
/**
* The shadow classloader serves to completely hide almost all classes in a given jar file by using a different file ending.
@@ -292,6 +299,10 @@ class ShadowClassLoader extends ClassLoader {
return null;
}
+ private boolean partOfShadow(URL item, String name) {
+ return inOwnBase(item, name) || isPartOfShadowSuffix(item, name, sclSuffix);
+ }
+
/**
* Checks if the stated item is located inside the same classpath root as the jar that hosts ShadowClassLoader.class. {@code item} and {@code name} refer to the same thing.
*/
@@ -301,6 +312,113 @@ class ShadowClassLoader extends ClassLoader {
return (itemString.length() == SELF_BASE_LENGTH + name.length()) && SELF_BASE.regionMatches(0, itemString, 0, SELF_BASE_LENGTH);
}
+ private static boolean sclFileContainsSuffix(InputStream in, String suffix) throws IOException {
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ line = line.trim();
+ if (line.isEmpty() || line.charAt(0) == '#') continue;
+ if (line.equals(suffix)) return true;
+ }
+ return false;
+ }
+
+ private static String urlDecode(String in) {
+ try {
+ return URLDecoder.decode(in, Charset.defaultCharset().name());
+ } catch (UnsupportedEncodingException e) {
+ try {
+ return URLDecoder.decode(in, "UTF-8");
+ } catch (UnsupportedEncodingException e1) {
+ return in;
+ }
+ }
+ }
+
+ private Map<String, Boolean> fileRootCache = new HashMap<String, Boolean>();
+ private boolean isPartOfShadowSuffixFileBased(String fileRoot, String suffix) {
+ String key = fileRoot + "::" + suffix;
+ Boolean existing = fileRootCache.get(key);
+ if (existing != null) return existing.booleanValue();
+
+ File f = new File(fileRoot + "/META-INF/ShadowClassLoader");
+ try {
+ FileInputStream fis = new FileInputStream(f);
+ try {
+ boolean v = sclFileContainsSuffix(fis, suffix);
+ fileRootCache.put(key, v);
+ return v;
+ } finally {
+ fis.close();
+ }
+ } catch (FileNotFoundException fnfEx) {
+ fileRootCache.put(key, false);
+ return false;
+ } catch (IOException e) {
+ fileRootCache.put(key, false);
+ return false; // *unexpected*
+ }
+ }
+
+ private Map<String, Boolean> jarLocCache = new HashMap<String, Boolean>();
+ private boolean isPartOfShadowSuffixJarBased(String jarLoc, String suffix) {
+ String key = jarLoc + "::" + suffix;
+ Boolean existing = jarLocCache.get(key);
+ if (existing != null) return existing.booleanValue();
+
+ if (jarLoc.startsWith("file:/")) jarLoc = urlDecode(jarLoc.substring(5));
+ try {
+ FileInputStream jar = new FileInputStream(jarLoc);
+ try {
+ ZipInputStream zip = new ZipInputStream(jar);
+ while (true) {
+ ZipEntry entry = zip.getNextEntry();
+ if (entry == null) {
+ jarLocCache.put(key, false);
+ return false;
+ }
+ if (!"META-INF/ShadowClassLoader".equals(entry.getName())) continue;
+ boolean v = sclFileContainsSuffix(zip, suffix);
+ jarLocCache.put(key, v);
+ return v;
+ }
+ } finally {
+ jar.close();
+ }
+ } catch (FileNotFoundException fnfEx) {
+ jarLocCache.put(key, false);
+ return false;
+ } catch (IOException ex) {
+ jarLocCache.put(key, false);
+ return false; // *unexpected*
+ }
+ }
+
+ private boolean isPartOfShadowSuffix(URL item, String name, String suffix) {
+ // Instead of throwing an exception or logging, weird, unexpected cases just return false.
+ // This is better than throwing an exception, because exceptions would make your build tools unusable.
+ // Such cases are marked with the comment: // *unexpected*
+ if (item == null) return false;
+ String url = item.toString();
+ if (url.startsWith("file:/")) {
+ url = urlDecode(url.substring(5));
+ if (url.length() <= name.length() || !url.endsWith(name) || url.charAt(url.length() - name.length() - 1) != '/') {
+ return false; // *unexpected*
+ }
+
+ String fileRoot = url.substring(0, url.length() - name.length() - 1);
+ return isPartOfShadowSuffixFileBased(fileRoot, suffix);
+ } else if (url.startsWith("jar:")) {
+ int sep = url.indexOf('!');
+ if (sep == -1) {
+ return false; // *unexpected*
+ }
+ String jarLoc = url.substring(4, sep);
+ return isPartOfShadowSuffixJarBased(jarLoc, suffix);
+ }
+
+ return false;
+ }
+
@Override public Enumeration<URL> getResources(String name) throws IOException {
String altName = null;
if (name.endsWith(".class")) altName = name.substring(0, name.length() - 6) + ".SCL." + sclSuffix;
@@ -325,14 +443,14 @@ class ShadowClassLoader extends ClassLoader {
Enumeration<URL> sec = super.getResources(name);
while (sec.hasMoreElements()) {
URL item = sec.nextElement();
- if (!inOwnBase(item, name)) vector.add(item);
+ if (!partOfShadow(item, name)) vector.add(item);
}
if (altName != null) {
Enumeration<URL> tern = super.getResources(altName);
while (tern.hasMoreElements()) {
URL item = tern.nextElement();
- if (!inOwnBase(item, altName)) vector.add(item);
+ if (!partOfShadow(item, altName)) vector.add(item);
}
}
@@ -372,11 +490,11 @@ class ShadowClassLoader extends ClassLoader {
if (altName != null) {
URL res = super.getResource(altName);
- if (res != null && (!noSuper || inOwnBase(res, altName))) return res;
+ if (res != null && (!noSuper || partOfShadow(res, altName))) return res;
}
URL res = super.getResource(name);
- if (res != null && (!noSuper || inOwnBase(res, name))) return res;
+ if (res != null && (!noSuper || partOfShadow(res, name))) return res;
return null;
}
@@ -390,12 +508,12 @@ class ShadowClassLoader extends ClassLoader {
private URL getResourceSkippingSelf(String name) throws IOException {
URL candidate = super.getResource(name);
if (candidate == null) return null;
- if (!inOwnBase(candidate, name)) return candidate;
+ if (!partOfShadow(candidate, name)) return candidate;
Enumeration<URL> en = super.getResources(name);
while (en.hasMoreElements()) {
candidate = en.nextElement();
- if (!inOwnBase(candidate, name)) return candidate;
+ if (!partOfShadow(candidate, name)) return candidate;
}
return null;