@@ -392,6 +392,7 @@ private void switchToNewLauncher() {
392
392
return ;
393
393
}
394
394
// New process seems to be up and running; we are done. Whew!
395
+ startExeRenameProcess (appDir .toPath ());
395
396
appService .getContext ().dispose ();
396
397
System .exit (0 );
397
398
}
@@ -400,6 +401,88 @@ private void switchToNewLauncher() {
400
401
}
401
402
}
402
403
404
+ /**
405
+ * Helper method to rename the current exe to a backup version, to discourage
406
+ * accidental use of a launcher that is incompatible with Java 21, for example
407
+ */
408
+ private static void startExeRenameProcess (Path appDir ) throws IOException {
409
+ Path originalExe ;
410
+ if (OS_WIN ) {
411
+ if (ARCH .equals ("x32" )) {
412
+ originalExe = appDir .resolve ("ImageJ-win32.exe" );
413
+ } else {
414
+ originalExe = appDir .resolve ("ImageJ-win64.exe" );
415
+ }
416
+ } else if (OS_LINUX ) {
417
+ originalExe = appDir .resolve ("ImageJ-linux64" );
418
+ } else if (OS_MACOS ) {
419
+ originalExe = appDir .resolve ("Contents" ).resolve ("MacOS" ).resolve ("ImageJ-macosx" );
420
+ } else {
421
+ throw new RuntimeException ("Unknown operating system" );
422
+ }
423
+
424
+ Path renamedExe = Paths .get (originalExe + ".old" );
425
+ // Copy the previous executable to a backup .old version
426
+ Files .copy (originalExe , renamedExe );
427
+
428
+ // We can't actually remove the previous executable while this process is
429
+ // running, since it was used to launch this JVM. So we need to start a
430
+ // sub-process that will continually try to delete the file until it
431
+ // succeeds.
432
+ final int checkIntervalMs = 1000 ;
433
+ final int numTries = 20 ;
434
+ ProcessBuilder pb ;
435
+ String pathToDelete = originalExe .toFile ().getAbsolutePath ();
436
+
437
+ if (OS_WIN ) {
438
+ // Windows implementation using PowerShell
439
+ pb = new ProcessBuilder (
440
+ "powershell.exe" ,
441
+ "-Command" ,
442
+ "$tries = 0; " +
443
+ "while ((Test-Path '" + pathToDelete + "') -and ($tries -lt " + numTries + ")) { " +
444
+ "try { " +
445
+ " Remove-Item -Path '" + pathToDelete + "' -Force -ErrorAction Stop; " +
446
+ " Write-Host 'File deleted successfully.'; " +
447
+ " break;" +
448
+ "} catch { " +
449
+ " $tries++; " +
450
+ " Write-Host \" Attempt $tries of " + numTries + " failed...\" ; " +
451
+ " if ($tries -eq " + numTries + ") { Write-Host 'Max attempts reached. Exiting.'; break; } " +
452
+ " Start-Sleep -Milliseconds " + checkIntervalMs + " " +
453
+ "} }"
454
+ );
455
+ } else {
456
+ // Unix/Linux/Mac implementation using bash
457
+ pb = new ProcessBuilder (
458
+ "bash" ,
459
+ "-c" ,
460
+ "tries=0; " +
461
+ "while [ -f \" " + pathToDelete + "\" ] && [$tries -lt " + numTries + " ]; do " +
462
+ " if rm -f \" " + pathToDelete + "\" 2>/dev/null; then " +
463
+ " echo \" File deleted successfully.\" ;" +
464
+ " break; " +
465
+ " else " +
466
+ " tries=$((tries+1)); " +
467
+ " echo \" Attempt $tries of " + numTries + " failed...\" ;" +
468
+ " if [ $tries -eq " + numTries + " ]; then " +
469
+ " echo \" Max attempts reached. Exiting.\" ; " +
470
+ " break; " +
471
+ " fi; " +
472
+ " fi; " +
473
+ " sleep " + (checkIntervalMs / 1000.0 ) + "; " +
474
+ "done"
475
+ );
476
+ }
477
+
478
+ // Redirect process output (optional - for debugging)
479
+ // pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
480
+ // pb.redirectError(ProcessBuilder.Redirect.INHERIT);
481
+
482
+ // Start the process
483
+ Process process = pb .start ();
484
+ }
485
+
403
486
/** Implores the user to report a bug relating to new launcher switch-over. */
404
487
private void askForBugReport (
405
488
Logger log , String appTitle , String appSlug , Exception exc )
0 commit comments