Global default uncaught exception handling

This commit is contained in:
Vhati 2018-01-09 15:57:17 -05:00
parent 636f77409f
commit e9cf8bb53f
2 changed files with 46 additions and 8 deletions

View file

@ -73,6 +73,13 @@ public class FTLModManager {
log.debug( "OS: {} {}", System.getProperty( "os.name" ), System.getProperty( "os.version" ) ); log.debug( "OS: {} {}", System.getProperty( "os.name" ), System.getProperty( "os.version" ) );
log.debug( "VM: {}, {}, {}", System.getProperty( "java.vm.name" ), System.getProperty( "java.version" ), System.getProperty( "os.arch" ) ); log.debug( "VM: {}, {}, {}", System.getProperty( "java.vm.name" ), System.getProperty( "java.version" ), System.getProperty( "os.arch" ) );
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException( Thread t, Throwable e ) {
log.error( "Uncaught exception in thread: "+ t.toString(), e );
}
});
if ( args.length > 0 ) { if ( args.length > 0 ) {
SlipstreamCLI.main( args ); SlipstreamCLI.main( args );
return; return;
@ -348,14 +355,26 @@ public class FTLModManager {
} }
// Create the main window. // Create the main window.
ManagerFrame frame = null;
try { try {
ManagerFrame frame = new ManagerFrame( appConfig, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR ); frame = new ManagerFrame( appConfig, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR );
frame.init(); frame.init();
frame.setVisible( true ); frame.setVisible( true );
} }
catch ( Exception e ) { catch ( Exception e ) {
log.error( "Exception while creating ManagerFrame", e ); log.error( "Exception while creating ManagerFrame", e );
// If the frame is constructed, but an exception prevents it
// becoming visible, that *must* be caught. The frame registers
// itself as a global uncaught exception handler. It doesn't
// dispose() itself in the handler, so EDT will wait forever
// for an invisible window to close.
if ( frame != null && frame.isDisplayable() ) {
frame.setDisposeNormally( false );
frame.dispose();
}
throw new ExitException(); throw new ExitException();
} }
} }

View file

@ -103,6 +103,10 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
private File appUpdateFile = new File( backupDir, "auto_update.json" ); private File appUpdateFile = new File( backupDir, "auto_update.json" );
private File appUpdateETagFile = new File( backupDir, "auto_update_etag.txt" ); private File appUpdateETagFile = new File( backupDir, "auto_update_etag.txt" );
private boolean disposeNormally = true;
private boolean ranInit = false;
private Thread.UncaughtExceptionHandler previousUncaughtExceptionHandler = null;
private final Lock managerLock = new ReentrantLock(); private final Lock managerLock = new ReentrantLock();
private final Condition scanEndedCond = managerLock.newCondition(); private final Condition scanEndedCond = managerLock.newCondition();
private boolean scanning = false; private boolean scanning = false;
@ -113,8 +117,8 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
private String appURL; private String appURL;
private String appAuthor; private String appAuthor;
private HashMap<File,String> modFileHashes = new HashMap<File,String>(); private Map<File,String> modFileHashes = new HashMap<File,String>();
private HashMap<String,Date> modFileDates = new HashMap<String,Date>(); private Map<String,Date> modFileDates = new HashMap<String,Date>();
private ModDB catalogModDB = new ModDB(); private ModDB catalogModDB = new ModDB();
private ModDB localModDB = new ModDB(); private ModDB localModDB = new ModDB();
@ -261,6 +265,12 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
@Override @Override
public void windowClosed( WindowEvent e ) { public void windowClosed( WindowEvent e ) {
// dispose() was called. // dispose() was called.
// Restore the previous exception handler.
if ( ranInit ) Thread.setDefaultUncaughtExceptionHandler( previousUncaughtExceptionHandler );
if ( !disposeNormally ) return; // Something bad happened. Exit quickly.
ListState<ModFileInfo> tableState = getCurrentModsTableState(); ListState<ModFileInfo> tableState = getCurrentModsTableState();
saveModsTableState( tableState ); saveModsTableState( tableState );
@ -404,9 +414,14 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
} }
/** /**
* Extra initialization that must be called after the constructor. * Extra one-time initialization that must be called after the constructor.
*/ */
public void init() { public void init() {
if ( ranInit ) return;
ranInit = true;
previousUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler( this );
ManagerInitThread initThread = new ManagerInitThread( ManagerInitThread initThread = new ManagerInitThread(
this, this,
@ -421,7 +436,6 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
); );
initThread.setDaemon( true ); initThread.setDaemon( true );
initThread.setPriority( Thread.MIN_PRIORITY ); initThread.setPriority( Thread.MIN_PRIORITY );
initThread.setDefaultUncaughtExceptionHandler( this );
initThread.start(); initThread.start();
} }
@ -524,7 +538,6 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
ModsScanThread scanThread = new ModsScanThread( modFiles, localModDB, this ); ModsScanThread scanThread = new ModsScanThread( modFiles, localModDB, this );
scanThread.setDaemon( true ); scanThread.setDaemon( true );
scanThread.setPriority( Thread.MIN_PRIORITY ); scanThread.setPriority( Thread.MIN_PRIORITY );
scanThread.setDefaultUncaughtExceptionHandler( this );
scanThread.start(); scanThread.start();
} }
@ -726,7 +739,6 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
log.info( "Patching..." ); log.info( "Patching..." );
log.info( "" ); log.info( "" );
ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, false, patchDlg ); ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, false, patchDlg );
patchThread.setDefaultUncaughtExceptionHandler( this );
patchThread.start(); patchThread.start();
patchDlg.setVisible( true ); patchDlg.setVisible( true );
@ -801,7 +813,6 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
File datsDir = new File( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH ) ); File datsDir = new File( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH ) );
DatExtractDialog extractDlg = new DatExtractDialog( this, extractDir, datsDir ); DatExtractDialog extractDlg = new DatExtractDialog( this, extractDir, datsDir );
extractDlg.getWorkerThread().setDefaultUncaughtExceptionHandler( this );
extractDlg.extract(); extractDlg.extract();
extractDlg.setVisible( true ); extractDlg.setVisible( true );
} }
@ -1048,6 +1059,14 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
updateBtn.setEnabled( isUpdateAvailable ); updateBtn.setEnabled( isUpdateAvailable );
} }
/**
* Toggles whether to perform the usual actions after disposal.
*
* Set this to false before an abnormal exit.
*/
public void setDisposeNormally( boolean b ) {
disposeNormally = false;
}
@Override @Override
public void uncaughtException( Thread t, Throwable e ) { public void uncaughtException( Thread t, Throwable e ) {