X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fio%2FBackupFiles.java;h=14c1260bbac561167382e2af1f2f2329ad79a589;hb=80b889f0cca49103e1b20ed806755a0719789906;hp=d18dc35c447f68c526a8d0de6b2389ce4e3ad6e9;hpb=ae1c1f209b535294ab95f3850dc95a87a402f991;p=jalview.git diff --git a/src/jalview/io/BackupFiles.java b/src/jalview/io/BackupFiles.java index d18dc35..14c1260 100644 --- a/src/jalview/io/BackupFiles.java +++ b/src/jalview/io/BackupFiles.java @@ -29,13 +29,21 @@ import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.gui.Desktop; import jalview.gui.JvOptionPane; import jalview.util.MessageManager; +import jalview.util.Platform; /* * BackupFiles used for manipulating (naming rolling/deleting) backup/version files when an alignment or project file is saved. @@ -103,6 +111,117 @@ public class BackupFiles private static final String oldTempFileSuffix = "_oldfile_tobedeleted"; + // thread pool used for completablefutures + private static final ExecutorService executorService = Executors + .newFixedThreadPool(3); + + private static List savesInProgress = new ArrayList<>(); + + private CompletableFuture myFuture = null; + + private boolean addSaveInProgress() + { + if (savesInProgress.contains(this)) + { + return false; + } + else + { + this.setMyFuture(); + savesInProgress.add(this); + return true; + } + } + + private boolean removeSaveInProgress(boolean ret) + { + if (savesInProgress.contains(this)) + { + this.getMyFuture().complete(ret); + // remove all occurrences + while (savesInProgress.remove(this)) + { + } + return true; + } + return false; + } + + private static CompletableFuture getNewFuture() + { + return new CompletableFuture() + { + }; + } + + private CompletableFuture getMyFuture() + { + return this.myFuture; + } + + private void setMyFuture() + { + this.myFuture = getNewFuture(); + } + + public static boolean hasSavesInProgress() + { + boolean has = false; + for (CompletableFuture cf : savesInProgressCompletableFutures(true)) + { + has |= !cf.isDone(); + } + return has; + } + + public static List savesInProgressFiles(boolean all) + { + List files = new ArrayList<>(); + for (BackupFiles bfile : savesInProgress) + { + if (all || !bfile.getMyFuture().isDone()) + files.add(bfile.getFile()); + } + return files; + } + + public static List> savesInProgressCompletableFutures( + boolean all) + { + List> cfs = new ArrayList<>(); + for (BackupFiles bfile : savesInProgress) + { + if (all || !bfile.getMyFuture().isDone()) + cfs.add(bfile.getMyFuture()); + } + return cfs; + } + + public static Future allSaved() + { + CompletableFuture f = new CompletableFuture<>(); + + executorService.submit(() -> { + for (BackupFiles buf : savesInProgress) + { + boolean allSaved = true; + try + { + allSaved &= buf.getMyFuture().get(); + } catch (InterruptedException e) + { + Console.debug("InterruptedException waiting for files to save", + e); + } catch (ExecutionException e) + { + Console.debug("ExecutionException waiting for files to save", e); + } + f.complete(allSaved); + } + }); + return f; + } + public BackupFiles(String filename) { this(new File(filename)); @@ -114,6 +233,10 @@ public class BackupFiles { classInit(); this.file = file; + + // add this file from the save in progress stack + addSaveInProgress(); + BackupFilesPresetEntry bfpe = BackupFilesPresetEntry .getSavedBackupEntry(); this.suffix = bfpe.suffix; @@ -130,30 +253,48 @@ public class BackupFiles { String tempfilename = file.getName(); File tempdir = file.getParentFile(); + tempdir.mkdirs(); + Console.trace( + "BACKUPFILES [file!=null] attempting to create temp file for " + + tempfilename + " in dir " + tempdir); temp = File.createTempFile(tempfilename, TEMP_FILE_EXT + newTempFileSuffix, tempdir); + Console.debug( + "BACKUPFILES using temp file " + temp.getAbsolutePath()); } else { + Console.trace( + "BACKUPFILES [file==null] attempting to create default temp file " + + DEFAULT_TEMP_FILE + " with extension " + + TEMP_FILE_EXT); temp = File.createTempFile(DEFAULT_TEMP_FILE, TEMP_FILE_EXT); } } catch (IOException e) { - Cache.log.error( - "Could not create temp file to save into (IOException)"); + Console.error("Could not create temp file to save to (IOException)"); + Console.error(e.getMessage()); + Console.debug(Cache.getStackTraceString(e)); } catch (Exception e) { - Cache.log.error("Exception ctreating temp file for saving"); + Console.error("Exception creating temp file for saving"); + Console.debug(Cache.getStackTraceString(e)); } this.setTempFile(temp); } public static void classInit() { - setEnabled(Cache.getDefault(ENABLED, true)); + Console.initLogger(); + Console.trace("BACKUPFILES classInit"); + boolean e = Cache.getDefault(ENABLED, !Platform.isJS()); + setEnabled(e); + Console.trace("BACKUPFILES " + (e ? "enabled" : "disabled")); BackupFilesPresetEntry bfpe = BackupFilesPresetEntry .getSavedBackupEntry(); + Console.trace("BACKUPFILES preset scheme " + bfpe.toString()); setConfirmDelete(bfpe.confirmDelete); + Console.trace("BACKUPFILES confirm delete " + bfpe.confirmDelete); } public static void setEnabled(boolean flag) @@ -197,9 +338,9 @@ public class BackupFiles path = this.getTempFile().getCanonicalPath(); } catch (IOException e) { - Cache.log.error( - "IOException when getting Canonical Path of temp file '" - + this.getTempFile().getName() + "'"); + Console.error("IOException when getting Canonical Path of temp file '" + + this.getTempFile().getName() + "'"); + Console.debug(Cache.getStackTraceString(e)); } return path; } @@ -235,7 +376,7 @@ public class BackupFiles || suffix.length() == 0) { // nothing to do - Cache.log.debug("BACKUPFILES rollBackupFiles nothing to do." + ", " + Console.debug("BACKUPFILES rollBackupFiles nothing to do." + ", " + "filename: " + (file != null ? file.getName() : "null") + ", " + "file exists: " + file.exists() + ", " + "enabled: " + enabled + ", " + "max: " + max + ", " + "suffix: '" + suffix @@ -243,22 +384,26 @@ public class BackupFiles return true; } + Console.trace("BACKUPFILES rollBackupFiles starting"); + String dir = ""; File dirFile; try { dirFile = file.getParentFile(); dir = dirFile.getCanonicalPath(); - Cache.log.debug("BACKUPFILES: dir: " + dir); + Console.trace("BACKUPFILES dir: " + dir); } catch (Exception e) { - Cache.log.error( - "Could not get canonical path for file '" + file + "'"); + Console.error("Could not get canonical path for file '" + file + "'"); + Console.error(e.getMessage()); + Console.debug(Cache.getStackTraceString(e)); return false; } String filename = file.getName(); String basename = filename; + Console.trace("BACKUPFILES filename is " + filename); boolean ret = true; // Create/move backups up one @@ -270,11 +415,12 @@ public class BackupFiles File[] backupFiles = dirFile.listFiles(bff); int nextIndexNum = 0; - Cache.log - .debug("BACKUPFILES backupFiles.length: " + backupFiles.length); + Console.trace("BACKUPFILES backupFiles.length: " + backupFiles.length); if (backupFiles.length == 0) { // No other backup files. Just need to move existing file to backupfile_1 + Console.trace( + "BACKUPFILES no existing backup files, setting index to 1"); nextIndexNum = 1; } else @@ -287,6 +433,7 @@ public class BackupFiles if (reverseOrder) { // backup style numbering + Console.trace("BACKUPFILES rolling files in reverse order"); int tempMax = noMax ? -1 : max; // noMax == true means no limits @@ -318,6 +465,7 @@ public class BackupFiles // no "oldest" file to delete previousFile = backupfile_n; fileToBeDeleted = null; + Console.trace("BACKUPFILES No oldest file to delete"); continue; } @@ -328,6 +476,10 @@ public class BackupFiles File replacementFile = backupfile_n; long fileToBeDeletedLMT = fileToBeDeleted.lastModified(); long replacementFileLMT = replacementFile.lastModified(); + Console.trace("BACKUPFILES fileToBeDeleted is " + + fileToBeDeleted.getAbsolutePath()); + Console.trace("BACKUPFILES replacementFile is " + + backupfile_n.getAbsolutePath()); try { @@ -340,7 +492,7 @@ public class BackupFiles .format(fileToBeDeletedLMT); String replacementFileLMTString = sdf .format(replacementFileLMT); - Cache.log.warn("WARNING! I am set to delete backupfile " + Console.warn("WARNING! I am set to delete backupfile " + fileToBeDeleted.getName() + " has modification time " + fileToBeDeletedLMTString @@ -351,6 +503,11 @@ public class BackupFiles boolean delete = confirmNewerDeleteFile(fileToBeDeleted, replacementFile, true); + Console.trace("BACKUPFILES " + + (delete ? "confirmed" : "not") + " deleting file " + + fileToBeDeleted.getAbsolutePath() + + " which is newer than " + + replacementFile.getAbsolutePath()); if (delete) { @@ -359,21 +516,27 @@ public class BackupFiles } else { + Console.debug("BACKUPFILES moving " + + fileToBeDeleted.getAbsolutePath() + " to " + + oldestTempFile.getAbsolutePath()); moveFileToFile(fileToBeDeleted, oldestTempFile); } } else { + Console.debug("BACKUPFILES going to move " + + fileToBeDeleted.getAbsolutePath() + " to " + + oldestTempFile.getAbsolutePath()); moveFileToFile(fileToBeDeleted, oldestTempFile); addDeleteFile(oldestTempFile); } } catch (Exception e) { - Cache.log.error( + Console.error( "Error occurred, probably making new temp file for '" + fileToBeDeleted.getName() + "'"); - Cache.log.error(e.getStackTrace()); + Console.error(Cache.getStackTraceString(e)); } // reset @@ -388,7 +551,9 @@ public class BackupFiles { if (previousFile != null) { - ret = ret && moveFileToFile(backupfile_n, previousFile); + // using boolean '&' instead of '&&' as don't want moveFileToFile + // attempt to be conditional (short-circuit) + ret = ret & moveFileToFile(backupfile_n, previousFile); } } @@ -413,13 +578,13 @@ public class BackupFiles } bfsb.append(backupFiles[i].getName()); } - Cache.log.debug("BACKUPFILES backupFiles: " + bfsb.toString()); + Console.trace("BACKUPFILES backupFiles: " + bfsb.toString()); // noMax == true means keep all backup files if ((!noMax) && bfTreeMap.size() >= max) { - Cache.log.debug("BACKUPFILES noMax: " + noMax + ", " + "max: " - + max + ", " + "bfTreeMap.size(): " + bfTreeMap.size()); + Console.trace("BACKUPFILES noMax: " + noMax + ", " + "max: " + max + + ", " + "bfTreeMap.size(): " + bfTreeMap.size()); // need to delete some files to keep number of backups to designated // max. // Note that if the suffix is not numbered then do not delete any @@ -428,7 +593,7 @@ public class BackupFiles int numToDelete = suffix.indexOf(NUM_PLACEHOLDER) > -1 ? bfTreeMap.size() - max + 1 : 0; - Cache.log.debug("BACKUPFILES numToDelete: " + numToDelete); + Console.trace("BACKUPFILES numToDelete: " + numToDelete); // the "replacement" file is the latest backup file being kept (it's // not replacing though) File replacementFile = numToDelete < backupFiles.length @@ -441,7 +606,7 @@ public class BackupFiles File fileToBeDeleted = backupFiles[i]; boolean delete = true; - Cache.log.debug( + Console.trace( "BACKUPFILES fileToBeDeleted: " + fileToBeDeleted); boolean newer = false; @@ -458,7 +623,7 @@ public class BackupFiles String replacementFileLMTString = sdf .format(replacementFileLMT); - Cache.log.warn("WARNING! I am set to delete backupfile '" + Console.warn("WARNING! I am set to delete backupfile '" + fileToBeDeleted.getName() + "' has modification time " + fileToBeDeletedLMTString @@ -473,14 +638,14 @@ public class BackupFiles { // User has confirmed delete -- no need to add it to the list fileToBeDeleted.delete(); - Cache.log.debug("BACKUPFILES deleting fileToBeDeleted: " + Console.debug("BACKUPFILES deleting fileToBeDeleted: " + fileToBeDeleted); delete = false; } else { // keeping file, nothing to do! - Cache.log.debug("BACKUPFILES keeping fileToBeDeleted: " + Console.debug("BACKUPFILES keeping fileToBeDeleted: " + fileToBeDeleted); } } @@ -488,7 +653,7 @@ public class BackupFiles if (delete) { addDeleteFile(fileToBeDeleted); - Cache.log.debug("BACKUPFILES addDeleteFile(fileToBeDelted): " + Console.debug("BACKUPFILES addDeleteFile(fileToBeDeleted): " + fileToBeDeleted); } @@ -504,15 +669,17 @@ public class BackupFiles String latestBackupFilename = dir + File.separatorChar + BackupFilenameParts.getBackupFilename(nextIndexNum, basename, suffix, digits); - Cache.log.debug("BACKUPFILES Moving old file [" + file + Console.trace("BACKUPFILES Moving old file [" + file + "] to latestBackupFilename [" + latestBackupFilename + "]"); - ret |= moveFileToFile(file, new File(latestBackupFilename)); - Cache.log.debug("BACKUPFILES moving " + latestBackupFilename + " to " - + file + " was " + (ret ? "" : "NOT ") + "successful"); - + // using boolean '&' instead of '&&' as don't want moveFileToFile attempt to + // be conditional (short-circuit) + ret = ret & moveFileToFile(file, new File(latestBackupFilename)); + Console.debug( + "BACKUPFILES moving " + file + " to " + latestBackupFilename + + " was " + (ret ? "" : "NOT ") + "successful"); if (tidyUp) { - Cache.log.debug("BACKUPFILES tidying up files"); + Console.debug("BACKUPFILES tidying up files"); tidyUpFiles(); } @@ -568,7 +735,7 @@ public class BackupFiles saveFile = nextTempFile(ftbd.getName(), ftbd.getParentFile()); } catch (Exception e) { - Cache.log.error( + Console.error( "Error when confirming to keep backup file newer than other backup files."); e.printStackTrace(); } @@ -588,12 +755,15 @@ public class BackupFiles MessageManager.getString("label.delete"), MessageManager.getString("label.rename") }; - confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop, - messageSB.toString(), - MessageManager.getString("label.backupfiles_confirm_delete"), - // "Confirm delete" - JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE, - null, options, options[0]); + confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION + : JvOptionPane.showOptionDialog(Desktop.desktop, + messageSB.toString(), + MessageManager.getString( + "label.backupfiles_confirm_delete"), + // "Confirm delete" + JvOptionPane.YES_NO_OPTION, + JvOptionPane.WARNING_MESSAGE, null, options, + options[0]); } else { @@ -613,12 +783,15 @@ public class BackupFiles MessageManager.getString("label.delete"), MessageManager.getString("label.keep") }; - confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop, - messageSB.toString(), - MessageManager.getString("label.backupfiles_confirm_delete"), - // "Confirm delete" - JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE, - null, options, options[0]); + confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION + : JvOptionPane.showOptionDialog(Desktop.desktop, + messageSB.toString(), + MessageManager.getString( + "label.backupfiles_confirm_delete"), + // "Confirm delete" + JvOptionPane.YES_NO_OPTION, + JvOptionPane.WARNING_MESSAGE, null, options, + options[0]); } // return should be TRUE if file is to be deleted @@ -651,12 +824,14 @@ public class BackupFiles // "(modified {0}, size {1})" } - int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop, - messageSB.toString(), - MessageManager - .getString("label.backupfiles_confirm_delete"), - // "Confirm delete" - JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE); + int confirmButton = Platform.isHeadless() ? JvOptionPane.YES_OPTION + : JvOptionPane.showConfirmDialog(Desktop.desktop, + messageSB.toString(), + MessageManager.getString( + "label.backupfiles_confirm_delete"), + // "Confirm delete" + JvOptionPane.YES_NO_OPTION, + JvOptionPane.WARNING_MESSAGE); doDelete = (confirmButton == JvOptionPane.YES_OPTION); } @@ -670,10 +845,10 @@ public class BackupFiles for (int i = 0; i < deleteFiles.size(); i++) { File fileToDelete = deleteFiles.get(i); - Cache.log.debug( - "BACKUPFILES deleting fileToDelete:" + fileToDelete); + Console.trace("BACKUPFILES about to delete fileToDelete:" + + fileToDelete); fileToDelete.delete(); - Cache.log.warn("deleting '" + fileToDelete.getName() + "'"); + Console.warn("deleted '" + fileToDelete.getName() + "'"); } } @@ -721,7 +896,6 @@ public class BackupFiles boolean okay = roll && rename; if (!okay) { - boolean yesno = false; StringBuilder messageSB = new StringBuilder(); messageSB.append(MessageManager.getString( "label.backupfiles_confirm_save_file_backupfiles_roll_wrong")); @@ -746,13 +920,20 @@ public class BackupFiles "label.backupfiles_confirm_save_new_saved_file_not_ok")); // "The new saved file might not be okay." } - - int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop, - messageSB.toString(), - MessageManager - .getString("label.backupfiles_confirm_save_file"), - // "Confirm save file" - JvOptionPane.OK_OPTION, JvOptionPane.WARNING_MESSAGE); + if (messageSB.length() > 0) + { + messageSB.append("\n"); + } + messageSB + .append(MessageManager.getString("label.continue_operation")); + + int confirmButton = Platform.isHeadless() ? JvOptionPane.OK_OPTION + : JvOptionPane.showConfirmDialog(Desktop.desktop, + messageSB.toString(), + MessageManager.getString( + "label.backupfiles_confirm_save_file"), + // "Confirm save file" + JvOptionPane.OK_OPTION, JvOptionPane.WARNING_MESSAGE); okay = confirmButton == JvOptionPane.OK_OPTION; } if (okay) @@ -760,6 +941,9 @@ public class BackupFiles tidyUpFiles(); } + // remove this file from the save in progress stack + removeSaveInProgress(rename); + return rename; } @@ -776,8 +960,7 @@ public class BackupFiles dirFile = file.getParentFile(); } catch (Exception e) { - Cache.log.error( - "Could not get canonical path for file '" + file + "'"); + Console.error("Could not get canonical path for file '" + file + "'"); return new TreeMap<>(); } @@ -818,36 +1001,52 @@ public class BackupFiles int pos = deleteFiles.indexOf(fileToBeDeleted); if (pos > -1) { + Console.debug("BACKUPFILES not adding file " + + fileToBeDeleted.getAbsolutePath() + + " to the delete list (already at index" + pos + ")"); return true; } else { + Console.debug("BACKUPFILES adding file " + + fileToBeDeleted.getAbsolutePath() + " to the delete list"); deleteFiles.add(fileToBeDeleted); } return ret; } + public File getFile() + { + return file; + } + public static boolean moveFileToFile(File oldFile, File newFile) { + Console.initLogger(); boolean ret = false; Path oldPath = Paths.get(oldFile.getAbsolutePath()); Path newPath = Paths.get(newFile.getAbsolutePath()); try { // delete destination file - not usually necessary but Just In Case... + Console.trace("BACKUPFILES deleting " + newFile.getAbsolutePath()); newFile.delete(); + Console.trace("BACKUPFILES moving " + oldFile.getAbsolutePath() + + " to " + newFile.getAbsolutePath()); Files.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING); ret = true; + Console.trace("BACKUPFILES move seems to have succeeded"); } catch (IOException e) { - Cache.log.warn("Could not move file '" + oldPath.toString() + "' to '" + Console.warn("Could not move file '" + oldPath.toString() + "' to '" + newPath.toString() + "'"); - Cache.log.error(e.getStackTrace()); + Console.error(e.getMessage()); + Console.debug(Cache.getStackTraceString(e)); ret = false; } catch (Exception e) { - Cache.log.error(e.getMessage()); - Cache.log.error(e.getStackTrace()); + Console.error(e.getMessage()); + Console.debug(Cache.getStackTraceString(e)); ret = false; } return ret;