Compare commits
2 Commits
33b40f0cba
...
dd2b4467ed
Author | SHA1 | Date | |
---|---|---|---|
dd2b4467ed | |||
6577367c27 |
@ -60,10 +60,4 @@ public class BungeeWorkdirProcess extends BackupProcess {
|
|||||||
return "workdir";
|
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;
|
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>> {
|
public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTime>> {
|
||||||
|
|
||||||
private static final boolean testOnly = false; // if true, no files are deleted
|
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) {
|
public static BackupCleaner KEEPING_N_LAST(int n) {
|
||||||
return new BackupCleaner() {
|
return new BackupCleaner() {
|
||||||
@Override
|
@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) {
|
public static BackupCleaner KEEPING_1_EVERY_N_MONTH(int n) {
|
||||||
return new BackupCleaner() {
|
return new BackupCleaner() {
|
||||||
@Override
|
@Override
|
||||||
public TreeSet<LocalDateTime> apply(TreeSet<LocalDateTime> localDateTimes) {
|
public TreeSet<LocalDateTime> apply(TreeSet<LocalDateTime> localDateTimes) {
|
||||||
return localDateTimes.stream()
|
return localDateTimes.stream()
|
||||||
.collect(Collectors.groupingBy(
|
.collect(Collectors.groupingBy(
|
||||||
ldt -> {
|
ldt -> ldt.getYear() * (12 / n) + ldt.getMonthValue() / n,
|
||||||
return ldt.getYear() * 4 + ldt.getMonthValue() / n;
|
|
||||||
},
|
|
||||||
TreeMap::new,
|
TreeMap::new,
|
||||||
Collectors.minBy(LocalDateTime::compareTo))
|
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) {
|
public BackupCleaner merge(BackupCleaner other) {
|
||||||
BackupCleaner self = this;
|
BackupCleaner self = this;
|
||||||
return new BackupCleaner() {
|
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) {
|
public void cleanupArchives(File archiveDir, String compressDisplayName) {
|
||||||
String[] files = archiveDir.list();
|
String[] files = archiveDir.list();
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package fr.pandacube.lib.core.backup;
|
package fr.pandacube.lib.core.backup;
|
||||||
|
|
||||||
import fc.cron.CronExpression;
|
|
||||||
import fr.pandacube.lib.util.Log;
|
import fr.pandacube.lib.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -13,20 +10,32 @@ import java.util.List;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.LongStream;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the backup processes.
|
||||||
|
*/
|
||||||
public class BackupManager extends TimerTask {
|
public class BackupManager extends TimerTask {
|
||||||
|
|
||||||
private final File backupDirectory;
|
private final File backupDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Persist} instance of this {@link BackupManager}.
|
||||||
|
*/
|
||||||
protected final Persist persist;
|
protected final Persist persist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of backup processes that are scheduled.
|
||||||
|
*/
|
||||||
protected final List<BackupProcess> backupQueue = new ArrayList<>();
|
protected final List<BackupProcess> backupQueue = new ArrayList<>();
|
||||||
|
|
||||||
/* package */ final AtomicReference<BackupProcess> runningBackup = new AtomicReference<>();
|
/* package */ final AtomicReference<BackupProcess> runningBackup = new AtomicReference<>();
|
||||||
|
|
||||||
private final Timer schedulerTimer = new Timer();
|
private final Timer schedulerTimer = new Timer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instanciate a new backup manager.
|
||||||
|
* @param backupDirectory the root backup directory.
|
||||||
|
*/
|
||||||
public BackupManager(File backupDirectory) {
|
public BackupManager(File backupDirectory) {
|
||||||
this.backupDirectory = backupDirectory;
|
this.backupDirectory = backupDirectory;
|
||||||
persist = new Persist(this);
|
persist = new Persist(this);
|
||||||
@ -37,17 +46,26 @@ public class BackupManager extends TimerTask {
|
|||||||
schedulerTimer.scheduleAtFixedRate(this, new Date(nextMinute), 60_000);
|
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) {
|
protected void addProcess(BackupProcess process) {
|
||||||
process.displayNextSchedule();
|
process.displayNextSchedule();
|
||||||
backupQueue.add(process);
|
backupQueue.add(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the backup root directory.
|
||||||
|
* @return the backup root directory.
|
||||||
|
*/
|
||||||
public File getBackupDirectory() {
|
public File getBackupDirectory() {
|
||||||
return backupDirectory;
|
return backupDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public synchronized void run() {
|
public synchronized void run() {
|
||||||
BackupProcess tmp;
|
BackupProcess tmp;
|
||||||
if ((tmp = runningBackup.get()) != null) {
|
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() {
|
public synchronized void onDisable() {
|
||||||
|
|
||||||
schedulerTimer.cancel();
|
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 net.md_5.bungee.api.ChatColor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.text.DateFormat;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A backup process.
|
||||||
|
*/
|
||||||
public abstract class BackupProcess implements Comparable<BackupProcess>, Runnable {
|
public abstract class BackupProcess implements Comparable<BackupProcess>, Runnable {
|
||||||
private final BackupManager backupManager;
|
private final BackupManager backupManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The process identifier.
|
||||||
|
*/
|
||||||
public final String identifier;
|
public final String identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The zip compressor.
|
||||||
|
*/
|
||||||
protected ZipCompressor compressor = null;
|
protected ZipCompressor compressor = null;
|
||||||
|
|
||||||
|
|
||||||
@ -29,20 +39,37 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
|||||||
private BackupCleaner backupCleaner = null;
|
private BackupCleaner backupCleaner = null;
|
||||||
private List<String> ignoreList = new ArrayList<>();
|
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) {
|
protected BackupProcess(BackupManager bm, final String n) {
|
||||||
backupManager = bm;
|
backupManager = bm;
|
||||||
identifier = n;
|
identifier = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the associated backup manager.
|
||||||
|
* @return the associated backup manager.
|
||||||
|
*/
|
||||||
public BackupManager getBackupManager() {
|
public BackupManager getBackupManager() {
|
||||||
return backupManager;
|
return backupManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the process identifier.
|
||||||
|
* @return the process identifier.
|
||||||
|
*/
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the displayname of this process.
|
||||||
|
* Default implementation returns {@link #getIdentifier()}.
|
||||||
|
* @return the displayname of this process.
|
||||||
|
*/
|
||||||
protected String getDisplayName() {
|
protected String getDisplayName() {
|
||||||
return getIdentifier();
|
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() {
|
public BiPredicate<File, String> getFilenameFilter() {
|
||||||
return (file, path) -> {
|
return (file, path) -> {
|
||||||
for (String exclude : ignoreList) {
|
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();
|
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();
|
protected abstract File getTargetDir();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the backup starts.
|
||||||
|
*/
|
||||||
protected abstract void onBackupStart();
|
protected abstract void onBackupStart();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the backup ends.
|
||||||
|
* @param success true if the backup ended successfuly.
|
||||||
|
*/
|
||||||
protected abstract void onBackupEnd(boolean success);
|
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() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the enabled status of this backup process.
|
||||||
|
* @param enabled the enabled status of this backup process.
|
||||||
|
*/
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string representation of the scheduling, using cron format.
|
||||||
|
* @return the string representation of the scheduling.
|
||||||
|
*/
|
||||||
public String getScheduling() {
|
public String getScheduling() {
|
||||||
return scheduling;
|
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) {
|
public void setScheduling(String scheduling) {
|
||||||
this.scheduling = 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() {
|
public BackupCleaner getBackupCleaner() {
|
||||||
return backupCleaner;
|
return backupCleaner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the backup cleaner of this backup process.
|
||||||
|
* @param backupCleaner the backup cleaner of this backup process.
|
||||||
|
*/
|
||||||
public void setBackupCleaner(BackupCleaner backupCleaner) {
|
public void setBackupCleaner(BackupCleaner backupCleaner) {
|
||||||
this.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() {
|
public List<String> getIgnoreList() {
|
||||||
return ignoreList;
|
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) {
|
public void setIgnoreList(List<String> ignoreList) {
|
||||||
this.ignoreList = 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())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A formatter used to format and parse the name of backup archives, based on a date and time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract void displayNextSchedule();
|
|
||||||
|
|
||||||
|
|
||||||
public static final DateTimeFormatter dateFileNameFormatter = new DateTimeFormatterBuilder()
|
public static final DateTimeFormatter dateFileNameFormatter = new DateTimeFormatterBuilder()
|
||||||
.appendValue(ChronoField.YEAR, 4)
|
.appendValue(ChronoField.YEAR, 4)
|
||||||
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
|
||||||
@ -216,7 +290,10 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab
|
|||||||
return dateFileNameFormatter.format(ZonedDateTime.now());
|
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() {
|
public void logProgress() {
|
||||||
if (compressor == null)
|
if (compressor == null)
|
||||||
return;
|
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() {
|
public boolean couldRunNow() {
|
||||||
if (!isEnabled())
|
if (!isEnabled())
|
||||||
return false;
|
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() {
|
public long getNext() {
|
||||||
if (!hasNextScheduled())
|
if (!hasNextScheduled())
|
||||||
return Long.MAX_VALUE;
|
return Long.MAX_VALUE;
|
||||||
return getNextCompress(backupManager.persist.isDirtySince(identifier));
|
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() {
|
public boolean hasNextScheduled() {
|
||||||
return isEnabled() && isDirty();
|
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() {
|
public boolean isDirty() {
|
||||||
return backupManager.persist.isDirty(identifier);
|
return backupManager.persist.isDirty(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the source data as dirty since now.
|
||||||
|
*/
|
||||||
public void setDirtySinceNow() {
|
public void setDirtySinceNow() {
|
||||||
backupManager.persist.setDirtySinceNow(identifier);
|
backupManager.persist.setDirtySinceNow(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the source data as not dirty.
|
||||||
|
*/
|
||||||
public void setNotDirty() {
|
public void setNotDirty() {
|
||||||
backupManager.persist.setNotDirty(identifier);
|
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.
|
* 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
|
* @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
|
* @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) {
|
public long getNextCompress(long dirtySince) {
|
||||||
if (dirtySince == -1)
|
if (dirtySince == -1)
|
||||||
|
@ -12,6 +12,11 @@ import java.io.IOException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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 {
|
public class Persist {
|
||||||
|
|
||||||
private Map<String, Long> dirtySince = new HashMap<>();
|
private Map<String, Long> dirtySince = new HashMap<>();
|
||||||
@ -20,16 +25,17 @@ public class Persist {
|
|||||||
|
|
||||||
// private final Set<String> dirtyWorldsSave = new HashSet<>();
|
// 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) {
|
public Persist(BackupManager bm) {
|
||||||
file = new File(bm.getBackupDirectory(), "source-dirty-since.json");
|
file = new File(bm.getBackupDirectory(), "source-dirty-since.json");
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reload() {
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void load() {
|
private void load() {
|
||||||
boolean loaded = false;
|
boolean loaded = false;
|
||||||
try (FileReader reader = new FileReader(file)) {
|
try (FileReader reader = new FileReader(file)) {
|
||||||
dirtySince = Json.gson.fromJson(reader, new TypeToken<Map<String, Long>>(){}.getType());
|
dirtySince = Json.gson.fromJson(reader, new TypeToken<Map<String, Long>>(){}.getType());
|
||||||
@ -49,7 +55,7 @@ public class Persist {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save() {
|
private void save() {
|
||||||
try (FileWriter writer = new FileWriter(file, false)) {
|
try (FileWriter writer = new FileWriter(file, false)) {
|
||||||
Json.gsonPrettyPrinting.toJson(dirtySince, writer);
|
Json.gsonPrettyPrinting.toJson(dirtySince, writer);
|
||||||
}
|
}
|
||||||
@ -59,25 +65,30 @@ public class Persist {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the backup process with the provided id as dirty.
|
||||||
|
* @param id the id of the backup process.
|
||||||
public void setDirtySinceNow(String id) {
|
*/
|
||||||
|
public synchronized void setDirtySinceNow(String id) {
|
||||||
dirtySince.put(id, System.currentTimeMillis());
|
dirtySince.put(id, System.currentTimeMillis());
|
||||||
save();
|
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);
|
dirtySince.put(id, -1L);
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isDirty(String id) {
|
public synchronized boolean isDirty(String id) {
|
||||||
return isDirtySince(id) != -1;
|
return isDirtySince(id) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long isDirtySince(String id) {
|
public synchronized long isDirtySince(String id) {
|
||||||
if (!dirtySince.containsKey(id))
|
if (!dirtySince.containsKey(id))
|
||||||
setDirtySinceNow(id);
|
setDirtySinceNow(id);
|
||||||
return dirtySince.get(id);
|
return dirtySince.get(id);
|
||||||
|
@ -120,10 +120,4 @@ public class RotatedLogsBackupProcess extends BackupProcess {
|
|||||||
protected void onBackupEnd(boolean success) {
|
protected void onBackupEnd(boolean success) {
|
||||||
setDirtySinceNow();
|
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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// TODO Add support for persisted last execution timestamps
|
|
||||||
/**
|
/**
|
||||||
* Application wide task scheduler using Cron expression.
|
* Application wide task scheduler using Cron expression.
|
||||||
*/
|
*/
|
||||||
@ -78,8 +77,10 @@ public class CronScheduler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule a task.
|
* Schedule a task.
|
||||||
|
* If a task with the provided taskId already exists, it will be replaced.
|
||||||
* @param taskId the id of the task.
|
* @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.
|
* @param task the task to run.
|
||||||
*/
|
*/
|
||||||
public static void schedule(String taskId, String cronExpression, Runnable task) {
|
public static void schedule(String taskId, String cronExpression, Runnable task) {
|
||||||
@ -185,8 +186,6 @@ public class CronScheduler {
|
|||||||
catch (final JsonParseException e) {
|
catch (final JsonParseException e) {
|
||||||
Log.severe("cannot load " + lastRunFile, e);
|
Log.severe("cannot load " + lastRunFile, e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
saveLastRuns();
|
saveLastRuns();
|
||||||
|
@ -53,10 +53,4 @@ public class PaperWorkdirProcess extends PaperBackupProcess {
|
|||||||
return "workdir";
|
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())));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -139,4 +139,24 @@ public class StringUtil {
|
|||||||
return s -> Long.toString(operator.apply(Long.parseLong(s)));
|
return s -> Long.toString(operator.apply(Long.parseLong(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a {@link Pattern} with extra wrapping regex around the provided one to consider a sentense (like a chat
|
||||||
|
* message). For instance, the returned pattern will only match the expression at the beginning or end of sentence,
|
||||||
|
* or separated by the rest of it with space or another non-letter character.
|
||||||
|
* @param wordPattern the regex pattern to wrap.
|
||||||
|
* @param caseInsensitive if the pattern must match ignoring case.
|
||||||
|
* @return a {@link Pattern}. The matching will match 3 groups. The first group is the eventual non-letter separator
|
||||||
|
* before the matched word, the second one is the actual word, and the last one is the eventual non-letter separator
|
||||||
|
* after the matched word. Any additionnal pattern group between the 2nd and the last one are thoses provided in the
|
||||||
|
* wordPattern.
|
||||||
|
*/
|
||||||
|
public static Pattern asPatternInSentense(String wordPattern, boolean caseInsensitive) {
|
||||||
|
return Pattern.compile((caseInsensitive ? "(?i)" : "") + "(\\P{L}|^)(" + wordPattern + ")(\\P{L}|$)");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user