pt III, slowly moving to the new file handling api (File -> Path)

2.x-1.17
Szum123321 2022-06-17 22:43:41 +02:00
parent a85db99d82
commit b4597f6f1f
8 changed files with 138 additions and 121 deletions

View File

@ -20,7 +20,7 @@ package net.szum123321.textile_backup;
import net.szum123321.textile_backup.core.restore.AwaitThread; import net.szum123321.textile_backup.core.restore.AwaitThread;
import java.io.File; import java.nio.file.Path;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -35,6 +35,6 @@ public class Statics {
public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true); public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true);
public static boolean disableWatchdog = false; public static boolean disableWatchdog = false;
public static AwaitThread restoreAwaitThread = null; public static AwaitThread restoreAwaitThread = null;
public static Optional<File> untouchableFile = Optional.empty(); public static Optional<Path> untouchableFile = Optional.empty();
public static boolean disableTMPFiles = false; public static boolean disableTMPFiles = false;
} }

View File

@ -21,7 +21,6 @@ package net.szum123321.textile_backup.commands.manage;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup; import net.szum123321.textile_backup.TextileBackup;
@ -31,11 +30,13 @@ import net.szum123321.textile_backup.Statics;
import net.szum123321.textile_backup.commands.FileSuggestionProvider; import net.szum123321.textile_backup.commands.FileSuggestionProvider;
import net.szum123321.textile_backup.core.Utilities; import net.szum123321.textile_backup.core.Utilities;
import java.io.File; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream;
public class DeleteCommand { public class DeleteCommand {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
@ -57,28 +58,28 @@ public class DeleteCommand {
throw CommandExceptions.DATE_TIME_PARSE_COMMAND_EXCEPTION_TYPE.create(e); throw CommandExceptions.DATE_TIME_PARSE_COMMAND_EXCEPTION_TYPE.create(e);
} }
File root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer())); Path root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer()));
Optional<File> optionalFile = Arrays.stream(root.listFiles()) try (Stream<Path> stream = Files.list(root)) {
.filter(Utilities::isValidBackup) stream.filter(Utilities::isValidBackup)
.filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime)) .filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime))
.findFirst(); .findFirst().ifPresent(file -> {
if(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(file)) {
try {
Files.delete(file);
log.sendInfo(source, "File {} successfully deleted!", file);
if(optionalFile.isPresent()) { if(source.isExecutedByPlayer())
if(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(optionalFile.get())) { log.info("Player {} deleted {}.", source.getPlayer().getName(), file);
if(optionalFile.get().delete()) { } catch (IOException e) {
log.sendInfo(source, "File {} successfully deleted!", optionalFile.get().getName()); log.sendError(source, "Something went wrong while deleting file!");
}
if(source.getEntity() instanceof PlayerEntity) } else {
log.info("Player {} deleted {}.", source.getPlayer().getName(), optionalFile.get().getName()); log.sendError(source, "Couldn't delete the file because it's being restored right now.");
} else { log.sendHint(source, "If you want to abort restoration then use: /backup killR");
log.sendError(source, "Something went wrong while deleting file!"); }
} });
} else { } catch (IOException ignored) {
log.sendError(source, "Couldn't delete the file because it's being restored right now.");
log.sendHint(source, "If you want to abort restoration then use: /backup killR");
}
} else {
log.sendError(source, "Couldn't find file by this name."); log.sendError(source, "Couldn't find file by this name.");
log.sendHint(source, "Maybe try /backup list"); log.sendHint(source, "Maybe try /backup list");
} }

View File

