Merged BackuContext, MakeBackupRunnable, MakeBackupRunnableFactory into ExecutableBackup
parent
27d6d68e97
commit
6782c4fe5f
|
@ -23,7 +23,6 @@ import net.szum123321.textile_backup.core.digest.BalticHash;
|
|||
import net.szum123321.textile_backup.core.digest.Hash;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
|
||||
import net.szum123321.textile_backup.core.create.MakeBackupRunnable;
|
||||
import net.szum123321.textile_backup.core.restore.AwaitThread;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
|
@ -70,8 +69,8 @@ public class Globals {
|
|||
if(!executorService.awaitTermination(timeout, TimeUnit.MICROSECONDS)) {
|
||||
log.error("Timeout occurred while waiting for currently running backups to finish!");
|
||||
executorService.shutdownNow().stream()
|
||||
.filter(r -> r instanceof MakeBackupRunnable)
|
||||
.map(r -> (MakeBackupRunnable)r)
|
||||
// .filter(r -> r instanceof ExecutableBackup)
|
||||
// .map(r -> (ExecutableBackup)r)
|
||||
.forEach(r -> log.error("Dropping: {}", r.toString()));
|
||||
if(!executorService.awaitTermination(1000, TimeUnit.MICROSECONDS))
|
||||
log.error("Couldn't shut down the executor!");
|
||||
|
|
|
@ -39,9 +39,8 @@ import net.szum123321.textile_backup.commands.restore.RestoreBackupCommand;
|
|||
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||
import net.szum123321.textile_backup.config.ConfigPOJO;
|
||||
import net.szum123321.textile_backup.core.ActionInitiator;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.BackupScheduler;
|
||||
import net.szum123321.textile_backup.core.create.MakeBackupRunnableFactory;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import net.szum123321.textile_backup.test.BalticHashTest;
|
||||
|
||||
public class TextileBackup implements ModInitializer {
|
||||
|
@ -82,14 +81,14 @@ public class TextileBackup implements ModInitializer {
|
|||
|
||||
if (config.get().shutdownBackup && Globals.INSTANCE.globalShutdownBackupFlag.get()) {
|
||||
try {
|
||||
MakeBackupRunnableFactory.create(
|
||||
BackupContext.Builder
|
||||
ExecutableBackup.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(server)
|
||||
.setInitiator(ActionInitiator.Shutdown)
|
||||
.setComment("shutdown")
|
||||
.announce()
|
||||
.build()
|
||||
).call();
|
||||
.call();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ import net.minecraft.text.Text;
|
|||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
@ -112,7 +112,7 @@ public class TextileLogger {
|
|||
sendFeedback(Level.INFO, source, msg, args);
|
||||
}
|
||||
|
||||
public void sendInfo(BackupContext context, String msg, Object... args) {
|
||||
public void sendInfo(ExecutableBackup context, String msg, Object... args) {
|
||||
sendInfo(context.commandSource(), msg, args);
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,8 @@ public class TextileLogger {
|
|||
sendFeedback(Level.ERROR, source, msg, args);
|
||||
}
|
||||
|
||||
public void sendError(BackupContext context, String msg, Object... args) {
|
||||
|
||||
public void sendError(ExecutableBackup context, String msg, Object... args) {
|
||||
sendError(context.commandSource(), msg, args);
|
||||
}
|
||||
|
||||
|
@ -134,7 +135,7 @@ public class TextileLogger {
|
|||
sendToPlayerAndLog(Level.INFO, source, msg, args);
|
||||
}
|
||||
|
||||
public void sendInfoAL(BackupContext context, String msg, Object... args) {
|
||||
public void sendInfoAL(ExecutableBackup context, String msg, Object... args) {
|
||||
sendInfoAL(context.commandSource(), msg, args);
|
||||
}
|
||||
|
||||
|
@ -142,7 +143,7 @@ public class TextileLogger {
|
|||
sendToPlayerAndLog(Level.ERROR, source, msg, args);
|
||||
}
|
||||
|
||||
public void sendErrorAL(BackupContext context, String msg, Object... args) {
|
||||
public void sendErrorAL(ExecutableBackup context, String msg, Object... args) {
|
||||
sendErrorAL(context.commandSource(), msg, args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
|||
import net.szum123321.textile_backup.Globals;
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import net.szum123321.textile_backup.TextileLogger;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.MakeBackupRunnableFactory;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -42,15 +41,13 @@ public class StartBackupCommand {
|
|||
|
||||
private static int execute(ServerCommandSource source, @Nullable String comment) {
|
||||
Globals.INSTANCE.getQueueExecutor().submit(
|
||||
MakeBackupRunnableFactory.create(
|
||||
BackupContext.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setCommandSource(source)
|
||||
.setComment(comment)
|
||||
.guessInitiator()
|
||||
.saveServer()
|
||||
.build()
|
||||
)
|
||||
ExecutableBackup.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setCommandSource(source)
|
||||
.setComment(comment)
|
||||
.guessInitiator()
|
||||
.saveServer()
|
||||
.build()
|
||||
);
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* A simple backup mod for Fabric
|
||||
* Copyright (C) 2022 Szum123321
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.szum123321.textile_backup.core.create;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.szum123321.textile_backup.core.ActionInitiator;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public record BackupContext(@NotNull MinecraftServer server,
|
||||
ServerCommandSource commandSource,
|
||||
ActionInitiator initiator,
|
||||
boolean save,
|
||||
boolean cleanup,
|
||||
String comment,
|
||||
LocalDateTime startDate) {
|
||||
|
||||
public boolean startedByPlayer() {
|
||||
return initiator == ActionInitiator.Player;
|
||||
}
|
||||
|
||||
public boolean shouldSave() {
|
||||
return save;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private MinecraftServer server;
|
||||
private ServerCommandSource commandSource;
|
||||
private ActionInitiator initiator;
|
||||
private boolean save;
|
||||
private boolean cleanup;
|
||||
private String comment;
|
||||
|
||||
private boolean guessInitiator;
|
||||
|
||||
public Builder() {
|
||||
this.server = null;
|
||||
this.commandSource = null;
|
||||
this.initiator = null;
|
||||
this.save = false;
|
||||
cleanup = true; //defaults
|
||||
this.comment = null;
|
||||
|
||||
guessInitiator = false;
|
||||
}
|
||||
|
||||
public static Builder newBackupContextBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder setCommandSource(ServerCommandSource commandSource) {
|
||||
this.commandSource = commandSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setServer(MinecraftServer server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setInitiator(ActionInitiator initiator) {
|
||||
this.initiator = initiator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setComment(String comment) {
|
||||
this.comment = comment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder guessInitiator() {
|
||||
this.guessInitiator = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder saveServer() {
|
||||
this.save = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dontCleanup() {
|
||||
this.cleanup = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BackupContext build() {
|
||||
if (guessInitiator) {
|
||||
initiator = Utilities.wasSentByPlayer(commandSource) ? ActionInitiator.Player : ActionInitiator.ServerConsole;
|
||||
} else if (initiator == null) throw new NoSuchElementException("No initiator provided!");
|
||||
|
||||
if (server == null) {
|
||||
if (commandSource != null) setServer(commandSource.getServer());
|
||||
else throw new RuntimeException("Neither MinecraftServer or ServerCommandSource were provided!");
|
||||
}
|
||||
|
||||
return new BackupContext(server, commandSource, initiator, save, cleanup, comment, LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,14 +53,13 @@ public class BackupScheduler {
|
|||
if(nextBackup <= now) {
|
||||
//It's time to run
|
||||
Globals.INSTANCE.getQueueExecutor().submit(
|
||||
MakeBackupRunnableFactory.create(
|
||||
BackupContext.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(server)
|
||||
.setInitiator(ActionInitiator.Timer)
|
||||
.saveServer()
|
||||
.build()
|
||||
)
|
||||
ExecutableBackup.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(server)
|
||||
.setInitiator(ActionInitiator.Timer)
|
||||
.saveServer()
|
||||
.announce()
|
||||
.build()
|
||||
);
|
||||
|
||||
nextBackup = now + config.get().backupInterval;
|
||||
|
@ -76,14 +75,13 @@ public class BackupScheduler {
|
|||
if(scheduled && nextBackup <= now) {
|
||||
//Verify we hadn't done the final one, and it's time to do so
|
||||
Globals.INSTANCE.getQueueExecutor().submit(
|
||||
MakeBackupRunnableFactory.create(
|
||||
BackupContext.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(server)
|
||||
.setInitiator(ActionInitiator.Timer)
|
||||
.saveServer()
|
||||
.build()
|
||||
)
|
||||
ExecutableBackup.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(server)
|
||||
.setInitiator(ActionInitiator.Timer)
|
||||
.saveServer()
|
||||
.announce()
|
||||
.build()
|
||||
);
|
||||
|
||||
scheduled = false;
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
package net.szum123321.textile_backup.core.create;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.szum123321.textile_backup.Globals;
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import net.szum123321.textile_backup.TextileLogger;
|
||||
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||
import net.szum123321.textile_backup.core.ActionInitiator;
|
||||
import net.szum123321.textile_backup.core.Cleanup;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
import net.szum123321.textile_backup.core.create.compressors.ParallelZipCompressor;
|
||||
import net.szum123321.textile_backup.core.create.compressors.ZipCompressor;
|
||||
import net.szum123321.textile_backup.core.create.compressors.tar.AbstractTarArchiver;
|
||||
import net.szum123321.textile_backup.core.create.compressors.tar.ParallelGzipCompressor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||
ServerCommandSource commandSource,
|
||||
ActionInitiator initiator,
|
||||
boolean save,
|
||||
boolean cleanup,
|
||||
String comment,
|
||||
LocalDateTime startDate) implements Callable<Void> {
|
||||
|
||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||
|
||||
public boolean startedByPlayer() {
|
||||
return initiator == ActionInitiator.Player;
|
||||
}
|
||||
|
||||
public void announce() {
|
||||
if(config.get().broadcastBackupStart) {
|
||||
Utilities.notifyPlayers(server,
|
||||
"Warning! Server backup will begin shortly. You may experience some lag."
|
||||
);
|
||||
} else {
|
||||
log.sendInfoAL(this, "Warning! Server backup will begin shortly. You may experience some lag.");
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Backup started ");
|
||||
|
||||
builder.append(initiator.getPrefix());
|
||||
|
||||
if(startedByPlayer())
|
||||
builder.append(commandSource.getDisplayName().getString());
|
||||
else
|
||||
builder.append(initiator.getName());
|
||||
|
||||
builder.append(" on: ");
|
||||
builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
|
||||
|
||||
log.info(builder.toString());
|
||||
}
|
||||
@Override
|
||||
public Void call() throws IOException, ExecutionException, InterruptedException {
|
||||
if (save) { //save the world
|
||||
log.sendInfoAL(this, "Saving server...");
|
||||
server.saveAll(true, true, false);
|
||||
}
|
||||
|
||||
Path outFile = Utilities.getBackupRootPath(Utilities.getLevelName(server)).resolve(getFileName());
|
||||
|
||||
log.trace("Outfile is: {}", outFile);
|
||||
|
||||
try {
|
||||
//I think I should synchronise these two next calls...
|
||||
Utilities.disableWorldSaving(server);
|
||||
Globals.INSTANCE.disableWatchdog = true;
|
||||
|
||||
Globals.INSTANCE.updateTMPFSFlag(server);
|
||||
|
||||
log.sendInfoAL(this, "Starting backup");
|
||||
|
||||
Path world = Utilities.getWorldFolder(server);
|
||||
|
||||
log.trace("Minecraft world is: {}", world);
|
||||
|
||||
Files.createDirectories(outFile.getParent());
|
||||
Files.createFile(outFile);
|
||||
|
||||
int coreCount;
|
||||
|
||||
if (config.get().compressionCoreCountLimit <= 0) coreCount = Runtime.getRuntime().availableProcessors();
|
||||
else
|
||||
coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
log.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
switch (config.get().format) {
|
||||
case ZIP -> {
|
||||
if (coreCount > 1 && !Globals.INSTANCE.disableTMPFS()) {
|
||||
log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
|
||||
ParallelZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
||||
} else {
|
||||
log.trace("Using REGULAR Zip Compressor.");
|
||||
ZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
||||
}
|
||||
}
|
||||
case GZIP -> ParallelGzipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
||||
case TAR -> new AbstractTarArchiver().createArchive(world, outFile, this, coreCount);
|
||||
}
|
||||
|
||||
if(cleanup) new Cleanup(commandSource, Utilities.getLevelName(server)).call();
|
||||
|
||||
if (config.get().broadcastBackupDone) Utilities.notifyPlayers(server, "Done!");
|
||||
else log.sendInfoAL(this, "Done!");
|
||||
|
||||
} catch (Throwable e) {
|
||||
//ExecutorService swallows exception, so I need to catch everything
|
||||
log.error("An exception occurred when trying to create new backup file!", e);
|
||||
|
||||
if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) {
|
||||
try {
|
||||
Files.delete(outFile);
|
||||
} catch (IOException ex) {
|
||||
log.error("An exception occurred while trying go delete: {}", outFile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (initiator == ActionInitiator.Player)
|
||||
log.sendError(this, "An exception occurred when trying to create new backup file!");
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
Utilities.enableWorldSaving(server);
|
||||
Globals.INSTANCE.disableWatchdog = false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getFileName() {
|
||||
return Utilities.getDateTimeFormatter().format(startDate) +
|
||||
(comment != null ? "#" + comment.replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
|
||||
config.get().format.getCompleteString();
|
||||
}
|
||||
public static class Builder {
|
||||
private MinecraftServer server;
|
||||
private ServerCommandSource commandSource;
|
||||
private ActionInitiator initiator;
|
||||
private boolean save;
|
||||
private boolean cleanup;
|
||||
private String comment;
|
||||
private boolean announce;
|
||||
|
||||
private boolean guessInitiator;
|
||||
|
||||
public Builder() {
|
||||
this.server = null;
|
||||
this.commandSource = null;
|
||||
this.initiator = null;
|
||||
this.save = false;
|
||||
cleanup = true; //defaults
|
||||
this.comment = null;
|
||||
this.announce = false;
|
||||
|
||||
guessInitiator = false;
|
||||
}
|
||||
|
||||
public static ExecutableBackup.Builder newBackupContextBuilder() {
|
||||
return new ExecutableBackup.Builder();
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder setCommandSource(ServerCommandSource commandSource) {
|
||||
this.commandSource = commandSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder setServer(MinecraftServer server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder setInitiator(ActionInitiator initiator) {
|
||||
this.initiator = initiator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder setComment(String comment) {
|
||||
this.comment = comment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder guessInitiator() {
|
||||
this.guessInitiator = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder saveServer() {
|
||||
this.save = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder noCleanup() {
|
||||
this.cleanup = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup.Builder announce() {
|
||||
this.announce = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutableBackup build() {
|
||||
if (guessInitiator) {
|
||||
initiator = Utilities.wasSentByPlayer(commandSource) ? ActionInitiator.Player : ActionInitiator.ServerConsole;
|
||||
} else if (initiator == null) throw new NoSuchElementException("No initiator provided!");
|
||||
|
||||
if (server == null) {
|
||||
if (commandSource != null) setServer(commandSource.getServer());
|
||||
else throw new RuntimeException("Neither MinecraftServer or ServerCommandSource were provided!");
|
||||
}
|
||||
|
||||
ExecutableBackup v = new ExecutableBackup(server, commandSource, initiator, save, cleanup, comment, LocalDateTime.now());
|
||||
|
||||
if(announce) v.announce();
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* A simple backup mod for Fabric
|
||||
* Copyright (C) 2022 Szum123321
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.szum123321.textile_backup.core.create;
|
||||
|
||||
import net.szum123321.textile_backup.Globals;
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import net.szum123321.textile_backup.TextileLogger;
|
||||
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||
import net.szum123321.textile_backup.core.ActionInitiator;
|
||||
import net.szum123321.textile_backup.core.Cleanup;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
import net.szum123321.textile_backup.core.create.compressors.ParallelZipCompressor;
|
||||
import net.szum123321.textile_backup.core.create.compressors.ZipCompressor;
|
||||
import net.szum123321.textile_backup.core.create.compressors.tar.AbstractTarArchiver;
|
||||
import net.szum123321.textile_backup.core.create.compressors.tar.ParallelBZip2Compressor;
|
||||
import net.szum123321.textile_backup.core.create.compressors.tar.ParallelGzipCompressor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* The actual object responsible for creating the backup
|
||||
*/
|
||||
public class MakeBackupRunnable implements Callable<Void> {
|
||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||
|
||||
private final BackupContext context;
|
||||
|
||||
public MakeBackupRunnable(BackupContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws IOException, ExecutionException, InterruptedException {
|
||||
Path outFile = Utilities.getBackupRootPath(Utilities.getLevelName(context.server())).resolve(getFileName());
|
||||
|
||||
log.trace("Outfile is: {}", outFile);
|
||||
|
||||
try {
|
||||
//I think I should synchronise these two next calls...
|
||||
Utilities.disableWorldSaving(context.server());
|
||||
Globals.INSTANCE.disableWatchdog = true;
|
||||
|
||||
Globals.INSTANCE.updateTMPFSFlag(context.server());
|
||||
|
||||
log.sendInfoAL(context, "Starting backup");
|
||||
|
||||
Path world = Utilities.getWorldFolder(context.server());
|
||||
|
||||
log.trace("Minecraft world is: {}", world);
|
||||
|
||||
Files.createDirectories(outFile.getParent());
|
||||
Files.createFile(outFile);
|
||||
|
||||
int coreCount;
|
||||
|
||||
if (config.get().compressionCoreCountLimit <= 0) coreCount = Runtime.getRuntime().availableProcessors();
|
||||
else
|
||||
coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
log.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
switch (config.get().format) {
|
||||
case ZIP -> {
|
||||
if (coreCount > 1 && !Globals.INSTANCE.disableTMPFS()) {
|
||||
log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
|
||||
ParallelZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||
} else {
|
||||
log.trace("Using REGULAR Zip Compressor.");
|
||||
ZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||
}
|
||||
}
|
||||
case BZIP2 -> ParallelBZip2Compressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||
case GZIP -> ParallelGzipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||
/* case LZMA -> new AbstractTarArchiver() {
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||
return new LZMACompressorOutputStream(stream);
|
||||
}
|
||||
}.createArchive(world, outFile, context, coreCount);*/
|
||||
case TAR -> new AbstractTarArchiver().createArchive(world, outFile, context, coreCount);
|
||||
}
|
||||
|
||||
if(context.cleanup())
|
||||
new Cleanup(context.commandSource(), Utilities.getLevelName(context.server())).call();
|
||||
|
||||
if (config.get().broadcastBackupDone) Utilities.notifyPlayers(context.server(), "Done!");
|
||||
else log.sendInfoAL(context, "Done!");
|
||||
|
||||
} catch (Throwable e) {
|
||||
//ExecutorService swallows exception, so I need to catch everything
|
||||
log.error("An exception occurred when trying to create new backup file!", e);
|
||||
|
||||
if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) {
|
||||
try {
|
||||
Files.delete(outFile);
|
||||
} catch (IOException ex) {
|
||||
log.error("An exception occurred while trying go delete: {}", outFile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.initiator() == ActionInitiator.Player)
|
||||
log.sendError(context, "An exception occurred when trying to create new backup file!");
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
Utilities.enableWorldSaving(context.server());
|
||||
Globals.INSTANCE.disableWatchdog = false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getFileName() {
|
||||
return Utilities.getDateTimeFormatter().format(context.startDate()) +
|
||||
(context.comment() != null ? "#" + context.comment().replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
|
||||
config.get().format.getCompleteString();
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* A simple backup mod for Fabric
|
||||
* Copyright (C) 2022 Szum123321
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.szum123321.textile_backup.core.create;
|
||||
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import net.szum123321.textile_backup.TextileLogger;
|
||||
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class MakeBackupRunnableFactory {
|
||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||
|
||||
public static Callable<Void> create(BackupContext ctx) {
|
||||
if(config.get().broadcastBackupStart) {
|
||||
Utilities.notifyPlayers(ctx.server(),
|
||||
"Warning! Server backup will begin shortly. You may experience some lag."
|
||||
);
|
||||
} else {
|
||||
log.sendInfoAL(ctx, "Warning! Server backup will begin shortly. You may experience some lag.");
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Backup started ");
|
||||
|
||||
builder.append(ctx.initiator().getPrefix());
|
||||
|
||||
if(ctx.startedByPlayer())
|
||||
builder.append(ctx.commandSource().getDisplayName().getString());
|
||||
else
|
||||
builder.append(ctx.initiator().getName());
|
||||
|
||||
builder.append(" on: ");
|
||||
builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
|
||||
|
||||
log.info(builder.toString());
|
||||
|
||||
if (ctx.shouldSave()) {
|
||||
log.sendInfoAL(ctx, "Saving server...");
|
||||
|
||||
ctx.server().saveAll(true, true, false);
|
||||
}
|
||||
|
||||
return new MakeBackupRunnable(ctx);
|
||||
}
|
||||
}
|
|
@ -23,8 +23,8 @@ import net.szum123321.textile_backup.TextileBackup;
|
|||
import net.szum123321.textile_backup.TextileLogger;
|
||||
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||
import net.szum123321.textile_backup.core.*;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.BrokenFileHandler;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import net.szum123321.textile_backup.core.create.FileInputStreamSupplier;
|
||||
import net.szum123321.textile_backup.core.create.InputSupplier;
|
||||
import net.szum123321.textile_backup.core.digest.FileTreeHashBuilder;
|
||||
|
@ -44,7 +44,7 @@ import java.util.stream.Stream;
|
|||
public abstract class AbstractCompressor {
|
||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||
|
||||
public void createArchive(Path inputFile, Path outputFile, BackupContext ctx, int coreLimit) throws IOException, ExecutionException, InterruptedException {
|
||||
public void createArchive(Path inputFile, Path outputFile, ExecutableBackup ctx, int coreLimit) throws IOException, ExecutionException, InterruptedException {
|
||||
Instant start = Instant.now();
|
||||
|
||||
BrokenFileHandler brokenFileHandler = new BrokenFileHandler(); //Basically a hashmap storing files and their respective exceptions
|
||||
|
@ -106,7 +106,7 @@ public abstract class AbstractCompressor {
|
|||
log.sendInfoAL(ctx, "Compression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||
}
|
||||
|
||||
protected abstract OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException;
|
||||
protected abstract OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException;
|
||||
protected abstract void addEntry(InputSupplier inputSupplier, OutputStream arc) throws IOException;
|
||||
|
||||
protected void finish(OutputStream arc) throws InterruptedException, ExecutionException, IOException {
|
||||
|
|
|
@ -21,7 +21,7 @@ package net.szum123321.textile_backup.core.create.compressors;
|
|||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import net.szum123321.textile_backup.TextileLogger;
|
||||
import net.szum123321.textile_backup.core.NoSpaceLeftOnDeviceException;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import net.szum123321.textile_backup.core.create.InputSupplier;
|
||||
import org.apache.commons.compress.archivers.zip.*;
|
||||
|
||||
|
@ -61,7 +61,7 @@ public class ParallelZipCompressor extends ZipCompressor {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) {
|
||||
protected OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) {
|
||||
scatterZipCreator = new ParallelScatterZipCreator(Executors.newFixedThreadPool(coreLimit));
|
||||
return super.createArchiveOutputStream(stream, ctx, coreLimit);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ package net.szum123321.textile_backup.core.create.compressors;
|
|||
|
||||
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import net.szum123321.textile_backup.core.create.InputSupplier;
|
||||
import org.apache.commons.compress.archivers.zip.Zip64Mode;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
|
@ -43,7 +43,7 @@ public class ZipCompressor extends AbstractCompressor {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) {
|
||||
protected OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) {
|
||||
ZipArchiveOutputStream arc = new ZipArchiveOutputStream(stream);
|
||||
|
||||
arc.setMethod(ZipArchiveOutputStream.DEFLATED);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package net.szum123321.textile_backup.core.create.compressors.tar;
|
||||
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import net.szum123321.textile_backup.core.create.compressors.AbstractCompressor;
|
||||
import net.szum123321.textile_backup.core.create.InputSupplier;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
|
@ -28,12 +28,12 @@ import org.apache.commons.compress.utils.IOUtils;
|
|||
import java.io.*;
|
||||
|
||||
public class AbstractTarArchiver extends AbstractCompressor {
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||
protected OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
|
||||
TarArchiveOutputStream tar = new TarArchiveOutputStream(getCompressorOutputStream(stream, ctx, coreLimit));
|
||||
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
||||
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package net.szum123321.textile_backup.core.create.compressors.tar;
|
||||
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import org.at4j.comp.bzip2.BZip2OutputStream;
|
||||
import org.at4j.comp.bzip2.BZip2OutputStreamSettings;
|
||||
|
||||
|
@ -30,7 +30,7 @@ public class ParallelBZip2Compressor extends AbstractTarArchiver {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
|
||||
return new BZip2OutputStream(stream, new BZip2OutputStreamSettings().setNumberOfEncoderThreads(coreLimit));
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package net.szum123321.textile_backup.core.create.compressors.tar;
|
||||
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import org.anarres.parallelgzip.ParallelGZIPOutputStream;
|
||||
|
||||
import java.io.*;
|
||||
|
@ -33,7 +33,7 @@ public class ParallelGzipCompressor extends AbstractTarArchiver {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
|
||||
executorService = Executors.newFixedThreadPool(coreLimit);
|
||||
|
||||
return new ParallelGZIPOutputStream(stream, executorService);
|
||||
|
|
|
@ -26,8 +26,7 @@ import net.szum123321.textile_backup.config.ConfigPOJO;
|
|||
import net.szum123321.textile_backup.core.ActionInitiator;
|
||||
import net.szum123321.textile_backup.core.CompressionStatus;
|
||||
import net.szum123321.textile_backup.core.Utilities;
|
||||
import net.szum123321.textile_backup.core.create.BackupContext;
|
||||
import net.szum123321.textile_backup.core.create.MakeBackupRunnableFactory;
|
||||
import net.szum123321.textile_backup.core.create.ExecutableBackup;
|
||||
import net.szum123321.textile_backup.core.restore.decompressors.GenericTarDecompressor;
|
||||
import net.szum123321.textile_backup.core.restore.decompressors.ZipDecompressor;
|
||||
import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
|
||||
|
@ -78,20 +77,19 @@ public class RestoreBackupRunnable implements Runnable {
|
|||
ctx.server().getThread().join(); //wait for server thread to die and save all its state
|
||||
|
||||
if(config.get().backupOldWorlds) {
|
||||
return MakeBackupRunnableFactory.create (
|
||||
BackupContext.Builder
|
||||
return ExecutableBackup.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(ctx.server())
|
||||
.setInitiator(ActionInitiator.Restore)
|
||||
.dontCleanup()
|
||||
.noCleanup()
|
||||
.setComment("Old_World" + (ctx.comment() != null ? "_" + ctx.comment() : ""))
|
||||
.build()
|
||||
).call();
|
||||
.announce()
|
||||
.build().call();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
//run the thread.
|
||||
new Thread(waitForShutdown, "Server shutdown wait thread").start();
|
||||
|
||||
try {
|
||||
|
@ -106,7 +104,7 @@ public class RestoreBackupRunnable implements Runnable {
|
|||
|
||||
log.info("Waiting for server to fully terminate...");
|
||||
|
||||
//locks until the backup is finished
|
||||
//locks until the backup is finished and the server is dead
|
||||
waitForShutdown.get();
|
||||
|
||||
Optional<String> errorMsg;
|
||||
|
@ -127,8 +125,8 @@ public class RestoreBackupRunnable implements Runnable {
|
|||
if (errorMsg.isEmpty()) log.info("Backup valid. Restoring");
|
||||
else log.info("Backup is damaged, but verification is disabled [{}]. Restoring", errorMsg.get());
|
||||
|
||||
((MinecraftServerSessionAccessor) ctx.server())
|
||||
.getSession().close();
|
||||
//Disables write lock to override world file
|
||||
((MinecraftServerSessionAccessor) ctx.server()).getSession().close();
|
||||
|
||||
Utilities.deleteDirectory(worldFile);
|
||||
Files.move(tmp, worldFile);
|
||||
|
|
Loading…
Reference in New Issue