From 43d48aa979a1e316af5ba0445412fe8926ce983b Mon Sep 17 00:00:00 2001 From: Keuin Date: Mon, 25 Jan 2021 02:07:54 +0800 Subject: Perform clean-up after deleting a backup. --- .../java/com/keuin/kbackupfabric/KBCommands.java | 3 +- .../backup/incremental/ObjectCollection2.java | 5 +- .../incremental/ObjectCollectionSerializer.java | 76 +++++++++------------- .../manager/IncrementalBackupStorageManager.java | 25 ++++--- .../serializer/IncBackupInfoSerializer.java | 4 ++ .../incremental/serializer/SavedIncBackupV1.java | 4 ++ .../kbackupfabric/operation/DeleteOperation.java | 71 ++++++++++++++------ .../method/ConfiguredIncrementalBackupMethod.java | 2 +- 8 files changed, 112 insertions(+), 78 deletions(-) diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommands.java b/src/main/java/com/keuin/kbackupfabric/KBCommands.java index 9c887fd..01231be 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBCommands.java +++ b/src/main/java/com/keuin/kbackupfabric/KBCommands.java @@ -407,8 +407,9 @@ public final class KBCommands { return "(Incremental) " + info.getBackupName() + ", " + DateUtil.getString(info.getBackupTime()) + ((info.getTotalSizeBytes() > 0) ? - ("size: " + BackupFilesystemUtil.getFriendlyFileSizeString(info.getTotalSizeBytes())) : ""); + (" size: " + BackupFilesystemUtil.getFriendlyFileSizeString(info.getTotalSizeBytes())) : ""); } catch (IOException e) { + e.printStackTrace(); return "(Incremental) " + backupFile.getName(); } } diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollection2.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollection2.java index 8d8eb14..7a198af 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollection2.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollection2.java @@ -3,12 +3,9 @@ package com.keuin.kbackupfabric.backup.incremental; import java.io.Serializable; import java.util.*; -/** - * This class must be in package `com.keuin.kbackupfabric.util.backup.incremental.ObjectCollection`, - * or it will not be compatible with old backups. - */ public class ObjectCollection2 implements Serializable { + private static final long serialVersionUID = 6651743898782813296L; private final String name; private final Map elements; private final Map subCollections; diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionSerializer.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionSerializer.java index fa411a0..3be1bfe 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionSerializer.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionSerializer.java @@ -1,11 +1,17 @@ package com.keuin.kbackupfabric.backup.incremental; -import org.jetbrains.annotations.NotNull; +import com.keuin.kbackupfabric.backup.incremental.serializer.IncBackupInfoSerializer; +import com.keuin.kbackupfabric.backup.incremental.serializer.SavedIncrementalBackup; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Iterator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -19,17 +25,18 @@ public class ObjectCollectionSerializer { */ @Deprecated public static ObjectCollection2 fromFile(File file) throws IOException { - Objects.requireNonNull(file); - ObjectCollection2 collection; - try (FileInputStream fileInputStream = new FileInputStream(file)) { - try (ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { - collection = (ObjectCollection2) objectInputStream.readObject(); - } catch (ClassNotFoundException ignored) { - // this should not happen - return null; - } - } - return collection; + throw new RuntimeException("This method has been depreciated."); +// Objects.requireNonNull(file); +// ObjectCollection2 collection; +// try (FileInputStream fileInputStream = new FileInputStream(file)) { +// try (ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { +// collection = (ObjectCollection2) objectInputStream.readObject(); +// } catch (ClassNotFoundException ignored) { +// // this should not happen +// return null; +// } +// } +// return collection; } /** @@ -46,38 +53,19 @@ public class ObjectCollectionSerializer { } public static Iterable fromDirectory(File directory) throws IOException { - if (!directory.isDirectory()) { throw new IllegalArgumentException("Given directory is invalid."); } - return new Iterable() { - private final Iterator iter = new Iterator() { - private final Iterator i = Files.walk(directory.toPath(), 1).filter(p -> { - File f = p.toFile(); - return f.isFile() && f.getName().endsWith(".kbi"); - }).iterator(); - - @Override - public boolean hasNext() { - return i.hasNext(); - } - - @Override - public ObjectCollection2 next() { - try { - return fromFile(i.next().toFile()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }; - - @NotNull - @Override - public Iterator iterator() { - return iter; - } - }; - + List pathList = new ArrayList<>(); + Files.walk(directory.toPath(), 1).filter(p -> { + File f = p.toFile(); + return f.isFile() && f.getName().endsWith(".kbi"); + }).forEach(pathList::add); + List objectList = new ArrayList<>(); + for (Path path : pathList) { + SavedIncrementalBackup info = IncBackupInfoSerializer.fromFile(path.toFile()); + objectList.add(info.getObjectCollection()); + } + return Collections.unmodifiableCollection(objectList); } } diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java index ad7287f..0a99bf0 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import static org.apache.commons.io.FileUtils.forceDelete; @@ -76,32 +77,38 @@ public class IncrementalBackupStorageManager { /** * Delete all files in the specific collection, from the storage base. * - * @param collection the collection containing files to be deleted. - * @param collectionBasePath the collection base path. + * @param collection the collection containing files to be deleted. + * @return files deleted * @throws IOException I/O error. */ - public void deleteObjectCollection(ObjectCollection2 collection, File collectionBasePath) throws IOException { - deleteObjectCollection(collection, collectionBasePath, Collections.emptySet()); + public int deleteObjectCollection(ObjectCollection2 collection) throws IOException { + return deleteObjectCollection(collection, Collections.emptySet()); } /** * Delete a collection from the storage base, optionally preserving files used by other backups. * * @param collection the collection containing files to be deleted. - * @param collectionBasePath the collection base path. * @param otherExistingCollections other collections (not to be deleted) in this base. Files exist in these collections will not be deleted. + * @return files deleted */ - public void deleteObjectCollection(ObjectCollection2 collection, File collectionBasePath, - Iterable otherExistingCollections) { + public int deleteObjectCollection(ObjectCollection2 collection, + Iterable otherExistingCollections) { Iterator iter = new ObjectCollectionIterator(collection); Set unusedElementSet = new HashSet<>(); iter.forEachRemaining(unusedElementSet::add); otherExistingCollections.forEach(col -> new ObjectCollectionIterator(col).forEachRemaining(unusedElementSet::remove)); + AtomicInteger deleteCount = new AtomicInteger(); unusedElementSet.forEach(ele -> { File file = new File(backupStorageBase.toFile(), ele.getIdentifier().getIdentification()); - if (!file.delete()) - LOGGER.warning("Failed to delete unused file " + file.getName()); + if (file.exists()) { + if (file.delete()) + deleteCount.incrementAndGet(); + else + LOGGER.warning("Failed to delete unused file " + file.getName()); + } }); + return deleteCount.get(); } /** diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/IncBackupInfoSerializer.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/IncBackupInfoSerializer.java index d084aef..e79c02f 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/IncBackupInfoSerializer.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/IncBackupInfoSerializer.java @@ -41,8 +41,12 @@ public class IncBackupInfoSerializer { } } catch (ClassNotFoundException e) { // this should not happen + e.printStackTrace(); throw new RuntimeException(e); } + } catch (Exception e) { + System.err.println("Failed to deserialize file " + file.getName()); + throw e; } } diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/SavedIncBackupV1.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/SavedIncBackupV1.java index bd2d4eb..e34efdb 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/SavedIncBackupV1.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/serializer/SavedIncBackupV1.java @@ -6,10 +6,12 @@ import com.keuin.kbackupfabric.util.DateUtil; import java.io.Serializable; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Objects; public class SavedIncBackupV1 implements SavedIncrementalBackup, Serializable { + private static final long serialVersionUID = 5798490056955510205L; private final ObjectCollection2 objectCollection2; private final String backupName; private final ZonedDateTime backupTime; @@ -17,6 +19,8 @@ public class SavedIncBackupV1 implements SavedIncrementalBackup, Serializable { private final long increasedSizeBytes; private final int filesAdded; private final int totalFiles; + // this field is depreciated and kept only for serialization compatibility + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); public SavedIncBackupV1(ObjectCollection2 objectCollection2, String backupName, ZonedDateTime backupTime, long totalSizeBytes, long increasedSizeBytes, int filesAdded, int totalFiles) { this.totalFiles = totalFiles; diff --git a/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java b/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java index 7ebff0a..7bfb0b7 100644 --- a/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java +++ b/src/main/java/com/keuin/kbackupfabric/operation/DeleteOperation.java @@ -1,5 +1,11 @@ package com.keuin.kbackupfabric.operation; +import com.keuin.kbackupfabric.backup.BackupFilesystemUtil; +import com.keuin.kbackupfabric.backup.incremental.ObjectCollection2; +import com.keuin.kbackupfabric.backup.incremental.ObjectCollectionSerializer; +import com.keuin.kbackupfabric.backup.incremental.manager.IncrementalBackupStorageManager; +import com.keuin.kbackupfabric.backup.incremental.serializer.IncBackupInfoSerializer; +import com.keuin.kbackupfabric.backup.incremental.serializer.SavedIncrementalBackup; import com.keuin.kbackupfabric.backup.suggestion.BackupNameSuggestionProvider; import com.keuin.kbackupfabric.operation.abstracts.InvokableAsyncBlockingOperation; import com.keuin.kbackupfabric.util.PrintUtil; @@ -9,15 +15,17 @@ import net.minecraft.server.command.ServerCommandSource; import java.io.File; import java.io.IOException; +import java.util.logging.Logger; import static com.keuin.kbackupfabric.backup.BackupFilesystemUtil.getBackupSaveDirectory; +import static com.keuin.kbackupfabric.backup.BackupFilesystemUtil.getIncrementalBackupBaseDirectory; import static com.keuin.kbackupfabric.util.PrintUtil.msgErr; import static com.keuin.kbackupfabric.util.PrintUtil.msgInfo; import static org.apache.commons.io.FileUtils.forceDelete; public class DeleteOperation extends InvokableAsyncBlockingOperation { - //private static final Logger LOGGER = LogManager.getLogger(); + private static final Logger LOGGER = Logger.getLogger(DeleteOperation.class.getName()); private final String backupFileName; private final CommandContext context; @@ -39,25 +47,50 @@ public class DeleteOperation extends InvokableAsyncBlockingOperation { } private void delete() { - MinecraftServer server = context.getSource().getMinecraftServer(); - PrintUtil.info("Deleting backup file " + this.backupFileName); - File backupFile = new File(getBackupSaveDirectory(server), backupFileName); - int tryCounter = 0; - do { - if (tryCounter == 5) { - String msg = "Failed to delete file " + backupFileName; - PrintUtil.error(msg); - msgErr(context, msg); - return; + try { + MinecraftServer server = context.getSource().getMinecraftServer(); + PrintUtil.info("Deleting backup file " + this.backupFileName); + File backupFile = new File(getBackupSaveDirectory(server), backupFileName); + SavedIncrementalBackup incrementalBackup = null; + if (backupFile.getName().endsWith(".kbi")) { + incrementalBackup = IncBackupInfoSerializer.fromFile(backupFile); } - try { - if (!backupFile.delete()) - forceDelete(backupFile); - } catch (SecurityException | NullPointerException | IOException ignored) { + + // remove .zip or .kbi file + PrintUtil.info("Deleting file " + backupFileName + "..."); + int tryCounter = 0; + do { + if (tryCounter == 5) { + String msg = "Failed to delete file " + backupFileName; + PrintUtil.error(msg); + msgErr(context, msg); + return; + } + try { + if (!backupFile.delete()) + forceDelete(backupFile); + } catch (SecurityException | NullPointerException | IOException ignored) { + } + ++tryCounter; + } while (backupFile.exists()); + + + // If it is an incremental backup, do clean-up + if (incrementalBackup != null) { + PrintUtil.info("Cleaning up..."); + IncrementalBackupStorageManager manager = + new IncrementalBackupStorageManager(getIncrementalBackupBaseDirectory(server).toPath()); + Iterable backups = ObjectCollectionSerializer + .fromDirectory(BackupFilesystemUtil + .getBackupSaveDirectory(context.getSource().getMinecraftServer())); + int deleted = manager.deleteObjectCollection(incrementalBackup.getObjectCollection(), backups); + PrintUtil.info("Deleted " + deleted + " unused file(s)."); } - ++tryCounter; - } while (backupFile.exists()); - PrintUtil.info("Successfully deleted backup file " + this.backupFileName); - msgInfo(context, "Successfully deleted backup file " + this.backupFileName); + + PrintUtil.info("Successfully deleted backup file " + this.backupFileName); + msgInfo(context, "Successfully deleted backup file " + this.backupFileName); + } catch (IOException e) { + LOGGER.severe("Failed to delete backup: " + e); + } } } diff --git a/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java b/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java index ffcc000..5cd58aa 100644 --- a/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java +++ b/src/main/java/com/keuin/kbackupfabric/operation/backup/method/ConfiguredIncrementalBackupMethod.java @@ -117,7 +117,7 @@ public class ConfiguredIncrementalBackupMethod implements ConfiguredBackupMethod // so we perform a clean here // perform a clean-up Iterable backups = ObjectCollectionSerializer.fromDirectory(new File(backupIndexFileSaveDirectory)); - storageManager.deleteObjectCollection(collection, levelPathFile, backups); + storageManager.deleteObjectCollection(collection, backups); } catch (IOException e) { LOGGER.warning("An exception occurred while cleaning up: " + e); } -- cgit v1.2.3