3333
3434import java .io .File ;
3535import java .io .IOException ;
36+ import java .nio .file .*;
37+ import java .nio .file .attribute .BasicFileAttributes ;
3638import java .util .ArrayList ;
3739import java .util .HashSet ;
3840import java .util .List ;
@@ -139,6 +141,35 @@ public synchronized void start() throws IOException {
139141 }
140142
141143 final List <Downloadable > list = new ArrayList <>();
144+
145+ // Special care must be taken when updating files in the Mac bundle (Fiji.app)
146+ // These files are all signed together as one unit, so individual files can not be modified.
147+ // First we scan the update list to see if there's anything in Fiji.app to update
148+ boolean updateMacApp = false ;
149+ for (final FileObject file : files .toInstallOrUpdate ()) {
150+ if (file .filename .contains ("Fiji.app" )) {
151+ updateMacApp = true ;
152+ break ;
153+ }
154+ }
155+ // If there is a Mac app update, we need to flag ALL installed files in Fiji.app for update
156+ if (updateMacApp ) {
157+ for (FileObject installed : files .installed ()) {
158+ if (installed .filename .contains ("Fiji.app" )) {
159+ installed .stageForUpdate (files , true );
160+ }
161+ }
162+ Path macBundlePath = files .prefix ("Fiji.app" ).toPath ();
163+ File backupAppFile = files .prefix ("Fiji.old.app" );
164+ Path backupAppPath = backupAppFile .toPath ();
165+ // Remove the existing Fiji.old.app if it exists
166+ if (backupAppFile .exists ()) {
167+ deleteDirectory (backupAppPath );
168+ }
169+ // Create a new Fiji.old.app backup
170+ copyDirectory (macBundlePath , backupAppPath );
171+ }
172+
142173 for (final FileObject file : files .toInstallOrUpdate ()) {
143174 final String name = file .filename ;
144175 File saveTo = files .prefixUpdate (name );
@@ -150,7 +181,12 @@ public synchronized void start() throws IOException {
150181 // put them in the update subdir to migrate over as they must be in place
151182 // for the launch itself. So we rename the old file to a backup (.old) and
152183 // set up the download of the new file directly to replace it.
153- if (file .executable ||
184+ //
185+ // However - in the case of Fiji.app files, we already handled the backup process atomically above,
186+ // so all we need to do is update the saveTo to download directly and not to the update directory
187+ if (name .contains ("Fiji.app" )) {
188+ saveTo = files .prefix (name );
189+ } else if (file .executable ||
154190 saveTo .getAbsolutePath ().contains ("config" + File .separator + "jaunch" )) {
155191 saveTo = files .prefix (name );
156192 String oldName = saveTo .getAbsolutePath () + ".old" ;
@@ -416,4 +452,39 @@ protected static boolean moveOutOfTheWay(final File file) {
416452 return file .renameTo (backup );
417453 }
418454
455+ private static void deleteDirectory (Path directory ) throws IOException {
456+ Files .walkFileTree (directory , new SimpleFileVisitor <Path >() {
457+ @ Override
458+ public FileVisitResult visitFile (Path file , BasicFileAttributes attrs ) throws IOException {
459+ Files .delete (file ); // Delete each file
460+ return FileVisitResult .CONTINUE ;
461+ }
462+
463+ @ Override
464+ public FileVisitResult postVisitDirectory (Path dir , IOException exc ) throws IOException {
465+ Files .delete (dir ); // Delete directory after contents
466+ return FileVisitResult .CONTINUE ;
467+ }
468+ });
469+ }
470+
471+ private static void copyDirectory (Path sourceDir , Path targetDir ) throws IOException {
472+ Files .walkFileTree (sourceDir , new SimpleFileVisitor <Path >() {
473+ @ Override
474+ public FileVisitResult preVisitDirectory (Path dir , BasicFileAttributes attrs ) throws IOException {
475+ Path targetPath = targetDir .resolve (sourceDir .relativize (dir ));
476+ if (!Files .exists (targetPath )) {
477+ Files .createDirectories (targetPath );
478+ }
479+ return FileVisitResult .CONTINUE ;
480+ }
481+
482+ @ Override
483+ public FileVisitResult visitFile (Path file , BasicFileAttributes attrs ) throws IOException {
484+ Path targetPath = targetDir .resolve (sourceDir .relativize (file ));
485+ Files .copy (file , targetPath , StandardCopyOption .REPLACE_EXISTING );
486+ return FileVisitResult .CONTINUE ;
487+ }
488+ });
489+ }
419490}
0 commit comments