diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index f86201424..b1d7f43a9 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -56,42 +56,53 @@ public class PlayerBackupProfile implements BackupProfile { private final Plot plot; private final BackupManager backupManager; + private volatile List backupCache; + private final Object backupLock = new Object(); + private static boolean isValidFile(@NotNull final Path path) { final String name = path.getFileName().toString(); return name.endsWith(".schem") || name.endsWith(".schematic"); } @Override @NotNull public CompletableFuture> listBackups() { - return CompletableFuture.supplyAsync(() -> { - final Path path = this.getBackupDirectory(); - if (!Files.exists(path)) { - try { - Files.createDirectories(path); - } catch (IOException e) { - e.printStackTrace(); - return Collections.emptyList(); - } + synchronized (this.backupLock) { + if (this.backupCache != null) { + return CompletableFuture.completedFuture(backupCache); } - final List backups = new ArrayList<>(); - try { - Files.walk(path).filter(PlayerBackupProfile::isValidFile).forEach(file -> { + return CompletableFuture.supplyAsync(() -> { + final Path path = this.getBackupDirectory(); + if (!Files.exists(path)) { try { - final BasicFileAttributes - basicFileAttributes = Files.readAttributes(file, BasicFileAttributes.class); - backups.add(new Backup(this, basicFileAttributes.creationTime().toMillis(), file)); + Files.createDirectories(path); } catch (IOException e) { e.printStackTrace(); + return Collections.emptyList(); } - }); - } catch (IOException e) { - e.printStackTrace(); - } - return backups; - }); + } + final List backups = new ArrayList<>(); + try { + Files.walk(path).filter(PlayerBackupProfile::isValidFile).forEach(file -> { + try { + final BasicFileAttributes basicFileAttributes = + Files.readAttributes(file, BasicFileAttributes.class); + backups.add( + new Backup(this, basicFileAttributes.creationTime().toMillis(), file)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + return backups; + }); + } } @Override public void destroy() throws IOException { Files.delete(this.getBackupDirectory()); + // Invalidate backup cache + this.backupCache = null; } @NotNull public Path getBackupDirectory() { @@ -102,14 +113,17 @@ public class PlayerBackupProfile implements BackupProfile { @Override @NotNull public CompletableFuture createBackup() { final CompletableFuture future = new CompletableFuture<>(); this.listBackups().thenAcceptAsync(backups -> { - if (backups.size() == backupManager.getBackupLimit()) { - backups.get(backups.size() - 1).delete(); - } - final List plots = Collections.singletonList(plot); - final boolean result = SchematicHandler.manager.exportAll(plots, null, null, () -> - future.complete(new Backup(this, System.currentTimeMillis(), null))); - if (!result) { - future.completeExceptionally(new RuntimeException("Failed to complete the backup")); + synchronized (this.backupLock) { + if (backups.size() == backupManager.getBackupLimit()) { + backups.get(backups.size() - 1).delete(); + } + final List plots = Collections.singletonList(plot); + final boolean result = SchematicHandler.manager.exportAll(plots, null, null, () -> + future.complete(new Backup(this, System.currentTimeMillis(), null))); + if (!result) { + future.completeExceptionally(new RuntimeException("Failed to complete the backup")); + } + this.backupCache = null; } }); return future; diff --git a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java index 492b5ed60..4c206b9ad 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java +++ b/Core/src/main/java/com/plotsquared/core/backup/SimpleBackupManager.java @@ -25,9 +25,12 @@ */ package com.plotsquared.core.backup; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.plot.Plot; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -36,6 +39,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; /** * {@inheritDoc} @@ -45,6 +50,8 @@ import java.util.concurrent.CompletableFuture; @Getter private final Path backupPath; private final boolean automaticBackup; @Getter private final int backupLimit; + private final Cache backupProfileCache = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES).build(); public SimpleBackupManager() throws Exception { this.backupPath = Objects.requireNonNull(PlotSquared.imp()).getDirectory().toPath().resolve("backups"); @@ -57,7 +64,13 @@ import java.util.concurrent.CompletableFuture; @Override @NotNull public BackupProfile getProfile(@NotNull final Plot plot) { if (plot.hasOwner() && !plot.isMerged()) { - return new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); + try { + return backupProfileCache.get(new PlotCacheKey(plot), () -> new PlayerBackupProfile(plot.getOwnerAbs(), plot, this)); + } catch (ExecutionException e) { + final BackupProfile profile = new PlayerBackupProfile(plot.getOwnerAbs(), plot, this); + this.backupProfileCache.put(new PlotCacheKey(plot), profile); + return profile; + } } return new NullBackupProfile(); } @@ -74,4 +87,26 @@ import java.util.concurrent.CompletableFuture; return this.automaticBackup; } + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) private static final class PlotCacheKey { + + private final Plot plot; + + @Override public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PlotCacheKey that = (PlotCacheKey) o; + return com.google.common.base.Objects.equal(plot.getArea(), that.plot.getArea()) + && com.google.common.base.Objects.equal(plot.getId(), that.plot.getId()) + && com.google.common.base.Objects.equal(plot.getOwnerAbs(), that.plot.getOwnerAbs()); + } + + @Override public int hashCode() { + return com.google.common.base.Objects.hashCode(plot.getArea(), plot.getId(), plot.getOwnerAbs()); + } + } + }