Javadoc + some small refactoring
This commit is contained in:
parent
33b40f0cba
commit
6577367c27
@ -60,10 +60,4 @@ public class BungeeWorkdirProcess extends BackupProcess {
|
||||
return "workdir";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void displayNextSchedule() {
|
||||
Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " next backup on "
|
||||
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(getNext())));
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,19 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static fr.pandacube.lib.chat.ChatStatic.text;
|
||||
|
||||
/**
|
||||
* Cleanup a backup directory (i.e. removes old backup archives).
|
||||
* It is possible to combine differents instances to affect which archive to keep or delete.
|
||||
*/
|
||||
public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTime>> {
|
||||
|
||||
private static final boolean testOnly = false; // if true, no files are deleted
|
||||
|
||||
/**
|
||||
* Creates a {@link BackupCleaner} that keeps the n last archives in the backup directory.
|
||||
* @param n the number of last archives to keep.
|
||||
* @return a {@link BackupCleaner} that keeps the n last archives in the backup directory.
|
||||
*/
|
||||
public static BackupCleaner KEEPING_N_LAST(int n) {
|
||||
return new BackupCleaner() {
|
||||
@Override
|
||||
@ -32,15 +41,23 @@ public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTi
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link BackupCleaner} that keeps one archive every n month.
|
||||
* <p>
|
||||
* This cleaner divides each year into sections of n month. For each month, its compute a section id using the
|
||||
* formula <code><i>YEAR</i> * (12 / <i>n</i>) + <i>MONTH</i> / <i>n</i></code>. It then keeps the first archive
|
||||
* found in each section.
|
||||
*
|
||||
* @param n the interval in month between each kept archives. Must be a dividor of 12 (1, 2, 3, 4, 6 or 12).
|
||||
* @return a {@link BackupCleaner} that keeps one archive every n month.
|
||||
*/
|
||||
public static BackupCleaner KEEPING_1_EVERY_N_MONTH(int n) {
|
||||
return new BackupCleaner() {
|
||||
@Override
|
||||
public TreeSet<LocalDateTime> apply(TreeSet<LocalDateTime> localDateTimes) {
|
||||
return localDateTimes.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
ldt -> {
|
||||
return ldt.getYear() * 4 + ldt.getMonthValue() / n;
|
||||
},
|
||||
ldt -> ldt.getYear() * (12 / n) + ldt.getMonthValue() / n,
|
||||
TreeMap::new,
|
||||
Collectors.minBy(LocalDateTime::compareTo))
|
||||
)
|
||||
@ -54,8 +71,13 @@ public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTi
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link BackupCleaner} that keeps the archives kept by this {@link BackupCleaner} or by the provided
|
||||
* one.
|
||||
* In other word, it makes a union operation with the set of archives kept by both original {@link BackupCleaner}.
|
||||
* @param other the other {@link BackupCleaner} to merge with.
|
||||
* @return a new {@link BackupCleaner}. The original ones are not affected.
|
||||
*/
|
||||
public BackupCleaner merge(BackupCleaner other) {
|
||||
BackupCleaner self = this;
|
||||
return new BackupCleaner() {
|
||||
@ -70,8 +92,11 @@ public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTi
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Performs the cleanup operation on the provided directory.
|
||||
* @param archiveDir the backup directory to cleanup.
|
||||
* @param compressDisplayName the displayname of the backup process that manages the backup directory. Used for logs.
|
||||
*/
|
||||
public void cleanupArchives(File archiveDir, String compressDisplayName) {
|
||||
String[] files = archiveDir.list();
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
package fr.pandacube.lib.core.backup;
|
||||
|
||||
import fc.cron.CronExpression;
|
||||
import fr.pandacube.lib.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -13,20 +10,32 @@ import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
/**
|
||||
* Handles the backup processes.
|
||||
*/
|
||||
public class BackupManager extends TimerTask {
|
||||
|
||||
private final File backupDirectory;
|
||||
|
||||
/**
|
||||
* The {@link Persist} instance of this {@link BackupManager}.
|
||||
*/
|
||||
protected final Persist persist;
|
||||
|
||||
/**
|
||||
* The list of backup processes that are scheduled.
|
||||
*/
|
||||
protected final List<BackupProcess> backupQueue = new ArrayList<>();
|
||||
|
||||
/* package */ final AtomicReference<BackupProcess> runningBackup = new AtomicReference<>();
|
||||
|
||||
private final Timer schedulerTimer = new Timer();
|
||||
|
||||
/**
|
||||
* Instanciate a new backup manager.
|
||||
* @param backupDirectory the root backup directory.
|
||||
*/
|
||||
public BackupManager(File backupDirectory) {
|
||||
this.backupDirectory = backupDirectory;
|
||||
persist = new Persist(this);
|
||||
@ -37,17 +46,26 @@ public class BackupManager extends TimerTask {
|
||||
schedulerTimer.scheduleAtFixedRate(this, new Date(nextMinute), 60_000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new backup process to the queue.
|
||||
* @param process the backup process to add.
|
||||
*/
|
||||
protected void addProcess(BackupProcess process) {
|
||||
process.displayNextSchedule();
|
||||
backupQueue.add(process);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the backup root directory.
|
||||
* @return the backup root directory.
|
||||
*/
|
||||
public File getBackupDirectory() {
|
||||
return backupDirectory;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public synchronized void run() {
|
||||
BackupProcess tmp;
|
||||
if ((tmp = runningBackup.get()) != null) {
|
||||
@ -65,7 +83,10 @@ public class BackupManager extends TimerTask {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Disables this backup manager, canceling scheduled backups.
|
||||
* It will wait for a currently running backup to finish before returning.
|
||||
*/
|
||||
public synchronized void onDisable() {
|
||||
|
||||
schedulerTimer.cancel();
|
||||
@ -88,8 +109,6 @@ public class BackupManager extends TimerTask {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
persist.save();
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,20 +7,30 @@ import fr.pandacube.lib.util.Log;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DateFormat;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
/**
|
||||
* A backup process.
|
||||
*/
|
||||
public abstract class BackupProcess implements Comparable<BackupProcess>, Runnable {
|
||||
private final BackupManager backupManager;
|
||||
|
||||
/**
|
||||
* The process identifier.
|
||||
*/
|
||||
public final String identifier;
|
||||
|
||||
|
||||
/**
|
||||
* The zip compressor.
|
||||
*/
|
||||
protected ZipCompressor compressor = null;
|
||||
|
||||
|
||||
@ -29,20 +39,37 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
private BackupCleaner backupCleaner = null;
|
||||
private List<String> ignoreList = new ArrayList<>();
|
||||
|
||||
|
||||
/**
|
||||
* Instanciates a new backup process.
|
||||
* @param bm the associated backup manager.
|
||||
* @param n the process identifier.
|
||||
*/
|
||||
protected BackupProcess(BackupManager bm, final String n) {
|
||||
backupManager = bm;
|
||||
identifier = n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated backup manager.
|
||||
* @return the associated backup manager.
|
||||
*/
|
||||
public BackupManager getBackupManager() {
|
||||
return backupManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the process identifier.
|
||||
* @return the process identifier.
|
||||
*/
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the displayname of this process.
|
||||
* Default implementation returns {@link #getIdentifier()}.
|
||||
* @return the displayname of this process.
|
||||
*/
|
||||
protected String getDisplayName() {
|
||||
return getIdentifier();
|
||||
}
|
||||
@ -55,9 +82,11 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provides a predicate that tells if a provided file must be included in the archive or not.
|
||||
* The default implementation returns a filter based on the content of {@link #getIgnoreList()}.
|
||||
* @return a predicate.
|
||||
*/
|
||||
public BiPredicate<File, String> getFilenameFilter() {
|
||||
return (file, path) -> {
|
||||
for (String exclude : ignoreList) {
|
||||
@ -75,47 +104,91 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source directory to backup.
|
||||
* @return the source directory to backup.
|
||||
*/
|
||||
public abstract File getSourceDir();
|
||||
|
||||
/**
|
||||
* Gets the directory in which to put the archives.
|
||||
* @return the directory in which to put the archives.
|
||||
*/
|
||||
protected abstract File getTargetDir();
|
||||
|
||||
/**
|
||||
* Called when the backup starts.
|
||||
*/
|
||||
protected abstract void onBackupStart();
|
||||
|
||||
/**
|
||||
* Called when the backup ends.
|
||||
* @param success true if the backup ended successfuly.
|
||||
*/
|
||||
protected abstract void onBackupEnd(boolean success);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tells if this backup process is enabled.
|
||||
* A disabled backup process will not run.
|
||||
* @return true if this backup process is enabled, false otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled status of this backup process.
|
||||
* @param enabled the enabled status of this backup process.
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the scheduling, using cron format.
|
||||
* @return the string representation of the scheduling.
|
||||
*/
|
||||
public String getScheduling() {
|
||||
return scheduling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string representation of the scheduling.
|
||||
* @param scheduling the string representation of the scheduling, in the CRON format (without seconds).
|
||||
*/
|
||||
public void setScheduling(String scheduling) {
|
||||
this.scheduling = scheduling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated backup cleaner, that is executed at the end of this backup process.
|
||||
* @return the associated backup cleaner.
|
||||
*/
|
||||
public BackupCleaner getBackupCleaner() {
|
||||
return backupCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the backup cleaner of this backup process.
|
||||
* @param backupCleaner the backup cleaner of this backup process.
|
||||
*/
|
||||
public void setBackupCleaner(BackupCleaner backupCleaner) {
|
||||
this.backupCleaner = backupCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current list of files that are ignored during the backup process.
|
||||
* @return the current list of files that are ignored during the backup process.
|
||||
*/
|
||||
public List<String> getIgnoreList() {
|
||||
return ignoreList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new list of files that will be ignored during the backup process.
|
||||
* @param ignoreList the new list of files that are ignored during the backup process.
|
||||
*/
|
||||
public void setIgnoreList(List<String> ignoreList) {
|
||||
this.ignoreList = ignoreList;
|
||||
}
|
||||
@ -190,17 +263,18 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs the scheduling status of this backup process.
|
||||
*/
|
||||
public void displayNextSchedule() {
|
||||
Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " next backup on "
|
||||
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(getNext())));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract void displayNextSchedule();
|
||||
|
||||
|
||||
/**
|
||||
* A formatter used to format and parse the name of backup archives, based on a date and time.
|
||||
*/
|
||||
public static final DateTimeFormatter dateFileNameFormatter = new DateTimeFormatterBuilder()
|
||||
.appendValue(ChronoField.YEAR, 4)
|
||||
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||
@ -216,7 +290,10 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
return dateFileNameFormatter.format(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs the progress of this currently running backup process.
|
||||
* Logs nothing if this backup is not in progress.
|
||||
*/
|
||||
public void logProgress() {
|
||||
if (compressor == null)
|
||||
return;
|
||||
@ -224,10 +301,10 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tells if this backup process could start now.
|
||||
* @return true if this backup process could start now, false otherwise.
|
||||
*/
|
||||
public boolean couldRunNow() {
|
||||
if (!isEnabled())
|
||||
return false;
|
||||
@ -239,26 +316,43 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the time of the next scheduled run.
|
||||
* @return the time, in millis-timestamp, of the next scheduled run, or {@link Long#MAX_VALUE} if it’s not scheduled.
|
||||
*/
|
||||
public long getNext() {
|
||||
if (!hasNextScheduled())
|
||||
return Long.MAX_VALUE;
|
||||
return getNextCompress(backupManager.persist.isDirtySince(identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if this backup is scheduled or not.
|
||||
* @return true if this backup is scheduled, false otherwise.
|
||||
*/
|
||||
public boolean hasNextScheduled() {
|
||||
return isEnabled() && isDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the content to be backed up is dirty or not. The source data is not dirty if it has not changed since
|
||||
* the last backup.
|
||||
* @return the dirty status of the data to be backed-up by this backup process.
|
||||
*/
|
||||
public boolean isDirty() {
|
||||
return backupManager.persist.isDirty(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source data as dirty since now.
|
||||
*/
|
||||
public void setDirtySinceNow() {
|
||||
backupManager.persist.setDirtySinceNow(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source data as not dirty.
|
||||
*/
|
||||
public void setNotDirty() {
|
||||
backupManager.persist.setNotDirty(identifier);
|
||||
}
|
||||
@ -268,9 +362,9 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
||||
|
||||
|
||||
/**
|
||||
* get the timestamp (in ms) of when the next compress will run, depending on since when the files to compress are dirty.
|
||||
* @param dirtySince the timestamp in ms since the files are dirty
|
||||
* @return the timestamp in ms when the next compress of the files should be run, or 0 if it is not yet scheduled
|
||||
* Gets the millis-timestamp of when the next compress will run, depending on since when the files to compress are dirty.
|
||||
* @param dirtySince the timestamp in ms since the files are dirty.
|
||||
* @return the timestamp in ms when the next compress of the files should be run, or 0 if it is not yet scheduled.
|
||||
*/
|
||||
public long getNextCompress(long dirtySince) {
|
||||
if (dirtySince == -1)
|
||||
|
@ -12,6 +12,11 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Handles the data stored used for backup manager, like dirty status of data to be backed up.
|
||||
* The data is stored using JSON format, in a file in the root backup directory.
|
||||
* The file is updated on disk on every call to a {@code set*(...)} method.
|
||||
*/
|
||||
public class Persist {
|
||||
|
||||
private Map<String, Long> dirtySince = new HashMap<>();
|
||||
@ -19,17 +24,18 @@ public class Persist {
|
||||
private final File file;
|
||||
|
||||
// private final Set<String> dirtyWorldsSave = new HashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance, immediatly loading the data from the file if it exists, or creating an empty one if not.
|
||||
* @param bm the associated backup manager.
|
||||
*/
|
||||
public Persist(BackupManager bm) {
|
||||
file = new File(bm.getBackupDirectory(), "source-dirty-since.json");
|
||||
load();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
load();
|
||||
}
|
||||
|
||||
protected void load() {
|
||||
private void load() {
|
||||
boolean loaded = false;
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
dirtySince = Json.gson.fromJson(reader, new TypeToken<Map<String, Long>>(){}.getType());
|
||||
@ -48,8 +54,8 @@ public class Persist {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
||||
private void save() {
|
||||
try (FileWriter writer = new FileWriter(file, false)) {
|
||||
Json.gsonPrettyPrinting.toJson(dirtySince, writer);
|
||||
}
|
||||
@ -57,27 +63,32 @@ public class Persist {
|
||||
Log.severe("could not save " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public void setDirtySinceNow(String id) {
|
||||
|
||||
/**
|
||||
* Sets the backup process with the provided id as dirty.
|
||||
* @param id the id of the backup process.
|
||||
*/
|
||||
public synchronized void setDirtySinceNow(String id) {
|
||||
dirtySince.put(id, System.currentTimeMillis());
|
||||
save();
|
||||
}
|
||||
|
||||
public void setNotDirty(String id) {
|
||||
/**
|
||||
* Sets the backup process with the provided id as not dirty.
|
||||
* @param id the id of the backup process.
|
||||
*/
|
||||
public synchronized void setNotDirty(String id) {
|
||||
dirtySince.put(id, -1L);
|
||||
save();
|
||||
}
|
||||
|
||||
|
||||
public boolean isDirty(String id) {
|
||||
|
||||
|
||||
public synchronized boolean isDirty(String id) {
|
||||
return isDirtySince(id) != -1;
|
||||
}
|
||||
|
||||
public long isDirtySince(String id) {
|
||||
public synchronized long isDirtySince(String id) {
|
||||
if (!dirtySince.containsKey(id))
|
||||
setDirtySinceNow(id);
|
||||
return dirtySince.get(id);
|
||||
|
@ -120,10 +120,4 @@ public class RotatedLogsBackupProcess extends BackupProcess {
|
||||
protected void onBackupEnd(boolean success) {
|
||||
setDirtySinceNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayNextSchedule() {
|
||||
Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " next backup on "
|
||||
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(getNext())));
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
// TODO Add support for persisted last execution timestamps
|
||||
/**
|
||||
* Application wide task scheduler using Cron expression.
|
||||
*/
|
||||
@ -78,8 +77,10 @@ public class CronScheduler {
|
||||
|
||||
/**
|
||||
* Schedule a task.
|
||||
* If a task with the provided taskId already exists, it will be replaced.
|
||||
* @param taskId the id of the task.
|
||||
* @param cronExpression the scheduling of the task. May use seconds (6 values) or not (5 values)
|
||||
* @param cronExpression the scheduling of the task. May use seconds (6 values) or not (5 values).
|
||||
* See {@link CronExpression} for the format.
|
||||
* @param task the task to run.
|
||||
*/
|
||||
public static void schedule(String taskId, String cronExpression, Runnable task) {
|
||||
@ -185,8 +186,6 @@ public class CronScheduler {
|
||||
catch (final JsonParseException e) {
|
||||
Log.severe("cannot load " + lastRunFile, e);
|
||||
}
|
||||
finally {
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
saveLastRuns();
|
||||
|
@ -53,10 +53,4 @@ public class PaperWorkdirProcess extends PaperBackupProcess {
|
||||
return "workdir";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void displayNextSchedule() {
|
||||
Log.info("[Backup] " + net.md_5.bungee.api.ChatColor.GRAY + getDisplayName() + net.md_5.bungee.api.ChatColor.RESET + " next backup on "
|
||||
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(getNext())));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user