diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/GuiLaunchHandler.java --- a/netx/net/sourceforge/jnlp/GuiLaunchHandler.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/GuiLaunchHandler.java Mon Sep 10 16:13:58 2018 +0200 @@ -37,10 +37,8 @@ package net.sourceforge.jnlp; -import java.lang.reflect.InvocationTargetException; import java.net.URL; - -import javax.swing.SwingUtilities; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.cache.ResourceTracker; import net.sourceforge.jnlp.cache.UpdatePolicy; @@ -70,7 +68,7 @@ @Override public void launchError(final LaunchException exception) { BasicExceptionDialog.willBeShown(); - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { closeSplashScreen(); @@ -94,7 +92,7 @@ @Override public void launchStarting(ApplicationInstance application) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { closeSplashScreen(); @@ -117,39 +115,17 @@ resourceTracker.addResource(splashImageURL, file.getFileVersion(), null, policy); } synchronized (mutex) { - try { - SwingUtilities.invokeAndWait(new Runnable() { + SwingUtils.invokeAndWait(new Runnable() { - @Override - public void run() { - splashScreen = new JNLPSplashScreen(resourceTracker, file); - } - }); - } catch (InterruptedException ie) { - // Wait till splash screen is created - while (splashScreen == null); - } catch (InvocationTargetException ite) { - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ite); - } - try { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - splashScreen.setSplashImageURL(splashImageURL); - } - }); - } catch (InterruptedException ie) { - // Wait till splash screen is created - while (!splashScreen.isSplashImageLoaded()); - } catch (InvocationTargetException ite) { - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ite); - } - - + @Override + public void run() { + splashScreen = new JNLPSplashScreen(resourceTracker, file); + splashScreen.setSplashImageURL(splashImageURL); + } + }); } - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/JNLPSplashScreen.java --- a/netx/net/sourceforge/jnlp/JNLPSplashScreen.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/JNLPSplashScreen.java Mon Sep 10 16:13:58 2018 +0200 @@ -47,6 +47,7 @@ import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JDialog; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.cache.ResourceTracker; import net.sourceforge.jnlp.splashscreen.SplashPanel; import net.sourceforge.jnlp.splashscreen.SplashUtils; @@ -70,7 +71,9 @@ private SplashPanel splash; public JNLPSplashScreen(ResourceTracker resourceTracker, final JNLPFile file) { - + super(); + this.setName("JNLPSplashScreen"); + SwingUtils.info(this); setIconImages(ImageResources.INSTANCE.getApplicationImages()); // If the JNLP file does not contain any icon images, the splash image diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/Launcher.java --- a/netx/net/sourceforge/jnlp/Launcher.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/Launcher.java Mon Sep 10 16:13:58 2018 +0200 @@ -549,11 +549,14 @@ R("LCantDetermineMainClassInfo"))); } + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "Starting application [" + mainName + "] ..."); + Class mainClass = app.getClassLoader().loadClass(mainName); Method main = mainClass.getMethod("main", new Class[] { String[].class }); String args[] = file.getApplication().getArguments(); + // Use SwingUtilities (not SwingUtils) to create EDT within application context: SwingUtilities.invokeAndWait(new Runnable() { // dummy method to force Event Dispatch Thread creation @Override diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/about/AboutDialog.java --- a/netx/net/sourceforge/jnlp/about/AboutDialog.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/about/AboutDialog.java Mon Sep 10 16:13:58 2018 +0200 @@ -60,6 +60,7 @@ import net.sourceforge.jnlp.util.docprovider.TextsProvider; import net.sourceforge.jnlp.util.docprovider.formatters.formatters.HtmlFormatter; import net.sourceforge.jnlp.util.logging.OutputController; +import net.sourceforge.swing.SwingUtils; public final class AboutDialog extends JPanel implements Runnable, ActionListener { @@ -95,10 +96,11 @@ super(new GridBagLayout()); this.app = app; frame = new JDialog((Frame) null, R("AboutDialogueTabAbout") + " IcedTea-Web", modal); + frame.setName("AboutDialog"); + SwingUtils.info(frame); frame.setContentPane(this); frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - aboutButton = new JButton( R("AboutDialogueTabAbout")); aboutButton.addActionListener(this); @@ -267,6 +269,7 @@ } public static void display(boolean modal, String app, ShowPage showPage) { + // Use SwingUtilities to keep main thread alive: SwingUtilities.invokeLater(new AboutDialog(modal, app, showPage)); } } diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java --- a/netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java Mon Sep 10 16:13:58 2018 +0200 @@ -26,6 +26,7 @@ import javax.swing.*; import javax.swing.Timer; import javax.jnlp.*; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.runtime.*; import net.sourceforge.jnlp.util.ImageResources; @@ -107,38 +108,47 @@ */ @Override public DownloadServiceListener getListener(ApplicationInstance app, String downloadName, URL resources[]) { - DownloadPanel result = new DownloadPanel(downloadName); + final FutureResult result = new FutureResult() { + @Override + public void run() { + DownloadPanel result = new DownloadPanel(downloadName); + + synchronized (dialogMutex) { + if (dialog == null) { + dialog = createDownloadIndicatorWindow(true); + } - synchronized (dialogMutex) { - if (dialog == null) { - dialog = createDownloadIndicatorWindow(true); - } + if (resources != null) { + for (URL url : resources) { + result.addProgressPanel(url, null); + } + } - if (resources != null) { - for (URL url : resources) { - result.addProgressPanel(url, null); - + dialog.getContentPane().add(result, vertical); + dialog.pack(); + placeFrameToLowerRight(); + + result.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + placeFrameToLowerRight(); + } + }); + + dialog.setVisible(true); + + setRef(result); } } - - dialog.getContentPane().add(result, vertical); - dialog.pack(); - placeFrameToLowerRight(); - result.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - placeFrameToLowerRight(); - } - }); - - dialog.setVisible(true); - - return result; - } + }; + SwingUtils.invokeAndWait(result); + return result.getRef(); } public static JDialog createDownloadIndicatorWindow(boolean undecorated) throws HeadlessException { JDialog f = new JDialog((JFrame)null, downloading + "..."); + f.setName("DownloadIndicatorDialog"); + SwingUtils.info(f); f.setUndecorated(undecorated); f.setIconImages(ImageResources.INSTANCE.getApplicationImages()); f.getContentPane().setLayout(new GridBagLayout()); @@ -328,7 +338,7 @@ } }; - SwingUtilities.invokeLater(r); + SwingUtils.invokeLater(r); } /** @@ -474,4 +484,15 @@ } }; + static abstract class FutureResult implements Runnable { + private V ref = null; + + public V getRef() { + return ref; + } + + public void setRef(V ref) { + this.ref = ref; + } + } } diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/controlpanel/AdvancedProxySettingsDialog.java --- a/netx/net/sourceforge/jnlp/controlpanel/AdvancedProxySettingsDialog.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/controlpanel/AdvancedProxySettingsDialog.java Mon Sep 10 16:13:58 2018 +0200 @@ -32,6 +32,7 @@ import net.sourceforge.jnlp.runtime.Translator; import net.sourceforge.jnlp.util.ImageResources; import net.sourceforge.jnlp.util.ScreenFinder; +import net.sourceforge.swing.SwingUtils; /** * This dialog provides a means for user to edit more of the proxy settings. @@ -55,6 +56,8 @@ */ public AdvancedProxySettingsDialog(DeploymentConfiguration config) { super((Frame) null, dialogTitle, true); // Don't need a parent. + this.setName("AdvancedProxySettingsDialog"); + SwingUtils.info(this); setIconImages(ImageResources.INSTANCE.getApplicationImages()); this.config = config; diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java --- a/netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java Mon Sep 10 16:13:58 2018 +0200 @@ -34,6 +34,7 @@ import net.sourceforge.jnlp.runtime.Translator; import net.sourceforge.jnlp.util.ImageResources; import net.sourceforge.jnlp.util.ScreenFinder; +import net.sourceforge.swing.SwingUtils; /** * This class will provide a visual way of viewing cache. @@ -55,6 +56,8 @@ */ public CacheViewer(DeploymentConfiguration config) { super((Frame) null, dialogTitle, true); // Don't need a parent. + this.setName("CacheViewer"); + SwingUtils.info(this); this.config = config; if (config == null) { throw new IllegalArgumentException("config: " + config); diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java --- a/netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java Mon Sep 10 16:13:58 2018 +0200 @@ -59,6 +59,7 @@ import net.sourceforge.jnlp.security.viewer.CertificatePane; import net.sourceforge.jnlp.util.ImageResources; import net.sourceforge.jnlp.util.logging.OutputController; +import net.sourceforge.swing.SwingUtils; /** * This is the control panel for Java. It provides a GUI for modifying the @@ -398,6 +399,9 @@ } public static void main(String[] args) throws Exception { + // setup Swing EDT tracing: + SwingUtils.setup(); + DeploymentConfiguration.move14AndOlderFilesTo15StructureCatched(); final DeploymentConfiguration config = new DeploymentConfiguration(); try { @@ -418,7 +422,7 @@ // ignore; not a big deal } - + // Use SwingUtilities to keep main thread alive: SwingUtilities.invokeLater(new Runnable() { @Override public void run() { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/runtime/Boot.java --- a/netx/net/sourceforge/jnlp/runtime/Boot.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/Boot.java Mon Sep 10 16:13:58 2018 +0200 @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.swing.SwingUtilities; import javax.swing.UIManager; import net.sourceforge.jnlp.LaunchException; import net.sourceforge.jnlp.OptionsDefinitions; @@ -47,6 +48,7 @@ import static net.sourceforge.jnlp.runtime.Translator.R; import net.sourceforge.jnlp.runtime.html.browser.LinkingBrowser; +import net.sourceforge.swing.SwingUtils; /** * This is the main entry point for the JNLP client. The main method parses the @@ -95,6 +97,9 @@ * @param argsIn launching arguments */ public static void main(String[] argsIn) throws UnevenParameterException { + // setup Swing EDT tracing: + SwingUtils.setup(); + optionParser = new OptionParser(argsIn, OptionsDefinitions.getJavaWsOptions()); if (optionParser.hasOption(OptionsDefinitions.OPTIONS.VERBOSE)) { @@ -112,11 +117,17 @@ if (optionParser.hasOption(OptionsDefinitions.OPTIONS.VIEWER)) { try { - CertificateViewer.main(null); - JNLPRuntime.exit(0); + // Use SwingUtilities to keep main thread alive: + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + CertificateViewer.showCertificateViewer(); + } + }); } catch (Exception e) { OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e); } + JNLPRuntime.exit(0); } if (optionParser.hasOption(OptionsDefinitions.OPTIONS.VERSION)) { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/runtime/HtmlBoot.java --- a/netx/net/sourceforge/jnlp/runtime/HtmlBoot.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/HtmlBoot.java Mon Sep 10 16:13:58 2018 +0200 @@ -45,7 +45,6 @@ import java.util.List; import java.util.Map; import javax.swing.JFrame; -import javax.swing.SwingUtilities; import net.sourceforge.jnlp.JNLPSplashScreen; import net.sourceforge.jnlp.OptionsDefinitions; import net.sourceforge.jnlp.ParserSettings; @@ -58,6 +57,7 @@ import static net.sourceforge.jnlp.runtime.Translator.R; import net.sourceforge.jnlp.util.optionparser.OptionParser; +import net.sourceforge.swing.SwingUtils; public final class HtmlBoot { @@ -111,7 +111,7 @@ boolean run(Map> extra) { if (!optionParser.hasOption(OptionsDefinitions.OPTIONS.HEADLESS)) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { @@ -157,7 +157,7 @@ //close all applets in time f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { Point movement = changeMovementSigns(move); @@ -180,7 +180,7 @@ } catch (final Exception ex) { OutputController.getLogger().log(ex); if (splashScreen != null) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java --- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java Mon Sep 10 16:13:58 2018 +0200 @@ -19,6 +19,8 @@ import static net.sourceforge.jnlp.runtime.Translator.R; import java.awt.EventQueue; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -48,7 +50,6 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.swing.JOptionPane; -import javax.swing.JWindow; import javax.swing.UIManager; import javax.swing.text.html.parser.ParserDelegator; @@ -741,8 +742,10 @@ } if (!headless) { try { - new JWindow().getOwner(); - } catch (Exception ex) { + if (GraphicsEnvironment.isHeadless()) { + throw new HeadlessException(); + } + } catch (HeadlessException ex) { headless = true; OutputController.getLogger().log(ex); OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, Translator.R("HEADLESS_MISSCONFIGURED")); diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java --- a/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java Mon Sep 10 16:13:58 2018 +0200 @@ -23,12 +23,12 @@ import java.security.AccessControlException; import java.security.Permission; -import javax.swing.JWindow; import net.sourceforge.jnlp.security.SecurityDialogs.AccessType; import net.sourceforge.jnlp.services.ServiceUtil; import net.sourceforge.jnlp.util.logging.OutputController; import net.sourceforge.jnlp.util.WeakList; +import net.sourceforge.swing.SwingUtils; import sun.awt.AWTSecurityManager; import sun.awt.AppContext; @@ -114,7 +114,8 @@ // called for it (and not disposed). if (!JNLPRuntime.isHeadless()) { - new JWindow().getOwner(); + /* is it really usefull ? */ + SwingUtils.getOrCreateWindowOwner(); } mainAppContext = AppContext.getAppContext(); diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/security/SecurityDialog.java --- a/netx/net/sourceforge/jnlp/security/SecurityDialog.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/security/SecurityDialog.java Mon Sep 10 16:13:58 2018 +0200 @@ -46,6 +46,7 @@ import java.security.cert.X509Certificate; import javax.swing.JDialog; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.JNLPFile; import net.sourceforge.jnlp.runtime.JNLPClassLoader.SecurityDelegate; @@ -65,7 +66,6 @@ import net.sourceforge.jnlp.security.dialogs.SingleCertInfoPane; import net.sourceforge.jnlp.security.dialogs.ViwableDialog; import net.sourceforge.jnlp.security.dialogs.apptrustwarningpanel.AppTrustWarningDialog; -import net.sourceforge.jnlp.util.ScreenFinder; import net.sourceforge.jnlp.util.logging.OutputController; /** @@ -227,12 +227,18 @@ private void initDialog() { String dialogTitle = createTitle(); + // Note: ViwableDialog methods are defered until show(): getViwableDialog().setTitle(dialogTitle); getViwableDialog().setModalityType(ModalityType.MODELESS); getViwableDialog().setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - installPanel(); + SwingUtils.invokeAndWait(new Runnable() { + @Override + public void run() { + installPanel(); + } + }); getViwableDialog().pack(); getViwableDialog().centerDialog(); @@ -251,6 +257,8 @@ @Override public void windowOpened(WindowEvent e) { + // dead code as (e.getSource() instanceof SecurityDialog) is false + // e.getSource() is a JDialog instead ! if (e.getSource() instanceof SecurityDialog) { SecurityDialog dialog = (SecurityDialog) e.getSource(); dialog.getViwableDialog().setResizable(true); diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/security/SecurityDialogs.java --- a/netx/net/sourceforge/jnlp/security/SecurityDialogs.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/security/SecurityDialogs.java Mon Sep 10 16:13:58 2018 +0200 @@ -47,7 +47,7 @@ import java.util.concurrent.Semaphore; import javax.swing.JDialog; -import javax.swing.SwingUtilities; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.JNLPFile; import net.sourceforge.jnlp.cache.Resource; @@ -320,7 +320,7 @@ /* * If this is the event dispatch thread the use the hack */ - if (SwingUtilities.isEventDispatchThread()) { + if (SwingUtils.isEventDispatchThread()) { /* * Create a tiny modal dialog (which creates a new EventQueue for * this AppContext, but blocks the original client EventQueue) and @@ -329,6 +329,8 @@ * continue processing */ final JDialog fakeDialog = new JDialog(); + fakeDialog.setName("FakeDialog"); + SwingUtils.info(fakeDialog); fakeDialog.setSize(0, 0); fakeDialog.setResizable(false); fakeDialog.setModalityType(ModalityType.APPLICATION_MODAL); diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/security/dialogs/ViwableDialog.java --- a/netx/net/sourceforge/jnlp/security/dialogs/ViwableDialog.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/security/dialogs/ViwableDialog.java Mon Sep 10 16:13:58 2018 +0200 @@ -45,6 +45,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.JDialog; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.util.ImageResources; import net.sourceforge.jnlp.util.ScreenFinder; @@ -57,18 +58,23 @@ */ public class ViwableDialog { - private JDialog jd; + private JDialog jd = null; List operations = new ArrayList(); public ViwableDialog() { } - private JDialog crateJDialog() { + private JDialog createJDialog() { jd = new JDialog(); + jd.setName("ViwableDialog"); + SwingUtils.info(jd); jd.setIconImages(ImageResources.INSTANCE.getApplicationImages()); + for (Runnable operation : operations) { operation.run(); } + // prune operations. May throw NPE if operations used after createJDialog() + operations = null; return jd; } @@ -100,10 +106,15 @@ } public void show() { - if (jd == null) { - jd = crateJDialog(); - } - jd.setVisible(true); + SwingUtils.invokeAndWait(new Runnable() { + @Override + public void run() { + if (jd == null) { + jd = createJDialog(); + } + jd.setVisible(true); + } + }); } /** @@ -112,8 +123,11 @@ */ public void dispose() { notifySelectionMade(); + if (jd != null) { jd.dispose(); + // recycle: + jd = null; } } @@ -190,12 +204,10 @@ } public void setResizable(final boolean b) { - operations.add(new Runnable() { - @Override - public void run() { - jd.setResizable(b); - } - }); + // not defered: called when alive + if (jd != null) { + jd.setResizable(b); + } } public void addWindowListener(final WindowAdapter adapter) { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java --- a/netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java Mon Sep 10 16:13:58 2018 +0200 @@ -41,18 +41,17 @@ import java.awt.BorderLayout; import java.awt.Container; -import java.awt.Dimension; import java.awt.Frame; -import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JDialog; -import javax.swing.UIManager; +import javax.swing.SwingUtilities; import net.sourceforge.jnlp.runtime.JNLPRuntime; import net.sourceforge.jnlp.util.ImageResources; import net.sourceforge.jnlp.util.ScreenFinder; +import net.sourceforge.swing.SwingUtils; public class CertificateViewer extends JDialog { @@ -63,6 +62,8 @@ public CertificateViewer() { super((Frame) null, dialogTitle, true); + this.setName("CertificateViewer"); + SwingUtils.info(this); setIconImages(ImageResources.INSTANCE.getApplicationImages()); Container contentPane = getContentPane(); @@ -98,7 +99,7 @@ ScreenFinder.centerWindowsToCurrentScreen(this); } - public static void showCertificateViewer() throws Exception { + public static void showCertificateViewer() { JNLPRuntime.initialize(true); CertificateViewer cv = new CertificateViewer(); @@ -109,6 +110,15 @@ } public static void main(String[] args) throws Exception { - CertificateViewer.showCertificateViewer(); + // setup Swing EDT tracing: + SwingUtils.setup(); + + // Use SwingUtilities to keep main thread alive: + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + CertificateViewer.showCertificateViewer(); + } + }); } } diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/splashscreen/impls/defaultsplashscreen2012/BasePainter.java --- a/netx/net/sourceforge/jnlp/splashscreen/impls/defaultsplashscreen2012/BasePainter.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/splashscreen/impls/defaultsplashscreen2012/BasePainter.java Mon Sep 10 16:13:58 2018 +0200 @@ -55,7 +55,7 @@ import java.util.Observable; import java.util.Observer; -import javax.swing.SwingUtilities; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.runtime.Translator; import net.sourceforge.jnlp.splashscreen.SplashUtils.SplashReason; import net.sourceforge.jnlp.splashscreen.parts.BasicComponentSplashScreen; @@ -558,18 +558,16 @@ @Override public void update(Observable o, Object arg) { - try { - SwingUtilities.invokeAndWait(new Runnable() { + SwingUtils.invokeAndWait(new Runnable() { - @Override - public void run() { + @Override + public void run() { + if (master.isAnimationRunning()) { ExtensionManager.getExtension().animate(); master.repaint(); } - }); - } catch (Exception ex) { - OutputController.getLogger().log(ex); - } + } + }); } public BasicComponentSplashScreen getMaster() { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/util/BasicExceptionDialog.java --- a/netx/net/sourceforge/jnlp/util/BasicExceptionDialog.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/util/BasicExceptionDialog.java Mon Sep 10 16:13:58 2018 +0200 @@ -57,7 +57,7 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; -import javax.swing.SwingUtilities; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.controlpanel.CachePane; import net.sourceforge.jnlp.util.logging.JavaConsole; @@ -81,6 +81,8 @@ JOptionPane optionPane = new JOptionPane(mainPanel, JOptionPane.ERROR_MESSAGE); final JDialog errorDialog = optionPane.createDialog(R("Error")); + errorDialog.setName("BasicExceptionDialog"); + SwingUtils.info(errorDialog); errorDialog.setIconImages(ImageResources.INSTANCE.getApplicationImages()); final JPanel quickInfoPanelAll = new JPanel(); @@ -179,7 +181,7 @@ @Override public void actionPerformed(java.awt.event.ActionEvent evt) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/util/FileUtils.java --- a/netx/net/sourceforge/jnlp/util/FileUtils.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/util/FileUtils.java Mon Sep 10 16:13:58 2018 +0200 @@ -33,8 +33,6 @@ import java.io.Writer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; -import java.nio.file.Files; -import java.nio.file.attribute.*; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -42,7 +40,7 @@ import javax.swing.JFrame; import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.config.DirectoryValidator; import net.sourceforge.jnlp.config.DirectoryValidator.DirectoryCheckResults; @@ -374,7 +372,7 @@ * @param frame a {@link JFrame} to act as parent to this dialog */ public static void showReadOnlyDialog(final Component frame) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { JOptionPane.showMessageDialog(frame, R("RFileReadOnly"), R("Warning"), JOptionPane.WARNING_MESSAGE); @@ -422,7 +420,7 @@ * @param message a {@link String} giving the specific reason the file could not be opened */ public static void showCouldNotOpenDialog(final Component frame, final String message) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { JOptionPane.showMessageDialog(frame, message, R("Error"), JOptionPane.ERROR_MESSAGE); diff -r bcbef8d7bbd6 netx/net/sourceforge/jnlp/util/logging/JavaConsole.java --- a/netx/net/sourceforge/jnlp/util/logging/JavaConsole.java Mon May 14 17:15:38 2018 +0200 +++ b/netx/net/sourceforge/jnlp/util/logging/JavaConsole.java Mon Sep 10 16:13:58 2018 +0200 @@ -69,10 +69,10 @@ import javax.swing.JSpinner; import javax.swing.JSplitPane; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.DefaultFormatter; +import net.sourceforge.swing.SwingUtils; import net.sourceforge.jnlp.config.DeploymentConfiguration; import net.sourceforge.jnlp.runtime.JNLPRuntime; import net.sourceforge.jnlp.util.ImageResources; @@ -204,6 +204,9 @@ private void initializeWindow(Dimension size, JPanel content) { consoleWindow = new JDialog((JFrame) null, R("DPJavaConsole")); + consoleWindow.setName("JavaConsole"); + SwingUtils.info(consoleWindow); + consoleWindow.addWindowListener(new WindowAdapter() { @Override @@ -353,7 +356,7 @@ @Override public void actionPerformed(ActionEvent e) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { @@ -407,7 +410,7 @@ } public void showConsoleLater(final boolean modal) { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { @@ -417,7 +420,7 @@ } public void hideConsoleLater() { - SwingUtilities.invokeLater(new Runnable() { + SwingUtils.invokeLater(new Runnable() { @Override public void run() { @@ -514,7 +517,17 @@ private synchronized void updateModel(Boolean force) { observable.setChanged(); - observable.notifyObservers(force); + + SwingUtils.invokeLater(new Runnable() { + + @Override + public void run() { + // avoid too much processing if already processed: + if (observable.hasChanged() || (Boolean.TRUE.equals(force))) { + observable.notifyObservers(force); + } + } + }); } /** diff -r bcbef8d7bbd6 netx/net/sourceforge/swing/SwingUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/swing/SwingUtils.java Mon Sep 10 16:13:58 2018 +0200 @@ -0,0 +1,232 @@ +/* SwingUtils.java + Copyright (C) 2018 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package net.sourceforge.swing; + +import java.awt.Window; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import javax.swing.JDialog; +import javax.swing.JWindow; +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; +import net.sourceforge.jnlp.runtime.Translator; +import net.sourceforge.jnlp.util.logging.OutputController; + +/** + * Swing / AWT utility class + * @author Laurent Bourgès + */ +public final class SwingUtils { + + private static final boolean DEBUG_EDT = System.getProperty("icedtea-web.edt.debug", "false").equalsIgnoreCase("true"); + // debugging flags: + private static final boolean INFO_DIALOG = false; + private static final boolean TRACE_INVOKE_EDT = false; + private static final boolean TRACE_TG = false; + + /** main thread group (initialized at startup) */ + static final ThreadGroup MAIN_GROUP = Thread.currentThread().getThreadGroup(); + + private SwingUtils() { + // forbidden + } + + public static void setup() { + if (DEBUG_EDT) { + TracingEventQueue.install(); + + trace("Using ThreadCheckingRepaintManager"); + RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager()); + } + } + + static void trace(final String msg) { + // Use System.err directly for debuging EDT without any conflict with console / logging system + System.err.println(msg); + } + + static void traceWithStack(final String msg) { + trace(msg); + new Throwable().printStackTrace(); + } + + public static void info(final JDialog dialog) { + if (INFO_DIALOG) { + trace("Dialog[" + dialog.getName() + "]" + + " in TG [" + Thread.currentThread().getThreadGroup() + "]"); + checkEDT(); + } + } + + public static void checkEDT() { + if (!isEventDispatchThread()) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, new Exception("EDT violation")); + } + } + + // --- SwingUtilities wrapper --- + public static boolean isEventDispatchThread() { + return SwingUtilities.isEventDispatchThread(); + } + + public static void invokeLater(final Runnable doRun) { + if (isMainThreadGroup()) { + if (isEventDispatchThread()) { + if (TRACE_INVOKE_EDT) { + traceWithStack("invokeLater() from EDT: MAY be fixed ?"); + } + } + SwingUtilities.invokeLater(doRun); + } else { + EDT_DAEMON_THREAD_POOL.submit(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(doRun); + } + }); + } + } + + public static void invokeAndWait(final Runnable doRun) { + if (isMainThreadGroup()) { + if (isEventDispatchThread()) { + if (TRACE_INVOKE_EDT) { + traceWithStack("invokeAndWait() from EDT: MAY be fixed ?"); + } + doRun.run(); + } else { + try { + SwingUtilities.invokeAndWait(doRun); + } catch (InterruptedException ie) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ie); + } catch (InvocationTargetException ite) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ite); + } + } + } else { + final Future future = EDT_DAEMON_THREAD_POOL.submit(new Callable() { + @Override + public Void call() throws Exception { + SwingUtilities.invokeAndWait(doRun); + return null; + } + }); + try { + // Wait on Future: + future.get(); + } catch (InterruptedException ie) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ie); + } catch (ExecutionException ee) { + if (ee.getCause() != null) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ee.getCause()); + } else { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ee); + } + } + } + } + + private static boolean isMainThreadGroup() { + final Thread t = Thread.currentThread(); + final ThreadGroup g = t.getThreadGroup(); + + if (g != MAIN_GROUP) { + if (TRACE_TG) { + traceWithStack("----------\ncheckThreadGroup: " + t); + } + return false; + } + return true; + } + + private static final class MainAppContextDaemonThreadFactory implements ThreadFactory { + + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix = "itw-edt-thread-"; + + @Override + public Thread newThread(Runnable r) { + final Thread t = new Thread(MAIN_GROUP, r, + namePrefix + threadNumber.getAndIncrement() + ); + if (!t.isDaemon()) { + t.setDaemon(true); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } + } + + /** single thread pool with max 1 live daemon thread */ + private static final ExecutorService EDT_DAEMON_THREAD_POOL = new ThreadPoolExecutor(0, 1, + 60L, TimeUnit.SECONDS, + new LinkedBlockingQueue(), + new MainAppContextDaemonThreadFactory() + ); + + /* shared Window owner */ + private static Window window = null; + + public static synchronized Window getOrCreateWindowOwner() { + if (window == null) { + invokeAndWait(new Runnable() { + @Override + public void run() { + try { + window = new JWindow().getOwner(); + window.setName("getOrCreateWindowOwner"); + } catch (Exception ex) { + OutputController.getLogger().log(ex); + OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, Translator.R("HEADLESS_MISSCONFIGURED")); + } + } + }); + } + return window; + } +} diff -r bcbef8d7bbd6 netx/net/sourceforge/swing/ThreadCheckingRepaintManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/swing/ThreadCheckingRepaintManager.java Mon Sep 10 16:13:58 2018 +0200 @@ -0,0 +1,150 @@ +/* ThreadCheckingRepaintManager.java + Copyright (C) 2018 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package net.sourceforge.swing; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import javax.swing.JComponent; +import javax.swing.RepaintManager; +import static net.sourceforge.swing.SwingUtils.trace; + +/** + * For usage of this class, please refer to http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html + *

To use it, call RepaintManager.setCurrentManager(new ThreadCheckingRepaintManager()) then watch the print out + * from the console of all threading violations.

+ */ +public final class ThreadCheckingRepaintManager extends RepaintManager { + + // it is recommended to pass the complete check + private boolean completeCheck = true; + private boolean checkIsShowing = false; + + /** + * Creates ThreadCheckingRepaintManager. You can create one and set it using RepaintManager.setCurrentManager(new + * ThreadCheckingRepaintManager()). + */ + public ThreadCheckingRepaintManager() { + super(); + } + + /** + * Creates ThreadCheckingRepaintManager. You can create one and set it using RepaintManager.setCurrentManager(new + * ThreadCheckingRepaintManager()). + * + * @param checkIsShowing true to only check showing components. + */ + public ThreadCheckingRepaintManager(boolean checkIsShowing) { + super(); + this.checkIsShowing = checkIsShowing; + } + + /** + * Initially there was a rule that it is safe to create and use Swing components until they are realized but this + * rule is not valid any more, and now it is recommended to interact with Swing from EDT only. + *

+ * That's why completeCheck flag is used - if you test the old program switch it to false, but new applications + * should be tested with completeCheck set to true* + * + * @return true or false. By default, it is false. + */ + public boolean isCompleteCheck() { + return completeCheck; + } + + /** + * @param completeCheck true or false. + * + * @see #isCompleteCheck() + */ + public void setCompleteCheck(boolean completeCheck) { + this.completeCheck = completeCheck; + } + + @Override + public synchronized void addInvalidComponent(JComponent jComponent) { + checkThreadViolations(jComponent); + super.addInvalidComponent(jComponent); + } + + @Override + public synchronized void addDirtyRegion(JComponent jComponent, int i, int i1, int i2, int i3) { + checkThreadViolations(jComponent); + super.addDirtyRegion(jComponent, i, i1, i2, i3); + } + + private void checkThreadViolations(JComponent c) { + if (!SwingUtils.isEventDispatchThread() && (completeCheck || checkIsShowing(c))) { + Exception exception = new Exception(); + boolean repaint = false; + boolean fromSwing = false; + StackTraceElement[] stackTrace = exception.getStackTrace(); + for (StackTraceElement st : stackTrace) { + if (repaint && st.getClassName().startsWith("javax.swing.")) { + fromSwing = true; + } + if ("repaint".equals(st.getMethodName())) { + repaint = true; + } + } + if (repaint && !fromSwing) { + //no problems here, since repaint() is thread safe + return; + } + trace("----------Wrong Thread START"); + trace(getStrackTraceAsString(exception)); + trace("----------Wrong Thread END"); + } + } + + @SuppressWarnings({"SimplifiableIfStatement"}) + private boolean checkIsShowing(JComponent c) { + if (this.checkIsShowing) { + return c.isShowing(); + } else { + return true; + } + } + + private String getStrackTraceAsString(Exception e) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + PrintStream printStream = new PrintStream(byteArrayOutputStream); + e.printStackTrace(printStream); + printStream.flush(); + return byteArrayOutputStream.toString(); + } +} diff -r bcbef8d7bbd6 netx/net/sourceforge/swing/TracingEventQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/swing/TracingEventQueue.java Mon Sep 10 16:13:58 2018 +0200 @@ -0,0 +1,129 @@ +/* TracingEventQueue.java + Copyright (C) 2018 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package net.sourceforge.swing; + +import java.awt.AWTEvent; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.util.HashMap; +import java.util.Map; +import static net.sourceforge.swing.SwingUtils.trace; + +/** + * Tracing EventQueue showing very long event processing (hanging or EDT blocked ?) + * @author Laurent Bourgès + */ +public final class TracingEventQueue extends EventQueue { + + public static void install() { + trace("Using TracingEventQueue"); + Toolkit.getDefaultToolkit().getSystemEventQueue().push(new TracingEventQueue()); + } + + private final TracingEventQueueThread tracingThread; + + private TracingEventQueue() { + this.tracingThread = new TracingEventQueueThread(SwingUtils.MAIN_GROUP, 500); + this.tracingThread.start(); + } + + @Override + protected void dispatchEvent(AWTEvent event) { + this.tracingThread.eventDispatched(event); + super.dispatchEvent(event); + this.tracingThread.eventProcessed(event); + } + + private final static class TracingEventQueueThread extends Thread { + + private final long thresholdDelay; + + private final Map eventTimeMap = new HashMap(1024); + + public TracingEventQueueThread(final ThreadGroup tg, long thresholdDelay) { + super(tg, "TracingEventQueueThread"); + setDaemon(true); + this.thresholdDelay = thresholdDelay; + } + + public void eventDispatched(AWTEvent event) { + synchronized (this) { + this.eventTimeMap.put(event, System.currentTimeMillis()); + } + } + + public void eventProcessed(AWTEvent event) { + synchronized (this) { + this.checkEventTime(event, System.currentTimeMillis(), + this.eventTimeMap.get(event)); + this.eventTimeMap.remove(event); + } + } + + private void checkEventTime(AWTEvent event, long currTime, long startTime) { + long currProcessingTime = currTime - startTime; + if (currProcessingTime >= this.thresholdDelay) { + trace("Event [" + event.hashCode() + "] " + + event.getClass().getName() + + " is taking too much time on EDT (" + currProcessingTime + + "): " + event.toString()); + } + } + + @Override + public void run() { + while (true) { + long currTime = System.currentTimeMillis(); + synchronized (this) { + for (Map.Entry entry : this.eventTimeMap.entrySet()) { + AWTEvent event = entry.getKey(); + if (entry.getValue() == null) { + continue; + } + long startTime = entry.getValue(); + this.checkEventTime(event, currTime, startTime); + } + } + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + } + } + } +}