# HG changeset patch # User Denis Lila # Date 1304110685 14400 # Node ID 77640d74d21c2303b64bbc05583a3850db564894 # Parent f0647c9385358b4fc3fe747ba7b95b9117c2f681 Fix appcontext related plugin bugs. diff -r f0647c938535 -r 77640d74d21c ChangeLog --- a/ChangeLog Sat Jan 07 02:48:05 2012 -0500 +++ b/ChangeLog Fri Apr 29 16:58:05 2011 -0400 @@ -1,3 +1,27 @@ +2011-04-28 Denis Lila + + * netx/net/sourceforge/jnlp/NetxPanel.java: + Remove unused import; add imports. + (uKey, uKeyToTG, appContextCreated): New members. + (getThreadGroup, createNewAppContext): New methods. + (runLoader): Pass uKey to PluginBridge's constructor. + (run): Remove. No longer needed. + (NetxPanel): Initialize uKey. If it is a new key, make a new thread + group for it and save it in the hash map. + (createAppletThread): Use getFutureTG instead of creating a thread + group on the spot. + * plugin/icedteanp/java/sun/applet/PluginAppletViewer.java: + (createPanel): Initialize and frame the panel in a separate thread. + * netx/net/sourceforge/jnlp/Launcher.java: + Remove unused import. + (createApplet, createApplication, createThreadGroup): Replace + AppThreadGroup with ThreadGroup. Remove all calls to setApplication. + * netx/net/sourceforge/jnlp/PluginBridge.java: + (PluginBridge): Remove the uniqueKey initialization logic. Set + uniqueKey to the uKey parameter. + * netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java: + Remove file. + 2012-01-07 Omair Majid * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java diff -r f0647c938535 -r 77640d74d21c netx/net/sourceforge/jnlp/Launcher.java --- a/netx/net/sourceforge/jnlp/Launcher.java Sat Jan 07 02:48:05 2012 -0500 +++ b/netx/net/sourceforge/jnlp/Launcher.java Fri Apr 29 16:58:05 2011 -0400 @@ -33,7 +33,6 @@ import net.sourceforge.jnlp.cache.CacheUtil; import net.sourceforge.jnlp.cache.UpdatePolicy; -import net.sourceforge.jnlp.runtime.AppThreadGroup; import net.sourceforge.jnlp.runtime.AppletInstance; import net.sourceforge.jnlp.runtime.ApplicationInstance; import net.sourceforge.jnlp.runtime.JNLPClassLoader; @@ -707,7 +706,7 @@ throw new ClassNotFoundException("Can't do a codebase look up and there are no jars. Failing sooner rather than later"); } - AppThreadGroup group = (AppThreadGroup) Thread.currentThread().getThreadGroup(); + ThreadGroup group = Thread.currentThread().getThreadGroup(); String appletName = file.getApplet().getMainClass(); @@ -723,7 +722,6 @@ else appletInstance = new AppletInstance(file, group, loader, applet, cont); - group.setApplication(appletInstance); loader.setApplication(appletInstance); setContextClassLoaderForAllThreads(appletInstance.getThreadGroup(), appletInstance.getClassLoader()); @@ -770,10 +768,9 @@ protected ApplicationInstance createApplication(JNLPFile file) throws LaunchException { try { JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy); - AppThreadGroup group = (AppThreadGroup) Thread.currentThread().getThreadGroup(); + ThreadGroup group = Thread.currentThread().getThreadGroup(); ApplicationInstance app = new ApplicationInstance(file, group, loader); - group.setApplication(app); loader.setApplication(app); return app; @@ -789,16 +786,16 @@ * then this method simply returns the existing ThreadGroup. The applet * ThreadGroup has to be created at an earlier point in the applet code. */ - protected AppThreadGroup createThreadGroup(JNLPFile file) { - AppThreadGroup appThreadGroup = null; + protected ThreadGroup createThreadGroup(JNLPFile file) { + ThreadGroup tg = null; if (file instanceof PluginBridge) { - appThreadGroup = (AppThreadGroup) Thread.currentThread().getThreadGroup(); + tg = Thread.currentThread().getThreadGroup(); } else { - appThreadGroup = new AppThreadGroup(mainGroup, file.getTitle()); + tg = new ThreadGroup(mainGroup, file.getTitle()); } - return appThreadGroup; + return tg; } /** diff -r f0647c938535 -r 77640d74d21c netx/net/sourceforge/jnlp/NetxPanel.java --- a/netx/net/sourceforge/jnlp/NetxPanel.java Sat Jan 07 02:48:05 2012 -0500 +++ b/netx/net/sourceforge/jnlp/NetxPanel.java Fri Apr 29 16:58:05 2011 -0400 @@ -23,12 +23,13 @@ package net.sourceforge.jnlp; import net.sourceforge.jnlp.AppletLog; -import net.sourceforge.jnlp.runtime.AppThreadGroup; import net.sourceforge.jnlp.runtime.AppletInstance; import net.sourceforge.jnlp.runtime.JNLPRuntime; import java.net.URL; import java.util.Hashtable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import sun.applet.AppletViewerPanel; import sun.awt.SunToolkit; @@ -44,9 +45,58 @@ private boolean exitOnFailure = true; private AppletInstance appInst = null; private boolean appletAlive; + private final String uKey; + + // We use this so that we can create exactly one thread group + // for all panels with the same uKey. + private static final ConcurrentMap uKeyToTG = + new ConcurrentHashMap(); + + // This map is actually a set (unfortunately there is no ConcurrentSet + // in java.util.concurrent). If KEY is in this map, then we know that + // an app context has been created for the panel that has uKey.equals(KEY), + // so we avoid creating it a second time for panels with the same uKey. + // Because it's a set, only the keys matter. However, we can't insert + // null values in because if we did, we couldn't use null checks to see + // if a key was absent before a putIfAbsent. + private static final ConcurrentMap appContextCreated = + new ConcurrentHashMap(); public NetxPanel(URL documentURL, Hashtable atts) { super(documentURL, atts); + + /* According to http://download.oracle.com/javase/6/docs/technotes/guides/deployment/deployment-guide/applet-compatibility.html, + * classloaders are shared iff these properties match: + * codebase, cache_archive, java_archive, archive + * + * To achieve this, we create the uniquekey based on those 4 values, + * always in the same order. The initial "=" parts ensure a + * bad tag cannot trick the loader into getting shared with another. + */ + + // Firefox sometimes skips the codebase if it is default -- ".", + // so set it that way if absent + String codebaseAttr = atts.get("codebase") != null ? + atts.get("codebase") : "."; + + String cache_archiveAttr = atts.get("cache_archive") != null ? + atts.get("cache_archive") : ""; + + String java_archiveAttr = atts.get("java_archive") != null ? + atts.get("java_archive") : ""; + + String archiveAttr = atts.get("archive") != null ? + atts.get("archive") : ""; + + this.uKey = "codebase=" + codebaseAttr + + "cache_archive=" + cache_archiveAttr + + "java_archive=" + java_archiveAttr + + "archive=" + archiveAttr; + + // when this was being done (incorrectly) in Launcher, the call was + // new AppThreadGroup(mainGroup, file.getTitle()); + ThreadGroup tg = new ThreadGroup(Launcher.mainGroup, this.documentURL.toString()); + uKeyToTG.putIfAbsent(this.uKey, tg); } // overloaded constructor, called when initialized via plugin @@ -58,18 +108,6 @@ } @Override - public void run() { - /* - * create an AppContext for this thread associated with this particular - * plugin instance (which runs in a different thread group from the rest - * of the plugin). - */ - SunToolkit.createNewAppContext(); - - super.run(); - } - - @Override protected void showAppletException(Throwable t) { /* * Log any exceptions thrown while loading, initializing, starting, @@ -78,7 +116,7 @@ AppletLog.log(t); super.showAppletException(t); } - + //Overriding to use Netx classloader. You might need to relax visibility //in sun.applet.AppletPanel for runLoader(). protected void runLoader() { @@ -90,7 +128,7 @@ getCode(), getWidth(), getHeight(), - atts); + atts, uKey); doInit = true; dispatchAppletEvent(APPLET_LOADING, null); @@ -154,11 +192,7 @@ } } - // when this was being done (incorrectly) in Launcher, the call was - // new AppThreadGroup(mainGroup, file.getTitle()); - ThreadGroup tg = new AppThreadGroup(Launcher.mainGroup, - this.documentURL.toString()); - handler = new Thread(tg, this); + handler = new Thread(getThreadGroup(), this); handler.start(); } @@ -174,4 +208,19 @@ public boolean isAlive() { return handler != null && handler.isAlive() && this.appletAlive; } + + public ThreadGroup getThreadGroup() { + return uKeyToTG.get(uKey); + } + + public void createNewAppContext() { + if (Thread.currentThread().getThreadGroup() != getThreadGroup()) { + throw new RuntimeException("createNewAppContext called from the wrong thread."); + } + // only create a new context if one hasn't already been created for the + // applets with this unique key. + if (null == appContextCreated.putIfAbsent(uKey, Boolean.TRUE)) { + SunToolkit.createNewAppContext(); + } + } } diff -r f0647c938535 -r 77640d74d21c netx/net/sourceforge/jnlp/PluginBridge.java --- a/netx/net/sourceforge/jnlp/PluginBridge.java Sat Jan 07 02:48:05 2012 -0500 +++ b/netx/net/sourceforge/jnlp/PluginBridge.java Fri Apr 29 16:58:05 2011 -0400 @@ -44,7 +44,8 @@ private boolean codeBaseLookup; public PluginBridge(URL codebase, URL documentBase, String jar, String main, - int width, int height, Hashtable atts) + int width, int height, Hashtable atts, + String uKey) throws Exception { specVersion = new Version("1.0"); fileVersion = new Version("1.1"); @@ -132,34 +133,7 @@ else security = null; - /* According to http://download.oracle.com/javase/6/docs/technotes/guides/deployment/deployment-guide/applet-compatibility.html, - * classloaders are shared iff these properties match: - * codebase, cache_archive, java_archive, archive - * - * To achieve this, we create the uniquekey based on those 4 values, - * always in the same order. The initial "=" parts ensure a - * bad tag cannot trick the loader into getting shared with another. - */ - - // Firefox sometimes skips the codebase if it is default -- ".", - // so set it that way if absent - String codebaseAttr = atts.get("codebase") != null ? - atts.get("codebase") : "."; - - String cache_archiveAttr = atts.get("cache_archive") != null ? - atts.get("cache_archive") : ""; - - String java_archiveAttr = atts.get("java_archive") != null ? - atts.get("java_archive") : ""; - - String archiveAttr = atts.get("archive") != null ? - atts.get("archive") : ""; - - this.uniqueKey = "codebase=" + codebaseAttr + - "cache_archive=" + cache_archiveAttr + - "java_archive=" + java_archiveAttr + - "archive=" + archiveAttr; - + this.uniqueKey = uKey; usePack = false; useVersion = false; String jargs = atts.get("java_arguments"); diff -r f0647c938535 -r 77640d74d21c netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java --- a/netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java Sat Jan 07 02:48:05 2012 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -// Copyright (C) 2001-2003 Jon A. Maxwell (JAM) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -package net.sourceforge.jnlp.runtime; - -/** - * Thread group for a JNLP application. - * - * @author Jon A. Maxwell (JAM) - initial author - * @version $Revision: 1.5 $ - */ -public class AppThreadGroup extends ThreadGroup { - - /** the app */ - private ApplicationInstance app = null; - - /** - * Creates new JavaAppThreadGroup - * - * @param name of the App - */ - public AppThreadGroup(ThreadGroup parent, String name) { - super(parent, name); - } - - /** - * Sets the JNLP app this group is for; can only be called once. - */ - public void setApplication(ApplicationInstance app) { - if (this.app != null) - throw new IllegalStateException("Application can only be set once"); - - this.app = app; - } - - /** - * Returns the JNLP app for this thread group. - */ - public ApplicationInstance getApplication() { - return app; - } - - /** - * Handle uncaught exceptions for the app. - */ - public void uncaughtException(Thread t, Throwable e) { - super.uncaughtException(t, e); - } - -} diff -r f0647c938535 -r 77640d74d21c plugin/icedteanp/java/sun/applet/PluginAppletViewer.java --- a/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java Sat Jan 07 02:48:05 2012 -0500 +++ b/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java Fri Apr 29 16:58:05 2011 -0400 @@ -123,10 +123,10 @@ class PluginAppletPanelFactory { public AppletPanel createPanel(PluginStreamHandler streamhandler, - int identifier, - long handle, int x, int y, - final URL doc, - final Hashtable atts) { + final int identifier, + final long handle, int x, int y, + final URL doc, + final Hashtable atts) { final NetxPanel panel = AccessController.doPrivileged(new PrivilegedAction() { public NetxPanel run() { NetxPanel panel = new NetxPanel(doc, atts, false); @@ -136,13 +136,29 @@ } }); - // create the frame. - PluginAppletViewer.framePanel(identifier, handle, panel); + // Framing the panel needs to happen in a thread whose thread group + // is the same as the threadgroup of the applet thread. If this + // isn't the case, the awt eventqueue thread's context classloader + // won't be set to a JNLPClassLoader, and when an applet class needs + // to be loaded from the awt eventqueue, it won't be found. + Thread panelInit = new Thread(panel.getThreadGroup(), new Runnable() { + @Override public void run() { + panel.createNewAppContext(); + // create the frame. + PluginAppletViewer.framePanel(identifier, handle, panel); + panel.init(); + // Start the applet + initEventQueue(panel); + } + }, "NetXPanel initializer"); - panel.init(); - - // Start the applet - initEventQueue(panel); + panelInit.start(); + while(panelInit.isAlive()) { + try { + panelInit.join(); + } catch (InterruptedException e) { + } + } // Wait for the panel to initialize PluginAppletViewer.waitForAppletInit(panel);