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 java.io.File;
import java.nio.file.Path;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
@ -35,6 +35,6 @@ public class Statics {
public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true);
public static boolean disableWatchdog = false;
public static AwaitThread restoreAwaitThread = null;
public static Optional<File> untouchableFile = Optional.empty();
public static Optional<Path> untouchableFile = Optional.empty();
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.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
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.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.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
public class DeleteCommand {
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);
}
File root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer()));
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer()));
Optional<File> optionalFile = Arrays.stream(root.listFiles())
.filter(Utilities::isValidBackup)
.filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime))
.findFirst();
try (Stream<Path> stream = Files.list(root)) {
stream.filter(Utilities::isValidBackup)
.filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime))
.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(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(optionalFile.get())) {
if(optionalFile.get().delete()) {
log.sendInfo(source, "File {} successfully deleted!", optionalFile.get().getName());
if(source.getEntity() instanceof PlayerEntity)
log.info("Player {} deleted {}.", source.getPlayer().getName(), optionalFile.get().getName());
} else {
log.sendError(source, "Something went wrong while deleting file!");
}
} else {
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 {
if(source.isExecutedByPlayer())
log.info("Player {} deleted {}.", source.getPlayer().getName(), file);
} catch (IOException e) {
log.sendError(source, "Something went wrong while deleting file!");
}
} else {
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");
}
});
} catch (IOException ignored) {
log.sendError(source, "Couldn't find file by this name.");
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());
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 {
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();
}
public static File getWorldFolder(MinecraftServer server) {
public static Path getWorldFolder(MinecraftServer server) {
return ((MinecraftServerSessionAccessor)server)
.getSession()
.getWorldDirectory(World.OVERWORLD)
.toFile();
.getWorldDirectory(World.OVERWORLD);
}
public static File getBackupRootPath(String worldName) {
File path = new File(config.get().path).getAbsoluteFile();
public static Path getBackupRootPath(String worldName) {
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;
}
@ -81,7 +84,7 @@ public class Utilities {
boolean flag = false;
Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir"));
if(
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server)) >=
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server).toFile()) >=
tmp_dir.toFile().getUsableSpace()
) {
log.error("Not enough space left in TMP directory! ({})", tmp_dir);
@ -130,11 +133,11 @@ public class Utilities {
.findAny();
}
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(File f) {
return getArchiveExtension(f.getName());
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(Path f) {
return getArchiveExtension(f.getFileName().toString());
}
public static Optional<LocalDateTime> getFileCreationTime(File file) {
public static Optional<LocalDateTime> getFileCreationTime(Path file) {
LocalDateTime creationTime = null;
if(getArchiveExtension(file).isPresent()) {
@ -143,7 +146,7 @@ public class Utilities {
try {
creationTime = LocalDateTime.from(
Utilities.getDateTimeFormatter().parse(
file.getName().split(fileExtension)[0].split("#")[0]
file.getFileName().toString().split(fileExtension)[0].split("#")[0]
)
);
} catch (Exception ignored) {}
@ -152,7 +155,7 @@ public class Utilities {
try {
creationTime = LocalDateTime.from(
Utilities.getBackupDateTimeFormatter().parse(
file.getName().split(fileExtension)[0].split("#")[0]
file.getFileName().toString().split(fileExtension)[0].split("#")[0]
)
);
} catch (Exception ignored2){}
@ -160,7 +163,7 @@ public class Utilities {
if(creationTime == null) {
try {
FileTime fileTime = (FileTime) Files.getAttribute(file.toPath(), "creationTime");
FileTime fileTime = (FileTime) Files.getAttribute(file, "creationTime");
creationTime = LocalDateTime.ofInstant(fileTime.toInstant(), ZoneOffset.systemDefault());
} catch (IOException ignored3) {}
}
@ -169,7 +172,7 @@ public class Utilities {
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);
}
@ -177,6 +180,10 @@ public class Utilities {
return f.exists() && f.isFile();
}
public static boolean isFileOk(Path f) {
return Files.exists(f) && Files.isRegularFile(f);
}
public static DateTimeFormatter getDateTimeFormatter() {
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 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.ZoneOffset;
import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Stream;
public class BackupHelper {
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) {
File root = Utilities.getBackupRootPath(worldName);
Path root = Utilities.getBackupRootPath(worldName);
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
final LocalDateTime now = LocalDateTime.now();
deletedFiles += Arrays.stream(root.listFiles())
.filter(Utilities::isValidBackup)// We check if we can get file's creation date so that the next line won't throw an exception
.filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
.map(f -> deleteFile(f, ctx))
.filter(b -> b).count(); //a bit awkward
try(Stream<Path> stream = Files.list(root)) {
deletedFiles += stream
.filter(Utilities::isValidBackup)// We check if we can get restoreableFile's creation date so that the next line won't throw an exception
.filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
.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) {
deletedFiles += Arrays.stream(root.listFiles())
.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) {
//It is fucking quadratic!
/*if (config.get().maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) {
deletedFiles += Arrays.stream(root.listFiles())
.filter(Utilities::isValidBackup)
.sorted(Comparator.comparing(f -> Utilities.getFileCreationTime(f).get()))
.takeWhile(f -> FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize)
.map(f -> deleteFile(f, ctx))
.filter(b -> b).count();
}
}*/
}
return deletedFiles;
}
private static boolean deleteFile(File f, ServerCommandSource ctx) {
if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) {
if(f.delete()) {
log.sendInfoAL(ctx, "Deleting: {}", f.getName());
return true;
} else {
log.sendErrorAL(ctx, "Something went wrong while deleting: {}.", f.getName());
private static boolean isEmpty(Path path) {
if (Files.isDirectory(path)) {
try (Stream<Path> entries = Files.list(path)) {
return entries.findFirst().isEmpty();
} catch (IOException e) {
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");
Path world = Utilities.getWorldFolder(context.getServer()).toPath();
Path world = Utilities.getWorldFolder(context.getServer());
log.trace("Minecraft world is: {}", world);
Path outFile = Utilities
.getBackupRootPath(Utilities.getLevelName(context.getServer()))
.toPath()
.resolve(getFileName());
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.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.Util;
import net.szum123321.textile_backup.core.ActionInitiator;
import javax.annotation.Nullable;
import java.util.UUID;
public record RestoreContext(RestoreHelper.RestoreableFile file,
public record RestoreContext(RestoreHelper.RestoreableFile restoreableFile,
MinecraftServer server,
@Nullable String comment,
ActionInitiator initiator,
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 {
private RestoreHelper.RestoreableFile file;
private MinecraftServer server;

View File

@ -28,23 +28,31 @@ import net.szum123321.textile_backup.core.ActionInitiator;
import net.szum123321.textile_backup.core.Utilities;
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.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class RestoreHelper {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
private final static ConfigHelper config = ConfigHelper.INSTANCE;
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())
.map(RestoreableFile::newInstance)
.flatMap(Optional::stream)
.filter(rf -> rf.getCreationTime().equals(backupTime))
.findFirst();
Optional<RestoreableFile> optionalFile;
try (Stream<Path> stream = Files.list(root)) {
optionalFile = stream
.map(RestoreableFile::newInstance)
.flatMap(Optional::stream)
.filter(rf -> rf.getCreationTime().equals(backupTime))
.findFirst();
} catch (IOException e) {
throw new RuntimeException(e);
}
Statics.untouchableFile = optionalFile.map(RestoreableFile::getFile);
@ -52,8 +60,8 @@ public class RestoreHelper {
}
public static AwaitThread create(RestoreContext ctx) {
if(ctx.getInitiator() == ActionInitiator.Player)
log.info("Backup restoration was initiated by: {}", ctx.getCommandSource().getName());
if(ctx.initiator() == ActionInitiator.Player)
log.info("Backup restoration was initiated by: {}", ctx.commandSource().getName());
else
log.info("Backup restoration was initiated form Server Console");
@ -69,28 +77,32 @@ public class RestoreHelper {
}
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())
.filter(Utilities::isValidBackup)
.map(RestoreableFile::newInstance)
.flatMap(Optional::stream)
.collect(Collectors.toList());
try (Stream<Path> stream = Files.list(root)) {
return stream.filter(Utilities::isValidBackup)
.map(RestoreableFile::newInstance)
.flatMap(Optional::stream)
.collect(Collectors.toList());
} catch (IOException e) {
log.error("Error while listing available backups", e);
return new LinkedList<>();
}
}
public static class RestoreableFile implements Comparable<RestoreableFile> {
private final File file;
private final Path file;
private final ConfigPOJO.ArchiveFormat archiveFormat;
private final LocalDateTime creationTime;
private final String comment;
private RestoreableFile(File file) throws NoSuchElementException {
private RestoreableFile(Path file) throws NoSuchElementException {
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();
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) {
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 {
return Optional.of(new RestoreableFile(file));
} catch (NoSuchElementException ignored) {}
@ -107,7 +119,7 @@ public class RestoreHelper {
return Optional.empty();
}
public File getFile() {
public Path getFile() {
return file;
}