|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.loader; |
|
|
|
import java.io.Closeable; |
|
import java.io.File; |
|
import java.io.FileInputStream; |
|
import java.io.FileNotFoundException; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.net.HttpURLConnection; |
|
import java.net.JarURLConnection; |
|
import java.net.MalformedURLException; |
|
import java.net.URI; |
|
import java.net.URL; |
|
import java.net.URLConnection; |
|
import java.net.URLStreamHandler; |
|
import java.net.URLStreamHandlerFactory; |
|
import java.security.AccessControlContext; |
|
import java.security.AccessControlException; |
|
import java.security.AccessController; |
|
import java.security.CodeSigner; |
|
import java.security.Permission; |
|
import java.security.PrivilegedActionException; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.security.cert.Certificate; |
|
import java.util.ArrayDeque; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.Collections; |
|
import java.util.Enumeration; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
import java.util.NoSuchElementException; |
|
import java.util.Properties; |
|
import java.util.Set; |
|
import java.util.StringTokenizer; |
|
import java.util.jar.JarFile; |
|
import java.util.zip.ZipEntry; |
|
import java.util.jar.JarEntry; |
|
import java.util.jar.Manifest; |
|
import java.util.jar.Attributes; |
|
import java.util.jar.Attributes.Name; |
|
import java.util.zip.ZipFile; |
|
|
|
import jdk.internal.access.JavaNetURLAccess; |
|
import jdk.internal.access.JavaUtilZipFileAccess; |
|
import jdk.internal.access.SharedSecrets; |
|
import jdk.internal.util.jar.InvalidJarIndexError; |
|
import jdk.internal.util.jar.JarIndex; |
|
import sun.net.util.URLUtil; |
|
import sun.net.www.ParseUtil; |
|
import sun.security.action.GetPropertyAction; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class URLClassPath { |
|
private static final String USER_AGENT_JAVA_VERSION = "UA-Java-Version"; |
|
private static final String JAVA_VERSION; |
|
private static final boolean DEBUG; |
|
private static final boolean DISABLE_JAR_CHECKING; |
|
private static final boolean DISABLE_ACC_CHECKING; |
|
private static final boolean DISABLE_CP_URL_CHECK; |
|
private static final boolean DEBUG_CP_URL_CHECK; |
|
|
|
static { |
|
Properties props = GetPropertyAction.privilegedGetProperties(); |
|
JAVA_VERSION = props.getProperty("java.version"); |
|
DEBUG = (props.getProperty("sun.misc.URLClassPath.debug") != null); |
|
String p = props.getProperty("sun.misc.URLClassPath.disableJarChecking"); |
|
DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.isEmpty() : false; |
|
|
|
p = props.getProperty("jdk.net.URLClassPath.disableRestrictedPermissions"); |
|
DISABLE_ACC_CHECKING = p != null ? p.equals("true") || p.isEmpty() : false; |
|
|
|
|
|
p = props.getProperty("jdk.net.URLClassPath.disableClassPathURLCheck"); |
|
DISABLE_CP_URL_CHECK = p != null ? p.equals("true") || p.isEmpty() : false; |
|
|
|
// Print a message for each Class-Path entry that is ignored (assuming |
|
|
|
p = props.getProperty("jdk.net.URLClassPath.showIgnoredClassPathEntries"); |
|
DEBUG_CP_URL_CHECK = p != null ? p.equals("true") || p.isEmpty() : false; |
|
} |
|
|
|
|
|
private final ArrayList<URL> path; |
|
|
|
|
|
private final ArrayDeque<URL> unopenedUrls; |
|
|
|
|
|
private final ArrayList<Loader> loaders = new ArrayList<>(); |
|
|
|
|
|
private final HashMap<String, Loader> lmap = new HashMap<>(); |
|
|
|
|
|
private final URLStreamHandler jarHandler; |
|
|
|
|
|
private boolean closed = false; |
|
|
|
|
|
|
|
* URLClassLoader. null implies no additional security restrictions. */ |
|
@SuppressWarnings("removal") |
|
private final AccessControlContext acc; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public URLClassPath(URL[] urls, |
|
URLStreamHandlerFactory factory, |
|
@SuppressWarnings("removal") AccessControlContext acc) { |
|
ArrayList<URL> path = new ArrayList<>(urls.length); |
|
ArrayDeque<URL> unopenedUrls = new ArrayDeque<>(urls.length); |
|
for (URL url : urls) { |
|
path.add(url); |
|
unopenedUrls.add(url); |
|
} |
|
this.path = path; |
|
this.unopenedUrls = unopenedUrls; |
|
|
|
if (factory != null) { |
|
jarHandler = factory.createURLStreamHandler("jar"); |
|
} else { |
|
jarHandler = null; |
|
} |
|
if (DISABLE_ACC_CHECKING) |
|
this.acc = null; |
|
else |
|
this.acc = acc; |
|
} |
|
|
|
public URLClassPath(URL[] urls, @SuppressWarnings("removal") AccessControlContext acc) { |
|
this(urls, null, acc); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
URLClassPath(String cp, boolean skipEmptyElements) { |
|
ArrayList<URL> path = new ArrayList<>(); |
|
if (cp != null) { |
|
|
|
int off = 0, next; |
|
do { |
|
next = cp.indexOf(File.pathSeparator, off); |
|
String element = (next == -1) |
|
? cp.substring(off) |
|
: cp.substring(off, next); |
|
if (!element.isEmpty() || !skipEmptyElements) { |
|
URL url = toFileURL(element); |
|
if (url != null) path.add(url); |
|
} |
|
off = next + 1; |
|
} while (next != -1); |
|
} |
|
|
|
// can't use ArrayDeque#addAll or new ArrayDeque(Collection); |
|
|
|
int size = path.size(); |
|
ArrayDeque<URL> unopenedUrls = new ArrayDeque<>(size); |
|
for (int i = 0; i < size; i++) |
|
unopenedUrls.add(path.get(i)); |
|
|
|
this.unopenedUrls = unopenedUrls; |
|
this.path = path; |
|
this.jarHandler = null; |
|
this.acc = null; |
|
} |
|
|
|
public synchronized List<IOException> closeLoaders() { |
|
if (closed) { |
|
return Collections.emptyList(); |
|
} |
|
List<IOException> result = new LinkedList<>(); |
|
for (Loader loader : loaders) { |
|
try { |
|
loader.close(); |
|
} catch (IOException e) { |
|
result.add(e); |
|
} |
|
} |
|
closed = true; |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void addURL(URL url) { |
|
if (closed || url == null) |
|
return; |
|
synchronized (unopenedUrls) { |
|
if (! path.contains(url)) { |
|
unopenedUrls.addLast(url); |
|
path.add(url); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void addFile(String s) { |
|
URL url = toFileURL(s); |
|
if (url != null) { |
|
addURL(url); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static URL toFileURL(String s) { |
|
try { |
|
File f = new File(s).getCanonicalFile(); |
|
return ParseUtil.fileToEncodedURL(f); |
|
} catch (IOException e) { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public URL[] getURLs() { |
|
synchronized (unopenedUrls) { |
|
return path.toArray(new URL[0]); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public URL findResource(String name, boolean check) { |
|
Loader loader; |
|
for (int i = 0; (loader = getLoader(i)) != null; i++) { |
|
URL url = loader.findResource(name, check); |
|
if (url != null) { |
|
return url; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Resource getResource(String name, boolean check) { |
|
if (DEBUG) { |
|
System.err.println("URLClassPath.getResource(\"" + name + "\")"); |
|
} |
|
|
|
Loader loader; |
|
for (int i = 0; (loader = getLoader(i)) != null; i++) { |
|
Resource res = loader.getResource(name, check); |
|
if (res != null) { |
|
return res; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Enumeration<URL> findResources(final String name, |
|
final boolean check) { |
|
return new Enumeration<>() { |
|
private int index = 0; |
|
private URL url = null; |
|
|
|
private boolean next() { |
|
if (url != null) { |
|
return true; |
|
} else { |
|
Loader loader; |
|
while ((loader = getLoader(index++)) != null) { |
|
url = loader.findResource(name, check); |
|
if (url != null) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
public boolean hasMoreElements() { |
|
return next(); |
|
} |
|
|
|
public URL nextElement() { |
|
if (!next()) { |
|
throw new NoSuchElementException(); |
|
} |
|
URL u = url; |
|
url = null; |
|
return u; |
|
} |
|
}; |
|
} |
|
|
|
public Resource getResource(String name) { |
|
return getResource(name, true); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Enumeration<Resource> getResources(final String name, |
|
final boolean check) { |
|
return new Enumeration<>() { |
|
private int index = 0; |
|
private Resource res = null; |
|
|
|
private boolean next() { |
|
if (res != null) { |
|
return true; |
|
} else { |
|
Loader loader; |
|
while ((loader = getLoader(index++)) != null) { |
|
res = loader.getResource(name, check); |
|
if (res != null) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
} |
|
|
|
public boolean hasMoreElements() { |
|
return next(); |
|
} |
|
|
|
public Resource nextElement() { |
|
if (!next()) { |
|
throw new NoSuchElementException(); |
|
} |
|
Resource r = res; |
|
res = null; |
|
return r; |
|
} |
|
}; |
|
} |
|
|
|
public Enumeration<Resource> getResources(final String name) { |
|
return getResources(name, true); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private synchronized Loader getLoader(int index) { |
|
if (closed) { |
|
return null; |
|
} |
|
// Expand URL search path until the request can be satisfied |
|
|
|
while (loaders.size() < index + 1) { |
|
final URL url; |
|
synchronized (unopenedUrls) { |
|
url = unopenedUrls.pollFirst(); |
|
if (url == null) |
|
return null; |
|
} |
|
// Skip this URL if it already has a Loader. (Loader |
|
// may be null in the case where URL has not been opened |
|
|
|
String urlNoFragString = URLUtil.urlNoFragString(url); |
|
if (lmap.containsKey(urlNoFragString)) { |
|
continue; |
|
} |
|
|
|
Loader loader; |
|
try { |
|
loader = getLoader(url); |
|
// If the loader defines a local class path then add the |
|
|
|
URL[] urls = loader.getClassPath(); |
|
if (urls != null) { |
|
push(urls); |
|
} |
|
} catch (IOException e) { |
|
|
|
continue; |
|
} catch (SecurityException se) { |
|
// Always silently ignore. The context, if there is one, that |
|
// this URLClassPath was given during construction will never |
|
|
|
if (DEBUG) { |
|
System.err.println("Failed to access " + url + ", " + se ); |
|
} |
|
continue; |
|
} |
|
|
|
loaders.add(loader); |
|
lmap.put(urlNoFragString, loader); |
|
} |
|
return loaders.get(index); |
|
} |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private Loader getLoader(final URL url) throws IOException { |
|
try { |
|
return AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<>() { |
|
public Loader run() throws IOException { |
|
String protocol = url.getProtocol(); |
|
String file = url.getFile(); |
|
if (file != null && file.endsWith("/")) { |
|
if ("file".equals(protocol)) { |
|
return new FileLoader(url); |
|
} else if ("jar".equals(protocol) && |
|
isDefaultJarHandler(url) && |
|
file.endsWith("!/")) { |
|
|
|
URL nestedUrl = new URL(file.substring(0, file.length() - 2)); |
|
return new JarLoader(nestedUrl, jarHandler, lmap, acc); |
|
} else { |
|
return new Loader(url); |
|
} |
|
} else { |
|
return new JarLoader(url, jarHandler, lmap, acc); |
|
} |
|
} |
|
}, acc); |
|
} catch (PrivilegedActionException pae) { |
|
throw (IOException)pae.getException(); |
|
} |
|
} |
|
|
|
private static final JavaNetURLAccess JNUA |
|
= SharedSecrets.getJavaNetURLAccess(); |
|
|
|
private static boolean isDefaultJarHandler(URL u) { |
|
URLStreamHandler h = JNUA.getHandler(u); |
|
return h instanceof sun.net.www.protocol.jar.Handler; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void push(URL[] urls) { |
|
synchronized (unopenedUrls) { |
|
for (int i = urls.length - 1; i >= 0; --i) { |
|
unopenedUrls.addFirst(urls[i]); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static URL checkURL(URL url) { |
|
if (url != null) { |
|
try { |
|
check(url); |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
} |
|
return url; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static void check(URL url) throws IOException { |
|
@SuppressWarnings("removal") |
|
SecurityManager security = System.getSecurityManager(); |
|
if (security != null) { |
|
URLConnection urlConnection = url.openConnection(); |
|
Permission perm = urlConnection.getPermission(); |
|
if (perm != null) { |
|
try { |
|
security.checkPermission(perm); |
|
} catch (SecurityException se) { |
|
// fallback to checkRead/checkConnect for pre 1.2 |
|
|
|
if ((perm instanceof java.io.FilePermission) && |
|
perm.getActions().indexOf("read") != -1) { |
|
security.checkRead(perm.getName()); |
|
} else if ((perm instanceof |
|
java.net.SocketPermission) && |
|
perm.getActions().indexOf("connect") != -1) { |
|
URL locUrl = url; |
|
if (urlConnection instanceof JarURLConnection) { |
|
locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); |
|
} |
|
security.checkConnect(locUrl.getHost(), |
|
locUrl.getPort()); |
|
} else { |
|
throw se; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static class Loader implements Closeable { |
|
private final URL base; |
|
private JarFile jarfile; |
|
|
|
|
|
|
|
*/ |
|
Loader(URL url) { |
|
base = url; |
|
} |
|
|
|
|
|
|
|
*/ |
|
URL getBaseURL() { |
|
return base; |
|
} |
|
|
|
URL findResource(final String name, boolean check) { |
|
URL url; |
|
try { |
|
url = new URL(base, ParseUtil.encodePath(name, false)); |
|
} catch (MalformedURLException e) { |
|
return null; |
|
} |
|
|
|
try { |
|
if (check) { |
|
URLClassPath.check(url); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
URLConnection uc = url.openConnection(); |
|
if (uc instanceof HttpURLConnection) { |
|
HttpURLConnection hconn = (HttpURLConnection)uc; |
|
hconn.setRequestMethod("HEAD"); |
|
if (hconn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) { |
|
return null; |
|
} |
|
} else { |
|
|
|
uc.setUseCaches(false); |
|
InputStream is = uc.getInputStream(); |
|
is.close(); |
|
} |
|
return url; |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
} |
|
|
|
Resource getResource(final String name, boolean check) { |
|
final URL url; |
|
try { |
|
url = new URL(base, ParseUtil.encodePath(name, false)); |
|
} catch (MalformedURLException e) { |
|
return null; |
|
} |
|
final URLConnection uc; |
|
try { |
|
if (check) { |
|
URLClassPath.check(url); |
|
} |
|
uc = url.openConnection(); |
|
|
|
if (uc instanceof JarURLConnection) { |
|
|
|
|
|
*/ |
|
JarURLConnection juc = (JarURLConnection)uc; |
|
jarfile = JarLoader.checkJar(juc.getJarFile()); |
|
} |
|
|
|
InputStream in = uc.getInputStream(); |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
return new Resource() { |
|
public String getName() { return name; } |
|
public URL getURL() { return url; } |
|
public URL getCodeSourceURL() { return base; } |
|
public InputStream getInputStream() throws IOException { |
|
return uc.getInputStream(); |
|
} |
|
public int getContentLength() throws IOException { |
|
return uc.getContentLength(); |
|
} |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Resource getResource(final String name) { |
|
return getResource(name, true); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void close() throws IOException { |
|
if (jarfile != null) { |
|
jarfile.close(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
URL[] getClassPath() throws IOException { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static class JarLoader extends Loader { |
|
private JarFile jar; |
|
private final URL csu; |
|
private JarIndex index; |
|
private URLStreamHandler handler; |
|
private final HashMap<String, Loader> lmap; |
|
@SuppressWarnings("removal") |
|
private final AccessControlContext acc; |
|
private boolean closed = false; |
|
private static final JavaUtilZipFileAccess zipAccess = |
|
SharedSecrets.getJavaUtilZipFileAccess(); |
|
|
|
|
|
|
|
|
|
*/ |
|
private JarLoader(URL url, URLStreamHandler jarHandler, |
|
HashMap<String, Loader> loaderMap, |
|
@SuppressWarnings("removal") AccessControlContext acc) |
|
throws IOException |
|
{ |
|
super(new URL("jar", "", -1, url + "!/", jarHandler)); |
|
csu = url; |
|
handler = jarHandler; |
|
lmap = loaderMap; |
|
this.acc = acc; |
|
|
|
ensureOpen(); |
|
} |
|
|
|
@Override |
|
public void close () throws IOException { |
|
|
|
if (!closed) { |
|
closed = true; |
|
|
|
ensureOpen(); |
|
jar.close(); |
|
} |
|
} |
|
|
|
JarFile getJarFile () { |
|
return jar; |
|
} |
|
|
|
private boolean isOptimizable(URL url) { |
|
return "file".equals(url.getProtocol()); |
|
} |
|
|
|
@SuppressWarnings("removal") |
|
private void ensureOpen() throws IOException { |
|
if (jar == null) { |
|
try { |
|
AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<>() { |
|
public Void run() throws IOException { |
|
if (DEBUG) { |
|
System.err.println("Opening " + csu); |
|
Thread.dumpStack(); |
|
} |
|
|
|
jar = getJarFile(csu); |
|
index = JarIndex.getJarIndex(jar); |
|
if (index != null) { |
|
String[] jarfiles = index.getJarFiles(); |
|
// Add all the dependent URLs to the lmap so that loaders |
|
// will not be created for them by URLClassPath.getLoader(int) |
|
// if the same URL occurs later on the main class path. We set |
|
// Loader to null here to avoid creating a Loader for each |
|
|
|
for (int i = 0; i < jarfiles.length; i++) { |
|
try { |
|
URL jarURL = new URL(csu, jarfiles[i]); |
|
|
|
String urlNoFragString = URLUtil.urlNoFragString(jarURL); |
|
if (!lmap.containsKey(urlNoFragString)) { |
|
lmap.put(urlNoFragString, null); |
|
} |
|
} catch (MalformedURLException e) { |
|
continue; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
}, acc); |
|
} catch (PrivilegedActionException pae) { |
|
throw (IOException)pae.getException(); |
|
} |
|
} |
|
} |
|
|
|
|
|
@SuppressWarnings("removal") |
|
static JarFile checkJar(JarFile jar) throws IOException { |
|
if (System.getSecurityManager() != null && !DISABLE_JAR_CHECKING |
|
&& !zipAccess.startsWithLocHeader(jar)) { |
|
IOException x = new IOException("Invalid Jar file"); |
|
try { |
|
jar.close(); |
|
} catch (IOException ex) { |
|
x.addSuppressed(ex); |
|
} |
|
throw x; |
|
} |
|
|
|
return jar; |
|
} |
|
|
|
private JarFile getJarFile(URL url) throws IOException { |
|
|
|
if (isOptimizable(url)) { |
|
FileURLMapper p = new FileURLMapper(url); |
|
if (!p.exists()) { |
|
throw new FileNotFoundException(p.getPath()); |
|
} |
|
return checkJar(new JarFile(new File(p.getPath()), true, ZipFile.OPEN_READ, |
|
JarFile.runtimeVersion())); |
|
} |
|
URLConnection uc = (new URL(getBaseURL(), "#runtime")).openConnection(); |
|
uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION); |
|
JarFile jarFile = ((JarURLConnection)uc).getJarFile(); |
|
return checkJar(jarFile); |
|
} |
|
|
|
|
|
|
|
*/ |
|
JarIndex getIndex() { |
|
try { |
|
ensureOpen(); |
|
} catch (IOException e) { |
|
throw new InternalError(e); |
|
} |
|
return index; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
Resource checkResource(final String name, boolean check, |
|
final JarEntry entry) { |
|
|
|
final URL url; |
|
try { |
|
String nm; |
|
if (jar.isMultiRelease()) { |
|
nm = entry.getRealName(); |
|
} else { |
|
nm = name; |
|
} |
|
url = new URL(getBaseURL(), ParseUtil.encodePath(nm, false)); |
|
if (check) { |
|
URLClassPath.check(url); |
|
} |
|
} catch (MalformedURLException e) { |
|
return null; |
|
// throw new IllegalArgumentException("name"); |
|
} catch (IOException e) { |
|
return null; |
|
} catch (@SuppressWarnings("removal") AccessControlException e) { |
|
return null; |
|
} |
|
|
|
return new Resource() { |
|
public String getName() { return name; } |
|
public URL getURL() { return url; } |
|
public URL getCodeSourceURL() { return csu; } |
|
public InputStream getInputStream() throws IOException |
|
{ return jar.getInputStream(entry); } |
|
public int getContentLength() |
|
{ return (int)entry.getSize(); } |
|
public Manifest getManifest() throws IOException { |
|
SharedSecrets.javaUtilJarAccess().ensureInitialization(jar); |
|
return jar.getManifest(); |
|
} |
|
public Certificate[] getCertificates() |
|
{ return entry.getCertificates(); }; |
|
public CodeSigner[] getCodeSigners() |
|
{ return entry.getCodeSigners(); }; |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
boolean validIndex(final String name) { |
|
String packageName = name; |
|
int pos; |
|
if ((pos = name.lastIndexOf('/')) != -1) { |
|
packageName = name.substring(0, pos); |
|
} |
|
|
|
String entryName; |
|
ZipEntry entry; |
|
Enumeration<JarEntry> enum_ = jar.entries(); |
|
while (enum_.hasMoreElements()) { |
|
entry = enum_.nextElement(); |
|
entryName = entry.getName(); |
|
if ((pos = entryName.lastIndexOf('/')) != -1) |
|
entryName = entryName.substring(0, pos); |
|
if (entryName.equals(packageName)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
URL findResource(final String name, boolean check) { |
|
Resource rsc = getResource(name, check); |
|
if (rsc != null) { |
|
return rsc.getURL(); |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
Resource getResource(final String name, boolean check) { |
|
try { |
|
ensureOpen(); |
|
} catch (IOException e) { |
|
throw new InternalError(e); |
|
} |
|
final JarEntry entry = jar.getJarEntry(name); |
|
if (entry != null) |
|
return checkResource(name, check, entry); |
|
|
|
if (index == null) |
|
return null; |
|
|
|
HashSet<String> visited = new HashSet<>(); |
|
return getResource(name, check, visited); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
Resource getResource(final String name, boolean check, |
|
Set<String> visited) { |
|
Resource res; |
|
String[] jarFiles; |
|
int count = 0; |
|
LinkedList<String> jarFilesList = null; |
|
|
|
|
|
|
|
*/ |
|
if ((jarFilesList = index.get(name)) == null) |
|
return null; |
|
|
|
do { |
|
int size = jarFilesList.size(); |
|
jarFiles = jarFilesList.toArray(new String[size]); |
|
|
|
while (count < size) { |
|
String jarName = jarFiles[count++]; |
|
JarLoader newLoader; |
|
final URL url; |
|
|
|
try{ |
|
url = new URL(csu, jarName); |
|
String urlNoFragString = URLUtil.urlNoFragString(url); |
|
if ((newLoader = (JarLoader)lmap.get(urlNoFragString)) == null) { |
|
|
|
|
|
*/ |
|
newLoader = AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<>() { |
|
public JarLoader run() throws IOException { |
|
return new JarLoader(url, handler, |
|
lmap, acc); |
|
} |
|
}, acc); |
|
|
|
|
|
|
|
|
|
*/ |
|
JarIndex newIndex = newLoader.getIndex(); |
|
if (newIndex != null) { |
|
int pos = jarName.lastIndexOf('/'); |
|
newIndex.merge(this.index, (pos == -1 ? |
|
null : jarName.substring(0, pos + 1))); |
|
} |
|
|
|
|
|
lmap.put(urlNoFragString, newLoader); |
|
} |
|
} catch (PrivilegedActionException pae) { |
|
continue; |
|
} catch (MalformedURLException e) { |
|
continue; |
|
} |
|
|
|
|
|
|
|
*/ |
|
boolean visitedURL = !visited.add(URLUtil.urlNoFragString(url)); |
|
if (!visitedURL) { |
|
try { |
|
newLoader.ensureOpen(); |
|
} catch (IOException e) { |
|
throw new InternalError(e); |
|
} |
|
final JarEntry entry = newLoader.jar.getJarEntry(name); |
|
if (entry != null) { |
|
return newLoader.checkResource(name, check, entry); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
if (!newLoader.validIndex(name)) { |
|
|
|
throw new InvalidJarIndexError("Invalid index"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (visitedURL || newLoader == this || |
|
newLoader.getIndex() == null) { |
|
continue; |
|
} |
|
|
|
|
|
*/ |
|
if ((res = newLoader.getResource(name, check, visited)) |
|
!= null) { |
|
return res; |
|
} |
|
} |
|
// Get the list of jar files again as the list could have grown |
|
|
|
jarFilesList = index.get(name); |
|
|
|
// If the count is unchanged, we are done. |
|
} while (count < jarFilesList.size()); |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
URL[] getClassPath() throws IOException { |
|
if (index != null) { |
|
return null; |
|
} |
|
|
|
ensureOpen(); |
|
|
|
|
|
if (SharedSecrets.javaUtilJarAccess().jarFileHasClassPathAttribute(jar)) { |
|
Manifest man = jar.getManifest(); |
|
if (man != null) { |
|
Attributes attr = man.getMainAttributes(); |
|
if (attr != null) { |
|
String value = attr.getValue(Name.CLASS_PATH); |
|
if (value != null) { |
|
return parseClassPath(csu, value); |
|
} |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static URL[] parseClassPath(URL base, String value) |
|
throws MalformedURLException |
|
{ |
|
StringTokenizer st = new StringTokenizer(value); |
|
URL[] urls = new URL[st.countTokens()]; |
|
int i = 0; |
|
while (st.hasMoreTokens()) { |
|
String path = st.nextToken(); |
|
URL url = DISABLE_CP_URL_CHECK ? new URL(base, path) : tryResolve(base, path); |
|
if (url != null) { |
|
urls[i] = url; |
|
i++; |
|
} else { |
|
if (DEBUG_CP_URL_CHECK) { |
|
System.err.println("Class-Path entry: \"" + path |
|
+ "\" ignored in JAR file " + base); |
|
} |
|
} |
|
} |
|
if (i == 0) { |
|
urls = null; |
|
} else if (i != urls.length) { |
|
|
|
urls = Arrays.copyOf(urls, i); |
|
} |
|
return urls; |
|
} |
|
|
|
static URL tryResolve(URL base, String input) throws MalformedURLException { |
|
if ("file".equalsIgnoreCase(base.getProtocol())) { |
|
return tryResolveFile(base, input); |
|
} else { |
|
return tryResolveNonFile(base, input); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static URL tryResolveFile(URL base, String input) throws MalformedURLException { |
|
URL retVal = new URL(base, input); |
|
if (input.indexOf(':') >= 0 && |
|
!"file".equalsIgnoreCase(retVal.getProtocol())) { |
|
// 'input' contains a ':', which might be a scheme, or might be |
|
// a Windows drive letter. If the protocol for the resolved URL |
|
|
|
return null; |
|
} |
|
return retVal; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static URL tryResolveNonFile(URL base, String input) throws MalformedURLException { |
|
String child = input.replace(File.separatorChar, '/'); |
|
if (isRelative(child)) { |
|
URL url = new URL(base, child); |
|
String bp = base.getPath(); |
|
String urlp = url.getPath(); |
|
int pos = bp.lastIndexOf('/'); |
|
if (pos == -1) { |
|
pos = bp.length() - 1; |
|
} |
|
if (urlp.regionMatches(0, bp, 0, pos + 1) |
|
&& urlp.indexOf("..", pos) == -1) { |
|
return url; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
static boolean isRelative(String child) { |
|
try { |
|
return !URI.create(child).isAbsolute(); |
|
} catch (IllegalArgumentException e) { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static class FileLoader extends Loader { |
|
|
|
private File dir; |
|
|
|
|
|
|
|
*/ |
|
private FileLoader(URL url) throws IOException { |
|
super(url); |
|
String path = url.getFile().replace('/', File.separatorChar); |
|
path = ParseUtil.decode(path); |
|
dir = (new File(path)).getCanonicalFile(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
URL findResource(final String name, boolean check) { |
|
Resource rsc = getResource(name, check); |
|
if (rsc != null) { |
|
return rsc.getURL(); |
|
} |
|
return null; |
|
} |
|
|
|
@Override |
|
Resource getResource(final String name, boolean check) { |
|
final URL url; |
|
try { |
|
URL normalizedBase = new URL(getBaseURL(), "."); |
|
url = new URL(getBaseURL(), ParseUtil.encodePath(name, false)); |
|
|
|
if (url.getFile().startsWith(normalizedBase.getFile()) == false) { |
|
|
|
return null; |
|
} |
|
|
|
if (check) |
|
URLClassPath.check(url); |
|
|
|
final File file; |
|
if (name.indexOf("..") != -1) { |
|
file = (new File(dir, name.replace('/', File.separatorChar))) |
|
.getCanonicalFile(); |
|
if ( !((file.getPath()).startsWith(dir.getPath())) ) { |
|
|
|
return null; |
|
} |
|
} else { |
|
file = new File(dir, name.replace('/', File.separatorChar)); |
|
} |
|
|
|
if (file.exists()) { |
|
return new Resource() { |
|
public String getName() { return name; }; |
|
public URL getURL() { return url; }; |
|
public URL getCodeSourceURL() { return getBaseURL(); }; |
|
public InputStream getInputStream() throws IOException |
|
{ return new FileInputStream(file); }; |
|
public int getContentLength() throws IOException |
|
{ return (int)file.length(); }; |
|
}; |
|
} |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
return null; |
|
} |
|
} |
|
} |