jarList = index.get(name.replace('.', '/'));
if (jarList != null) {
for (String jarName : jarList) {
JARDesc desc;
try {
desc = new JARDesc(new URL(file.getCodeBase(), jarName),
null, null, false, true, false, true);
} catch (MalformedURLException mfe) {
throw new ClassNotFoundException(name);
}
try {
addNewJar(desc);
} catch (Exception e) {
if (JNLPRuntime.isDebug()) {
e.printStackTrace();
}
}
}
// If it still fails, let it error out
result = loadClassExt(name);
}
}
}
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
/**
* Adds a new JARDesc into this classloader.
*
* This will add the JARDesc into the resourceTracker and block until it
* is downloaded.
* @param desc the JARDesc for the new jar
*/
private void addNewJar(final JARDesc desc) {
available.add(desc);
tracker.addResource(desc.getLocation(),
desc.getVersion(),
null,
JNLPRuntime.getDefaultUpdatePolicy()
);
// Give read permissions to the cached jar file
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
Permission p = CacheUtil.getReadPermission(desc.getLocation(),
desc.getVersion());
resourcePermissions.add(p);
return null;
}
});
final URL remoteURL = desc.getLocation();
final URL cachedUrl = tracker.getCacheURL(remoteURL); // blocks till download
available.remove(desc); // Resource downloaded. Remove from available list.
try {
// Verify if needed
final JarSigner signer = new JarSigner();
final List jars = new ArrayList();
jars.add(desc);
// Decide what level of security this jar should have
// The verification and security setting functions rely on
// having AllPermissions as those actions normally happen
// during initialization. We therefore need to do those
// actions as privileged.
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Void run() throws Exception {
signer.verifyJars(jars, tracker);
if (signer.anyJarsSigned() && !signer.getAlreadyTrustPublisher()) {
checkTrustWithUser(signer);
}
final SecurityDesc security;
if (signer.anyJarsSigned()) {
security = new SecurityDesc(file,
SecurityDesc.ALL_PERMISSIONS,
file.getCodeBase().getHost());
} else {
security = new SecurityDesc(file,
SecurityDesc.SANDBOX_PERMISSIONS,
file.getCodeBase().getHost());
}
jarLocationSecurityMap.put(remoteURL, security);
return null;
}
});
addURL(remoteURL);
CachedJarFileCallback.getInstance().addMapping(remoteURL, cachedUrl);
} catch (Exception e) {
// Do nothing. This code is called by loadClass which cannot
// throw additional exceptions. So instead, just ignore it.
// Exception => jar will not get added to classpath, which will
// result in CNFE from loadClass.
e.printStackTrace();
}
}
/**
* Find the class in this loader or any of its extension loaders.
*/
protected Class findClass(String name) throws ClassNotFoundException {
for (int i = 0; i < loaders.length; i++) {
try {
if (loaders[i] == this)
return super.findClass(name);
else
return loaders[i].findClass(name);
} catch (ClassNotFoundException ex) {
} catch (ClassFormatError cfe) {
}
}
// Try codebase loader
if (codeBaseLoader != null)
return codeBaseLoader.findClass(name);
// All else failed. Throw CNFE
throw new ClassNotFoundException(name);
}
/**
* Search for the class by incrementally adding resources to the
* classloader and its extension classloaders until the resource
* is found.
*/
private Class loadClassExt(String name) throws ClassNotFoundException {
// make recursive
addAvailable();
// find it
try {
return findClass(name);
} catch (ClassNotFoundException ex) {
}
// add resources until found
while (true) {
JNLPClassLoader addedTo = null;
try {
addedTo = addNextResource();
} catch (LaunchException e) {
/*
* This method will never handle any search for the main class
* [It is handled in initializeResources()]. Therefore, this
* exception will never be thrown here and is escaped
*/
throw new IllegalStateException(e);
}
if (addedTo == null)
throw new ClassNotFoundException(name);
try {
return addedTo.findClass(name);
} catch (ClassNotFoundException ex) {
}
}
}
/**
* Finds the resource in this, the parent, or the extension
* class loaders.
*/
public URL getResource(String name) {
URL result = super.getResource(name);
for (int i = 1; i < loaders.length; i++)
if (result == null)
result = loaders[i].getResource(name);
// If result is still null, look in the codebase loader
if (result == null && codeBaseLoader != null)
result = codeBaseLoader.getResource(name);
return result;
}
/**
* Finds the resource in this, the parent, or the extension
* class loaders.
*/
@Override
public Enumeration findResources(String name) throws IOException {
Vector resources = new Vector();
Enumeration e;
for (int i = 0; i < loaders.length; i++) {
if (loaders[i] == this)
e = super.findResources(name);
else
e = loaders[i].findResources(name);
while (e.hasMoreElements())
resources.add(e.nextElement());
}
// Add resources from codebase (only if nothing was found above,
// otherwise the server will get hammered)
if (resources.isEmpty() && codeBaseLoader != null) {
e = codeBaseLoader.findResources(name);
while (e.hasMoreElements())
resources.add(e.nextElement());
}
return resources.elements();
}
/**
* Returns if the specified resource is available locally from a cached jar
*
* @param s The name of the resource
* @return Whether or not the resource is available locally
*/
public boolean resourceAvailableLocally(String s) {
return jarEntries.contains(s);
}
/**
* Adds whatever resources have already been downloaded in the
* background.
*/
protected void addAvailable() {
// go through available, check tracker for it and all of its
// part brothers being available immediately, add them.
for (int i = 1; i < loaders.length; i++) {
loaders[i].addAvailable();
}
}
/**
* Adds the next unused resource to the classloader. That
* resource and all those in the same part will be downloaded
* and added to the classloader before returning. If there are
* no more resources to add, the method returns immediately.
*
* @return the classloader that resources were added to, or null
* @throws LaunchException Thrown if the signed JNLP file, within the main jar, fails to be verified or does not match
*/
protected JNLPClassLoader addNextResource() throws LaunchException {
if (available.size() == 0) {
for (int i = 1; i < loaders.length; i++) {
JNLPClassLoader result = loaders[i].addNextResource();
if (result != null)
return result;
}
return null;
}
// add jar
List jars = new ArrayList();
jars.add(available.get(0));
fillInPartJars(jars);
checkForMain(jars);
activateJars(jars);
return this;
}
// this part compatibility with previous classloader
/**
* @deprecated
*/
@Deprecated
public String getExtensionName() {
String result = file.getInformation().getTitle();
if (result == null)
result = file.getInformation().getDescription();
if (result == null && file.getFileLocation() != null)
result = file.getFileLocation().toString();
if (result == null && file.getCodeBase() != null)
result = file.getCodeBase().toString();
return result;
}
/**
* @deprecated
*/
@Deprecated
public String getExtensionHREF() {
return file.getFileLocation().toString();
}
public boolean getSigning() {
return signing;
}
protected SecurityDesc getSecurity() {
return security;
}
/**
* Returns the security descriptor for given code source URL
*
* @param source the origin (remote) url of the code
* @return The SecurityDescriptor for that source
*/
protected SecurityDesc getCodeSourceSecurity(URL source) {
return jarLocationSecurityMap.get(source);
}
/**
* Merges the code source/security descriptor mapping from another loader
*
* @param extLoader The loader form which to merge
* @throws SecurityException if the code is called from an untrusted source
*/
private void merge(JNLPClassLoader extLoader) {
try {
System.getSecurityManager().checkPermission(new AllPermission());
} catch (SecurityException se) {
throw new SecurityException("JNLPClassLoader() may only be called from trusted sources!");
}
// jars
for (URL u : extLoader.getURLs())
addURL(u);
// Codebase
addToCodeBaseLoader(extLoader.file.getCodeBase());
// native search paths
for (File nativeDirectory : extLoader.getNativeDirectories())
addNativeDirectory(nativeDirectory);
// security descriptors
for (URL key : extLoader.jarLocationSecurityMap.keySet()) {
jarLocationSecurityMap.put(key, extLoader.jarLocationSecurityMap.get(key));
}
}
/**
* Adds the given path to the path loader
*
* @param URL the path to add
* @throws IllegalArgumentException If the given url is not a path
*/
private void addToCodeBaseLoader(URL u) {
// Only paths may be added
if (!u.getFile().endsWith("/")) {
throw new IllegalArgumentException("addToPathLoader only accepts path based URLs");
}
// If there is no loader yet, create one, else add it to the
// existing one (happens when called from merge())
if (codeBaseLoader == null) {
codeBaseLoader = new CodeBaseClassLoader(new URL[] { u }, this);
} else {
codeBaseLoader.addURL(u);
}
}
private DownloadOptions getDownloadOptionsForJar(JARDesc jar) {
return file.getDownloadOptionsForJar(jar);
}
/**
* Returns a set of paths that indicate the Class-Path entries in the
* manifest file. The paths are rooted in the same directory as the
* originalJarPath.
* @param mf the manifest
* @param originalJarPath the remote/original path of the jar containing
* the manifest
* @return a Set of String where each string is a path to the jar on
* the original jar's classpath.
*/
private Set getClassPathsFromManifest(Manifest mf, String originalJarPath) {
Set result = new HashSet();
if (mf != null) {
// extract the Class-Path entries from the manifest and split them
String classpath = mf.getMainAttributes().getValue("Class-Path");
if (classpath == null || classpath.trim().length() == 0) {
return result;
}
String[] paths = classpath.split(" +");
for (String path : paths) {
if (path.trim().length() == 0) {
continue;
}
// we want to search for jars in the same subdir on the server
// as the original jar that contains the manifest file, so find
// out its subdirectory and use that as the dir
String dir = "";
int lastSlash = originalJarPath.lastIndexOf("/");
if (lastSlash != -1) {
dir = originalJarPath.substring(0, lastSlash + 1);
}
String fullPath = dir + path;
result.add(fullPath);
}
}
return result;
}
/*
* Helper class to expose protected URLClassLoader methods.
*/
public static class CodeBaseClassLoader extends URLClassLoader {
JNLPClassLoader parentJNLPClassLoader;
public CodeBaseClassLoader(URL[] urls, JNLPClassLoader cl) {
super(urls);
parentJNLPClassLoader = cl;
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
@Override
public Class> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
/**
* Returns the output of super.findLoadedClass().
*
* The method is renamed because ClassLoader.findLoadedClass() is final
*
* @param name The name of the class to find
* @return Output of ClassLoader.findLoadedClass() which is the class if found, null otherwise
* @see java.lang.ClassLoader#findLoadedClass(String)
*/
public Class> findLoadedClassFromParent(String name) {
return findLoadedClass(name);
}
/**
* Returns JNLPClassLoader that encompasses this loader
*
* @return parent JNLPClassLoader
*/
public JNLPClassLoader getParentJNLPClassLoader() {
return parentJNLPClassLoader;
}
@Override
public Enumeration findResources(String name) throws IOException {
if (!name.startsWith("META-INF")) {
return super.findResources(name);
}
return (new Vector(0)).elements();
}
@Override
public URL findResource(String name) {
if (!name.startsWith("META-INF")) {
return super.findResource(name);
}
return null;
}
}
}