@ -82,7 +82,7 @@ public class RestoreBackupCommand {
Optional<RestoreHelper.RestoreableFile> backupFile = RestoreHelper.findFileAndLockIfPresent(dateTime, source.getServer()); Optional<RestoreHelper.RestoreableFile> backupFile = RestoreHelper.findFileAndLockIfPresent(dateTime, source.getServer());
if(backupFile.isPresent()) { if(backupFile.isPresent()) {
log.info("Found file to restore {}", backupFile.get().getFile().getName()); log.info("Found file to restore {}", backupFile.get().getFile().getFileName().toString());
} else { } else {
log.sendInfo(source, "No file created on {} was found!", dateTime.format(Statics.defaultDateTimeFormatter)); log.sendInfo(source, "No file created on {} was found!", dateTime.format(Statics.defaultDateTimeFormatter));

View File

@ -60,19 +60,22 @@ public class Utilities {
return ((MinecraftServerSessionAccessor)server).getSession().getDirectoryName(); return ((MinecraftServerSessionAccessor)server).getSession().getDirectoryName();
} }
public static File getWorldFolder(MinecraftServer server) { public static Path getWorldFolder(MinecraftServer server) {
return ((MinecraftServerSessionAccessor)server) return ((MinecraftServerSessionAccessor)server)
.getSession() .getSession()
.getWorldDirectory(World.OVERWORLD) .getWorldDirectory(World.OVERWORLD);
.toFile();
} }
public static File getBackupRootPath(String worldName) { public static Path getBackupRootPath(String worldName) {
File path = new File(config.get().path).getAbsoluteFile(); Path path = Path.of(config.get().path).toAbsolutePath();
if (config.get().perWorldBackup) path = path.toPath().resolve(worldName).toFile(); if (config.get().perWorldBackup) path = path.resolve(worldName);
if (!path.exists()) path.mkdirs(); try {
Files.createDirectories(path);
} catch (IOException e) {
//I REALLY shouldn't be handling this here
}
return path; return path;
} }
@ -81,7 +84,7 @@ public class Utilities {
boolean flag = false; boolean flag = false;
Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir")); Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir"));
if( if(
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server)) >= FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server).toFile()) >=
tmp_dir.toFile().getUsableSpace() tmp_dir.toFile().getUsableSpace()
) { ) {
log.error("Not enough space left in TMP directory! ({})", tmp_dir); log.error("Not enough space left in TMP directory! ({})", tmp_dir);
@ -130,11 +133,11 @@ public class Utilities {
.findAny(); .findAny();
} }
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(File f) { public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(Path f) {
return getArchiveExtension(f.getName()); return getArchiveExtension(f.getFileName().toString());
} }
public static Optional<LocalDateTime> getFileCreationTime(File file) { public static Optional<LocalDateTime> getFileCreationTime(Path file) {
LocalDateTime creationTime = null; LocalDateTime creationTime = null;
if(getArchiveExtension(file).isPresent()) { if(getArchiveExtension(file).isPresent()) {
@ -143,7 +146,7 @@ public class Utilities {
try { try {
creationTime = LocalDateTime.from( creationTime = LocalDateTime.from(
Utilities.getDateTimeFormatter().parse( Utilities.getDateTimeFormatter().parse(
file.getName().split(fileExtension)[0].split("#")[0] file.getFileName().toString().split(fileExtension)[0].split("#")[0]
) )
); );
} catch (Exception ignored) {} } catch (Exception ignored) {}
@ -152,7 +155,7 @@ public class Utilities {
try { try {
creationTime = LocalDateTime.from( creationTime = LocalDateTime.from(
Utilities.getBackupDateTimeFormatter().parse( Utilities.getBackupDateTimeFormatter().parse(
file.getName().split(fileExtension)[0].split("#")[0] file.getFileName().toString().split(fileExtension)[0].split("#")[0]
) )
); );
} catch (Exception ignored2){} } catch (Exception ignored2){}
@ -160,7 +163,7 @@ public class Utilities {
if(creationTime == null) { if(creationTime == null) {
try { try {
FileTime fileTime = (FileTime) Files.getAttribute(file.toPath(), "creationTime"); FileTime fileTime = (FileTime) Files.getAttribute(file, "creationTime");
creationTime = LocalDateTime.ofInstant(fileTime.toInstant(), ZoneOffset.systemDefault()); creationTime = LocalDateTime.ofInstant(fileTime.toInstant(), ZoneOffset.systemDefault());
} catch (IOException ignored3) {} } catch (IOException ignored3) {}
} }
@ -169,7 +172,7 @@ public class Utilities {
return Optional.ofNullable(creationTime); return Optional.ofNullable(creationTime);
} }
public static boolean isValidBackup(File f) { public static boolean isValidBackup(Path f) {
return getArchiveExtension(f).isPresent() && getFileCreationTime(f).isPresent() && isFileOk(f); return getArchiveExtension(f).isPresent() && getFileCreationTime(f).isPresent() && isFileOk(f);
} }
@ -177,6 +180,10 @@ public class Utilities {
return f.exists() && f.isFile(); return f.exists() && f.isFile();
} }
public static boolean isFileOk(Path f) {
return Files.exists(f) && Files.isRegularFile(f);
}
public static DateTimeFormatter getDateTimeFormatter() { public static DateTimeFormatter getDateTimeFormatter() {
return DateTimeFormatter.ofPattern(config.get().dateTimeFormat); return DateTimeFormatter.ofPattern(config.get().dateTimeFormat);
} }

View File

@ -26,11 +26,13 @@ import net.szum123321.textile_backup.config.ConfigHelper;
import net.szum123321.textile_backup.core.Utilities; import net.szum123321.textile_backup.core.Utilities;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.stream.Stream;
public class BackupHelper { public class BackupHelper {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
@ -77,52 +79,76 @@ public class BackupHelper {
} }
public static int executeFileLimit(ServerCommandSource ctx, String worldName) { public static int executeFileLimit(ServerCommandSource ctx, String worldName) {
File root = Utilities.getBackupRootPath(worldName); Path root = Utilities.getBackupRootPath(worldName);
int deletedFiles = 0; int deletedFiles = 0;
if (root.isDirectory() && root.exists() && root.listFiles() != null) {
if (Files.isDirectory(root) && Files.exists(root) && !isEmpty(root)) {
if (config.get().maxAge > 0) { // delete files older that configured if (config.get().maxAge > 0) { // delete files older that configured
final LocalDateTime now = LocalDateTime.now(); final LocalDateTime now = LocalDateTime.now();
deletedFiles += Arrays.stream(root.listFiles()) try(Stream<Path> stream = Files.list(root)) {
.filter(Utilities::isValidBackup)// We check if we can get file's creation date so that the next line won't throw an exception deletedFiles += stream
.filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge) .filter(Utilities::isValidBackup)// We check if we can get restoreableFile's creation date so that the next line won't throw an exception
.map(f -> deleteFile(f, ctx)) .filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
.filter(b -> b).count(); //a bit awkward .map(f -> deleteFile(f, ctx))
.filter(b -> b).count(); //a bit awkward
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (config.get().backupsToKeep > 0 /*&& root.listFiles().length > config.get().backupsToKeep*/) {
try(Stream<Path> stream = Files.list(root)) {
deletedFiles += stream
.filter(Utilities::isValidBackup)
.sorted(Comparator.comparing(f -> Utilities.getFileCreationTime((Path) f).get()).reversed())
.skip(config.get().backupsToKeep)
.map(f -> deleteFile(f, ctx))
.filter(b -> b).count();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
if (config.get().backupsToKeep > 0 && root.listFiles().length > config.get().backupsToKeep) { //It is fucking quadratic!
deletedFiles += Arrays.stream(root.listFiles()) /*if (config.get().maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) {
.filter(Utilities::isValidBackup)
.sorted(Comparator.comparing(f -> Utilities.getFileCreationTime((File) f).get()).reversed())
.skip(config.get().backupsToKeep)
.map(f -> deleteFile(f, ctx))
.filter(b -> b).count();
}
if (config.get().maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) {
deletedFiles += Arrays.stream(root.listFiles()) deletedFiles += Arrays.stream(root.listFiles())
.filter(Utilities::isValidBackup) .filter(Utilities::isValidBackup)
.sorted(Comparator.comparing(f -> Utilities.getFileCreationTime(f).get())) .sorted(Comparator.comparing(f -> Utilities.getFileCreationTime(f).get()))
.takeWhile(f -> FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) .takeWhile(f -> FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize)
.map(f -> deleteFile(f, ctx)) .map(f -> deleteFile(f, ctx))
.filter(b -> b).count(); .filter(b -> b).count();
} }*/
} }
return deletedFiles; return deletedFiles;
} }
private static boolean deleteFile(File f, ServerCommandSource ctx) { private static boolean isEmpty(Path path) {
if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) { if (Files.isDirectory(path)) {
if(f.delete()) { try (Stream<Path> entries = Files.list(path)) {
log.sendInfoAL(ctx, "Deleting: {}", f.getName()); return entries.findFirst().isEmpty();
return true; } catch (IOException e) {
} else { return false;
log.sendErrorAL(ctx, "Something went wrong while deleting: {}.", f.getName());
} }
} }
return false; return false;
} }
private static boolean deleteFile(Path f, ServerCommandSource ctx) {
if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) {
try {
Files.delete(f);
log.sendInfoAL(ctx, "Deleting: {}", f);
} catch (IOException e) {
if(ctx.isExecutedByPlayer()) log.sendError(ctx, "Something went wrong while deleting: {}.", f);
log.error("Something went wrong while deleting: {}.", f, e);
return false;
}
return true;
}
return false;
}
} }

View File

@ -56,13 +56,12 @@ public class MakeBackupRunnable implements Runnable {
log.sendInfoAL(context, "Starting backup"); log.sendInfoAL(context, "Starting backup");
Path world = Utilities.getWorldFolder(context.getServer()).toPath(); Path world = Utilities.getWorldFolder(context.getServer());
log.trace("Minecraft world is: {}", world); log.trace("Minecraft world is: {}", world);
Path outFile = Utilities Path outFile = Utilities
.getBackupRootPath(Utilities.getLevelName(context.getServer())) .getBackupRootPath(Utilities.getLevelName(context.getServer()))
.toPath()
.resolve(getFileName()); .resolve(getFileName());
log.trace("Outfile is: {}", outFile); log.trace("Outfile is: {}", outFile);

View File

@ -21,43 +21,15 @@ package net.szum123321.textile_backup.core.restore;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.Util;
import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.ActionInitiator;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.UUID;
public record RestoreContext(RestoreHelper.RestoreableFile file, public record RestoreContext(RestoreHelper.RestoreableFile restoreableFile,
MinecraftServer server, MinecraftServer server,
@Nullable String comment, @Nullable String comment,
ActionInitiator initiator, ActionInitiator initiator,
ServerCommandSource commandSource) { ServerCommandSource commandSource) {
public RestoreHelper.RestoreableFile getFile() {
return file;
}
public MinecraftServer getServer() {
return server;
}
@Nullable
public String getComment() {
return comment;
}
public ActionInitiator getInitiator() {
return initiator;
}
public UUID getInitiatorUUID() {
return initiator.equals(ActionInitiator.Player) && commandSource.getEntity() != null ? commandSource.getEntity().getUuid(): Util.NIL_UUID;
}
public ServerCommandSource getCommandSource() {
return commandSource;
}
public static final class Builder { public static final class Builder {
private RestoreHelper.RestoreableFile file; private RestoreHelper.RestoreableFile file;
private MinecraftServer server; private MinecraftServer server;

View File

@ -28,23 +28,31 @@ import net.szum123321.textile_backup.core.ActionInitiator;
import net.szum123321.textile_backup.core.Utilities; import net.szum123321.textile_backup.core.Utilities;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class RestoreHelper { public class RestoreHelper {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
private final static ConfigHelper config = ConfigHelper.INSTANCE; private final static ConfigHelper config = ConfigHelper.INSTANCE;
public static Optional<RestoreableFile> findFileAndLockIfPresent(LocalDateTime backupTime, MinecraftServer server) { public static Optional<RestoreableFile> findFileAndLockIfPresent(LocalDateTime backupTime, MinecraftServer server) {
File root = Utilities.getBackupRootPath(Utilities.getLevelName(server)); Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
Optional<RestoreableFile> optionalFile = Arrays.stream(root.listFiles()) Optional<RestoreableFile> optionalFile;
.map(RestoreableFile::newInstance) try (Stream<Path> stream = Files.list(root)) {
.flatMap(Optional::stream) optionalFile = stream
.filter(rf -> rf.getCreationTime().equals(backupTime)) .map(RestoreableFile::newInstance)
.findFirst(); .flatMap(Optional::stream)
.filter(rf -> rf.getCreationTime().equals(backupTime))
.findFirst();
} catch (IOException e) {
throw new RuntimeException(e);
}
Statics.untouchableFile = optionalFile.map(RestoreableFile::getFile); Statics.untouchableFile = optionalFile.map(RestoreableFile::getFile);
@ -52,8 +60,8 @@ public class RestoreHelper {
} }
public static AwaitThread create(RestoreContext ctx) { public static AwaitThread create(RestoreContext ctx) {
if(ctx.getInitiator() == ActionInitiator.Player) if(ctx.initiator() == ActionInitiator.Player)
log.info("Backup restoration was initiated by: {}", ctx.getCommandSource().getName()); log.info("Backup restoration was initiated by: {}", ctx.commandSource().getName());
else else
log.info("Backup restoration was initiated form Server Console"); log.info("Backup restoration was initiated form Server Console");
@ -69,28 +77,32 @@ public class RestoreHelper {
} }
public static List<RestoreableFile> getAvailableBackups(MinecraftServer server) { public static List<RestoreableFile> getAvailableBackups(MinecraftServer server) {
File root = Utilities.getBackupRootPath(Utilities.getLevelName(server)); Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
return Arrays.stream(root.listFiles()) try (Stream<Path> stream = Files.list(root)) {
.filter(Utilities::isValidBackup) return stream.filter(Utilities::isValidBackup)
.map(RestoreableFile::newInstance) .map(RestoreableFile::newInstance)
.flatMap(Optional::stream) .flatMap(Optional::stream)
.collect(Collectors.toList()); .collect(Collectors.toList());
} catch (IOException e) {
log.error("Error while listing available backups", e);
return new LinkedList<>();
}
} }
public static class RestoreableFile implements Comparable<RestoreableFile> { public static class RestoreableFile implements Comparable<RestoreableFile> {
private final File file; private final Path file;
private final ConfigPOJO.ArchiveFormat archiveFormat; private final ConfigPOJO.ArchiveFormat archiveFormat;
private final LocalDateTime creationTime; private final LocalDateTime creationTime;
private final String comment; private final String comment;
private RestoreableFile(File file) throws NoSuchElementException { private RestoreableFile(Path file) throws NoSuchElementException {
this.file = file; this.file = file;
archiveFormat = Utilities.getArchiveExtension(file).orElseThrow(() -> new NoSuchElementException("Couldn't get file extension!")); archiveFormat = Utilities.getArchiveExtension(file).orElseThrow(() -> new NoSuchElementException("Couldn't get restoreableFile extension!"));
String extension = archiveFormat.getCompleteString(); String extension = archiveFormat.getCompleteString();
creationTime = Utilities.getFileCreationTime(file).orElseThrow(() -> new NoSuchElementException("Couldn't get file creation time!")); creationTime = Utilities.getFileCreationTime(file).orElseThrow(() -> new NoSuchElementException("Couldn't get restoreableFile creation time!"));
final String filename = file.getName(); final String filename = file.getFileName().toString();
if(filename.split("#").length > 1) { if(filename.split("#").length > 1) {
this.comment = filename.split("#")[1].split(extension)[0]; this.comment = filename.split("#")[1].split(extension)[0];
@ -99,7 +111,7 @@ public class RestoreHelper {
} }
} }
public static Optional<RestoreableFile> newInstance(File file) { public static Optional<RestoreableFile> newInstance(Path file) {
try { try {
return Optional.of(new RestoreableFile(file)); return Optional.of(new RestoreableFile(file));
} catch (NoSuchElementException ignored) {} } catch (NoSuchElementException ignored) {}
@ -107,7 +119,7 @@ public class RestoreHelper {
return Optional.empty(); return Optional.empty();
} }
public File getFile() { public Path getFile() {
return file; return file;
} }