From b99b81ff89cd36c8754066b639afc05f6c6ec481 Mon Sep 17 00:00:00 2001 From: Vhati Date: Sat, 27 Sep 2014 03:02:48 -0400 Subject: [PATCH] Added uncaught exception handling for background threads --- skel_common/readme_changelog.txt | 1 + .../vhati/modmanager/cli/SlipstreamCLI.java | 15 +++++++-- .../vhati/modmanager/ui/DatExtractDialog.java | 22 ++++++++++--- .../net/vhati/modmanager/ui/ManagerFrame.java | 31 ++++++++++++++++++- .../vhati/modmanager/ui/ModPatchDialog.java | 5 +-- .../vhati/modmanager/ui/ProgressDialog.java | 2 +- 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/skel_common/readme_changelog.txt b/skel_common/readme_changelog.txt index f9aa89a..1e7e820 100644 --- a/skel_common/readme_changelog.txt +++ b/skel_common/readme_changelog.txt @@ -16,6 +16,7 @@ Changelog - Added an error popup when the jar is double-clicked - Added *.xml.rawappend suffix to preserve whitespace in 'misc.xml' - Minor optimizations to reduce memory usage +- Added uncaught exception handling for background threads 1.4: - Cleaned up some dodgy code when initially prompting for FTL's location diff --git a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java index baa19ba..0f7bdd2 100644 --- a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java +++ b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java @@ -45,9 +45,19 @@ public class SlipstreamCLI { private static File backupDir = new File( "./backup/" ); private static File modsDir = new File( "./mods/" ); + private static Thread.UncaughtExceptionHandler exceptionHandler = null; + public static void main( String[] args ) { + exceptionHandler = new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException( Thread t, Throwable e ) { + log.error( "Uncaught exception in thread: "+ t.toString(), e ); + System.exit( 1 ); + } + }; + BasicParser parser = new BasicParser(); Options options = new Options(); @@ -266,6 +276,7 @@ public class SlipstreamCLI { SilentPatchObserver patchObserver = new SilentPatchObserver(); ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, globalPanic, patchObserver ); + patchThread.setDefaultUncaughtExceptionHandler( exceptionHandler ); deleteHook.addWatchedThread( patchThread ); patchThread.start(); @@ -325,7 +336,7 @@ public class SlipstreamCLI { config.load( new InputStreamReader( in, "UTF-8" ) ); } } - catch (IOException e) { + catch ( IOException e ) { log.error( "Error loading config.", e ); } finally { @@ -420,7 +431,7 @@ public class SlipstreamCLI { private static String getVersionMessage() { StringBuilder buf = new StringBuilder(); buf.append( String.format( "%s %s\n", FTLModManager.APP_NAME, FTLModManager.APP_VERSION ) ); - buf.append( "Copyright (C) 2013 David Millis\n" ); + buf.append( "Copyright (C) 2014 David Millis\n" ); buf.append( "\n" ); buf.append( "This program is free software; you can redistribute it and/or modify\n" ); buf.append( "it under the terms of the GNU General Public License as published by\n" ); diff --git a/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java b/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java index ec1f566..c8bbed9 100644 --- a/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java +++ b/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java @@ -23,6 +23,8 @@ public class DatExtractDialog extends ProgressDialog { private File extractDir; private File[] datFiles; + private DatExtractThread workerThread = null; + public DatExtractDialog( Frame owner, File extractDir, File[] datFiles ) { super( owner, false ); @@ -34,6 +36,18 @@ public class DatExtractDialog extends ProgressDialog { this.setSize( 400, 160 ); this.setMinimumSize( this.getPreferredSize() ); this.setLocationRelativeTo( owner ); + + workerThread = new DatExtractThread( extractDir, datFiles ); + } + + /** + * Returns the worker thread that does the extracting. + * + * This method is provided so other classes can customize the thread + * before calling extract(). + */ + public Thread getWorkerThread() { + return workerThread; } /** @@ -43,8 +57,7 @@ public class DatExtractDialog extends ProgressDialog { public void extract() { if ( started ) return; - DatExtractThread t = new DatExtractThread( extractDir, datFiles ); - t.start(); + workerThread.start(); started = true; } @@ -53,10 +66,11 @@ public class DatExtractDialog extends ProgressDialog { super.setTaskOutcome( outcome, e ); if ( !this.isShowing() ) return; - if ( succeeded ) + if ( succeeded ) { setStatusText( "All resources extracted successfully." ); - else + } else { setStatusText( String.format( "Error extracting dats: %s", e ) ); + } } diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java index 4cccbfe..d3776b9 100644 --- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java +++ b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java @@ -85,7 +85,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class ManagerFrame extends JFrame implements ActionListener, ModsScanObserver, Nerfable, Statusbar { +public class ManagerFrame extends JFrame implements ActionListener, ModsScanObserver, Nerfable, Statusbar, Thread.UncaughtExceptionHandler { private static final Logger log = LogManager.getLogger(ManagerFrame.class); @@ -406,6 +406,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse ); initThread.setDaemon( true ); initThread.setPriority( Thread.MIN_PRIORITY ); + initThread.setDefaultUncaughtExceptionHandler( this ); initThread.start(); } @@ -508,6 +509,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse ModsScanThread scanThread = new ModsScanThread( modFiles, localModDB, this ); scanThread.setDaemon( true ); scanThread.setPriority( Thread.MIN_PRIORITY ); + scanThread.setDefaultUncaughtExceptionHandler( this ); scanThread.start(); } @@ -685,6 +687,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse log.info( "Patching..." ); log.info( "" ); ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, false, patchDlg ); + patchThread.setDefaultUncaughtExceptionHandler( this ); patchThread.start(); patchDlg.setVisible( true ); @@ -763,6 +766,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse File[] datFiles = new File[] {dataDatFile, resDatFile}; DatExtractDialog extractDlg = new DatExtractDialog( this, extractDir, datFiles ); + extractDlg.getWorkerThread().setDefaultUncaughtExceptionHandler( this ); extractDlg.extract(); extractDlg.setVisible( true ); } @@ -919,6 +923,31 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse } + @Override + public void uncaughtException( Thread t, Throwable e ) { + log.error( "Uncaught exception in thread: "+ t.toString(), e ); + + final String threadString = t.toString(); + final String errString = e.toString(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + String message = "An unexpected error has occurred.\n"; + message += "\n"; + message += "Thread: "+ threadString +"\n"; + message += "Error: "+ errString +"\n"; + message += "\n"; + message += "See the log for details.\n"; + message += "\n"; + message += "If this interrupted patching, FTL's resources were probably corrupted.\n"; + message += "Restart Slipstream and patch without mods to restore vanilla backups."; + + JOptionPane.showMessageDialog( ManagerFrame.this, message, "Error", JOptionPane.ERROR_MESSAGE ); + } + }); + } + + private class SpawnGameTask implements Runnable { private final File exeFile; diff --git a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java index a5239ed..d594f92 100644 --- a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java +++ b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java @@ -68,9 +68,10 @@ public class ModPatchDialog extends ProgressDialog implements ModPatchObserver { super.setTaskOutcome( outcome, e ); if ( !this.isShowing() ) return; - if ( succeeded == true ) + if ( succeeded == true ) { setStatusText( "Patching completed." ); - else + } else { setStatusText( String.format( "Patching failed: %s", e ) ); + } } } diff --git a/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java b/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java index 660c332..4bff541 100644 --- a/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java +++ b/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java @@ -168,7 +168,7 @@ public class ProgressDialog extends JDialog implements ActionListener { /** - * Triggers a responce to the immediate task ending. (Thread-safe) + * Triggers a response to the immediate task ending. (Thread-safe) * * If anything went wrong, e may be non-null. */