[icedtea-web] RFC: Patch to support plugin classloader sharing
Deepak Bhole
dbhole at redhat.com
Wed Mar 2 14:17:35 PST 2011
This patch allows plugins from the same page to use the same
classloader.
There are known applets (Microsoft Live Meeting, *.map24.com) which rely
on this behaviour as they try to access static variables from one applet
through another.
ChangeLog:
2011-03-02 Deepak Bhole <dbhole at redhat.com>
Fix RH604061: Microsoft Live Meeting doesn't work
Fix PR475: (uk.map24.com) Applet doesn't stop loading
* configure.ac: Added checks for sun.misc.JarIndex and
sun.misc.URLClassPath
* netx/net/sourceforge/jnlp/PluginBridge.java
(PluginBridge): Set unique key based on document base (i.e. plugin origin
page).
* netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
(getInstance): When trying to compare against file.getFileLocation(),
ensure that it is a JNLP application. If it is a plugin and baseloader is
not null, marge the loaders.
(merge): Use the new addFileURLsFirst function.
(addFileURLsFirst): New function. Adds a url to the search path and moves
it to the top if it is a file (jar/zip/etc.) path.
* NEWS: Updated.
This should go in 1.1 only for now.
Cheers,
Deepak
-------------- next part --------------
diff -r 5dbf0f9de599 NEWS
--- a/NEWS Wed Mar 02 11:50:30 2011 -0500
+++ b/NEWS Wed Mar 02 17:16:03 2011 -0500
@@ -21,7 +21,9 @@
- Use Firefox's proxy settings if possible
- RH669942: javaws fails to download version/packed files (missing support for jnlp.packEnabled and jnlp.versionEnabled)
* Plugin
+ - PR475: (uk.map24.com) Applet doesn't stop loading
- PR612: NetDania application ends on java.security.AccessControlException: access denied (java.util.PropertyPermission browser read)
+ - RH604061: Microsoft Live Meeting doesn't work
New in release 1.0 (2010-XX-XX):
diff -r 5dbf0f9de599 configure.ac
--- a/configure.ac Wed Mar 02 11:50:30 2011 -0500
+++ b/configure.ac Wed Mar 02 17:16:03 2011 -0500
@@ -60,6 +60,7 @@
dnl IT575 - Plugin depends on com.sun/jndi.toolkit.url.UrlUtil
dnl IT576 - Plugin depends on sun.applet.AppletImageRef
dnl IT578 - Remove need for patching AppletPanel for Plugin/Webstart
+dnl IT657 - IcedTea-Web depends on sun.misc.JarIndex
IT_CHECK_FOR_CLASS(JAVA_UTIL_JAR_PACK200, [java.util.jar.Pack200])
IT_CHECK_FOR_CLASS(JAVA_NET_COOKIEMANAGER, [java.net.CookieManager])
IT_CHECK_FOR_CLASS(JAVA_NET_HTTPCOOKIE, [java.net.HttpCookie])
@@ -70,6 +71,8 @@
IT_CHECK_FOR_CLASS(SUN_SECURITY_X509_X500NAME, [sun.security.x509.X500Name])
IT_CHECK_FOR_CLASS(SUN_MISC_BASE64ENCODER, [sun.misc.BASE64Encoder])
IT_CHECK_FOR_CLASS(SUN_MISC_HEXDUMPENCODER, [sun.misc.HexDumpEncoder])
+IT_CHECK_FOR_CLASS(SUN_MISC_JARINDEX, [sun.misc.JarIndex])
+IT_CHECK_FOR_CLASS(SUN_MISC_URLCLASSPATH, [sun.misc.URLClassPath])
IT_CHECK_FOR_CLASS(SUN_SECURITY_VALIDATOR_VALIDATOREXCEPTION, [sun.security.validator.ValidatorException])
IT_CHECK_FOR_CLASS(COM_SUN_NET_SSL_INTERNAL_SSL_X509EXTENDEDTRUSTMANAGER,
[com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager])
diff -r 5dbf0f9de599 netx/net/sourceforge/jnlp/PluginBridge.java
--- a/netx/net/sourceforge/jnlp/PluginBridge.java Wed Mar 02 11:50:30 2011 -0500
+++ b/netx/net/sourceforge/jnlp/PluginBridge.java Wed Mar 02 17:16:03 2011 -0500
@@ -130,9 +130,10 @@
else
security = null;
- this.uniqueKey = Calendar.getInstance().getTimeInMillis() + "-" +
- Math.abs(((new java.util.Random()).nextInt())) + "-" +
- documentBase;
+ // Plugin needs to share classloaders so that applet instances from
+ // same page can communicate (there are applets known to require
+ // such communication for proper functionality)
+ this.uniqueKey = documentBase.toString();
}
public String getTitle() {
diff -r 5dbf0f9de599 netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Wed Mar 02 11:50:30 2011 -0500
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Wed Mar 02 17:16:03 2011 -0500
@@ -21,6 +21,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -40,6 +42,7 @@
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.Stack;
import java.util.TreeSet;
import java.util.Vector;
import java.util.jar.JarEntry;
@@ -63,6 +66,7 @@
import net.sourceforge.jnlp.tools.JarSigner;
import net.sourceforge.jnlp.util.FileUtils;
import sun.misc.JarIndex;
+import sun.misc.URLClassPath;
/**
* Classloader that takes it's resources from a JNLP file. If the
@@ -276,10 +280,12 @@
try {
- // If base loader is null, or the baseloader's file and this
- // file is different, initialize a new loader
+
+ // A null baseloader implies that no loader has been created
+ // for this codebase/jnlp yet. Create one.
if (baseLoader == null ||
- !baseLoader.getJNLPFile().getFileLocation().equals(file.getFileLocation())) {
+ (file.isApplication() &&
+ !baseLoader.getJNLPFile().getFileLocation().equals(file.getFileLocation()))) {
loader = new JNLPClassLoader(file, policy);
@@ -303,6 +309,13 @@
} else {
// if key is same and locations match, this is the loader we want
+ if (!file.isApplication()) {
+ // If this is an applet, we do need to consider its loader
+ loader = new JNLPClassLoader(file, policy);
+
+ if (baseLoader != null)
+ baseLoader.merge(loader);
+ }
loader = baseLoader;
}
@@ -1249,7 +1262,7 @@
// jars
for (URL u : extLoader.getURLs())
- addURL(u);
+ addFileURLsFirst(u);
// native search paths
for (File nativeDirectory : extLoader.getNativeDirectories())
@@ -1283,4 +1296,149 @@
return new DownloadOptions(usePack, useVersion);
}
+ /**
+ * Adds file urls to the front of the URL classpath
+ *
+ * Basically, the way url classloader works is as follows:
+ * 1. url is added via addURL()
+ * 2. This url is stored in a path list, and added to a url stack
+ * 3. When a find request comes in, a loader list is searched
+ * a. If resource is found, it is returned
+ * b. If not found, the url stack is popped and a new loader
+ * loader is created for each url, which is then searched
+ *
+ * Since the loader list exists throughout the lifetime of the
+ * URLClassLoader, to ensure that a url is searched first, this
+ * list needs to be modified.
+ *
+ * In the interest of re-using as much of URLClassPath code as possible
+ * (especially loader creation), we add the url, force the loader to
+ * be created actively, and then go through the loader list and move
+ * non jar paths to the bottom
+ *
+ * @param u The URL to add
+ */
+ private void addFileURLsFirst(URL u) {
+
+ Field ucpField = null;
+ Field pathField = null;
+ Field urlsField = null;
+ Field loadersField = null;
+ Method getLoaderMethod = null;
+ Method loaderClassGetURLMethod = null;
+
+ try {
+
+ // Get the fields
+ ucpField = URLClassLoader.class.getDeclaredField("ucp");
+ pathField = URLClassPath.class.getDeclaredField("path");
+ urlsField = URLClassPath.class.getDeclaredField("urls");
+ loadersField = URLClassPath.class.getDeclaredField("loaders");
+ getLoaderMethod = URLClassPath.class.getDeclaredMethod("getLoader", int.class);
+
+ // Access the instances
+
+ // The URLClassPath object
+ ucpField.setAccessible(true);
+ URLClassPath ucp = (URLClassPath) ucpField.get(this);
+ ucpField.setAccessible(false);
+
+ // The Path field used to track which urls have been seen thus far
+ pathField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ ArrayList<URL> path = (ArrayList<URL>) pathField.get(ucp);
+ pathField.setAccessible(false);
+
+ // The URL stack which contains unprocessed urls
+ urlsField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Stack<URL> urls = (Stack<URL>) urlsField.get(ucp);
+ urlsField.setAccessible(false);
+
+ // The loaders created from processed urls
+ loadersField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ ArrayList<Object> loaders = (ArrayList<Object>) loadersField.get(ucp);
+ loadersField.setAccessible(false);
+
+ synchronized (loaders) {
+
+ if (path.contains(u))
+ return;
+
+ urls.add(0, u);
+ path.add(0, u);
+
+ // Force creation of all loaders
+ getLoaderMethod.setAccessible(true);
+ getLoaderMethod.invoke(ucp, loaders.size());
+ getLoaderMethod.setAccessible(false);
+
+ loadersField.setAccessible(true);
+ loaders = (ArrayList<Object>) loadersField.get(ucp);
+ loadersField.setAccessible(false);
+
+ // Create temp lists that hold file loaders and path loaders
+
+ // Since the type is an inner class,
+ // we cannot set it during initialization
+ ArrayList<Object> fileLoaderList = new ArrayList<Object>();
+ ArrayList<Object> pathLoaderList = new ArrayList<Object>();
+
+ Class<?>[] urlCPClasses = URLClassPath.class.getDeclaredClasses();
+
+ for (Class<?> c: urlCPClasses) {
+ if (c.getName().equals("sun.misc.URLClassPath$Loader")) {
+
+ for (Method m: c.getDeclaredMethods())
+ if (m.getName().equals("getBaseURL"))
+ loaderClassGetURLMethod = m;
+ }
+ }
+
+ // Figure out what is a file loader and what is a path loader
+ for (Object l: loaders) {
+ loaderClassGetURLMethod.setAccessible(true);
+ String url = (String) loaderClassGetURLMethod.invoke(l).toString();
+ loaderClassGetURLMethod.setAccessible(false);
+
+ if (url.endsWith("!/"))
+ fileLoaderList.add(l);
+ else
+ pathLoaderList.add(l);
+
+ }
+
+ // Add file loaders first, then path loaders
+ loaders.clear();
+ loaders.addAll(fileLoaderList);
+ loaders.addAll(pathLoaderList);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // Make sure all access restrictions are re-enabled, in case
+ // something went wrong above
+
+ if (ucpField != null)
+ ucpField.setAccessible(false);
+
+ if (pathField != null)
+ pathField.setAccessible(false);
+
+ if (urlsField != null)
+ urlsField.setAccessible(false);
+
+ if (loadersField != null)
+ loadersField.setAccessible(false);
+
+ if (getLoaderMethod != null)
+ getLoaderMethod.setAccessible(false);
+
+ if (loaderClassGetURLMethod != null)
+ loaderClassGetURLMethod.setAccessible(false);
+
+ }
}
More information about the distro-pkg-dev
mailing list