mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2025-02-16 20:41:57 +01:00
Generalize purge task for all storages
This commit is contained in:
parent
c2499df3a7
commit
f3f609c573
@ -61,7 +61,7 @@ public boolean scheduleMapUpdateTask(BlueMapMap map, Collection<Vector2i> region
|
|||||||
@Override
|
@Override
|
||||||
public boolean scheduleMapPurgeTask(BlueMapMap map) throws IOException {
|
public boolean scheduleMapPurgeTask(BlueMapMap map) throws IOException {
|
||||||
BlueMapMapImpl cmap = castMap(map);
|
BlueMapMapImpl cmap = castMap(map);
|
||||||
return renderManager.scheduleRenderTask(MapPurgeTask.create(cmap.getBmMap()));
|
return renderManager.scheduleRenderTask(new MapPurgeTask(cmap.getBmMap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -796,7 +796,7 @@ public int purgeCommand(CommandContext<S> context) {
|
|||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
// delete map
|
// delete map
|
||||||
MapPurgeTask purgeTask = MapPurgeTask.create(map);
|
MapPurgeTask purgeTask = new MapPurgeTask(map);
|
||||||
|
|
||||||
plugin.getRenderManager().scheduleRenderTaskNext(purgeTask);
|
plugin.getRenderManager().scheduleRenderTaskNext(purgeTask);
|
||||||
source.sendMessage(Text.of(TextColor.GREEN, "Created new Task to purge map '" + map.getId() + "'"));
|
source.sendMessage(Text.of(TextColor.GREEN, "Created new Task to purge map '" + map.getId() + "'"));
|
||||||
@ -817,7 +817,7 @@ public int purgeCommand(CommandContext<S> context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
source.sendMessage(Text.of(TextColor.GREEN, "Use ", TextColor.GRAY, "/bluemap", TextColor.GREEN, " to see the progress."));
|
source.sendMessage(Text.of(TextColor.GREEN, "Use ", TextColor.GRAY, "/bluemap", TextColor.GREEN, " to see the progress."));
|
||||||
} catch (IOException | IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
source.sendMessage(Text.of(TextColor.RED, "There was an error trying to purge '" + map.getId() + "', see console for details."));
|
source.sendMessage(Text.of(TextColor.RED, "There was an error trying to purge '" + map.getId() + "', see console for details."));
|
||||||
Logger.global.logError("Failed to purge map '" + map.getId() + "'!", e);
|
Logger.global.logError("Failed to purge map '" + map.getId() + "'!", e);
|
||||||
}
|
}
|
||||||
|
@ -26,191 +26,78 @@
|
|||||||
|
|
||||||
import de.bluecolored.bluemap.api.debug.DebugDump;
|
import de.bluecolored.bluemap.api.debug.DebugDump;
|
||||||
import de.bluecolored.bluemap.core.map.BmMap;
|
import de.bluecolored.bluemap.core.map.BmMap;
|
||||||
import de.bluecolored.bluemap.core.storage.Storage;
|
|
||||||
import de.bluecolored.bluemap.core.storage.file.FileStorage;
|
|
||||||
import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public abstract class MapPurgeTask implements RenderTask {
|
public class MapPurgeTask implements RenderTask {
|
||||||
|
|
||||||
public static MapPurgeTask create(BmMap map) throws IOException {
|
private final BmMap map;
|
||||||
Storage storage = map.getStorage();
|
|
||||||
if (storage instanceof FileStorage) {
|
private volatile double progress;
|
||||||
return new MapFilePurgeTask(map, (FileStorage) storage);
|
private volatile boolean hasMoreWork;
|
||||||
} else {
|
private volatile boolean cancelled;
|
||||||
return new MapStoragePurgeTask(map);
|
|
||||||
}
|
public MapPurgeTask(BmMap map) {
|
||||||
|
this.map = Objects.requireNonNull(map);
|
||||||
|
this.progress = 0d;
|
||||||
|
this.hasMoreWork = true;
|
||||||
|
this.cancelled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MapPurgeTask create(Path mapDirectory) throws IOException {
|
@Override
|
||||||
return new MapFilePurgeTask(mapDirectory);
|
public void doWork() throws Exception {
|
||||||
}
|
synchronized (this) {
|
||||||
|
if (!this.hasMoreWork) return;
|
||||||
@DebugDump
|
|
||||||
private static class MapFilePurgeTask extends MapPurgeTask {
|
|
||||||
|
|
||||||
@Nullable private final BmMap map;
|
|
||||||
private final Path directory;
|
|
||||||
private final int subFilesCount;
|
|
||||||
private final LinkedList<Path> subFiles;
|
|
||||||
|
|
||||||
private volatile boolean hasMoreWork;
|
|
||||||
private volatile boolean cancelled;
|
|
||||||
|
|
||||||
public MapFilePurgeTask(Path mapDirectory) throws IOException {
|
|
||||||
this(null, mapDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapFilePurgeTask(BmMap map, FileStorage fileStorage) throws IOException {
|
|
||||||
this(map, fileStorage.getFilePath(map.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private MapFilePurgeTask(@Nullable BmMap map, Path directory) throws IOException {
|
|
||||||
this.map = map;
|
|
||||||
this.directory = directory;
|
|
||||||
try (Stream<Path> pathStream = Files.walk(directory, 3)) {
|
|
||||||
this.subFiles = pathStream.collect(Collectors.toCollection(LinkedList::new));
|
|
||||||
}
|
|
||||||
this.subFilesCount = subFiles.size();
|
|
||||||
this.hasMoreWork = true;
|
|
||||||
this.cancelled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doWork() throws Exception {
|
|
||||||
synchronized (this) {
|
|
||||||
if (!this.hasMoreWork) return;
|
|
||||||
this.hasMoreWork = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// save lowres-tile-manager to clear/flush any buffered data
|
|
||||||
if (this.map != null) this.map.getLowresTileManager().save();
|
|
||||||
|
|
||||||
// delete subFiles first to be able to track the progress and cancel
|
|
||||||
while (!subFiles.isEmpty()) {
|
|
||||||
Path subFile = subFiles.getLast();
|
|
||||||
Files.walkFileTree(subFile, DeletingPathVisitor.INSTANCE);
|
|
||||||
subFiles.removeLast();
|
|
||||||
if (this.cancelled) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure everything is deleted
|
|
||||||
if (Files.exists(directory)) {
|
|
||||||
Files.walkFileTree(directory, DeletingPathVisitor.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset texture-gallery
|
|
||||||
if (this.map != null) this.map.resetTextureGallery();
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
// reset map render state
|
|
||||||
if (this.map != null) {
|
|
||||||
this.map.getRenderState().reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasMoreWork() {
|
|
||||||
return this.hasMoreWork;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@DebugDump
|
|
||||||
public double estimateProgress() {
|
|
||||||
return 1d - (subFiles.size() / (double) subFilesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel() {
|
|
||||||
this.cancelled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(RenderTask task) {
|
|
||||||
if (task == this) return true;
|
|
||||||
if (task instanceof MapFilePurgeTask) {
|
|
||||||
return ((MapFilePurgeTask) task).directory.toAbsolutePath().normalize()
|
|
||||||
.startsWith(this.directory.toAbsolutePath().normalize());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "Purge Map " + directory.getFileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@DebugDump
|
|
||||||
private static class MapStoragePurgeTask extends MapPurgeTask {
|
|
||||||
|
|
||||||
private final BmMap map;
|
|
||||||
|
|
||||||
private volatile boolean hasMoreWork;
|
|
||||||
|
|
||||||
public MapStoragePurgeTask(BmMap map) {
|
|
||||||
this.map = Objects.requireNonNull(map);
|
|
||||||
this.hasMoreWork = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doWork() throws Exception {
|
|
||||||
synchronized (this) {
|
|
||||||
if (!this.hasMoreWork) return;
|
|
||||||
this.hasMoreWork = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
map.getStorage().purgeMap(map.getId());
|
|
||||||
} finally {
|
|
||||||
// reset map render state
|
|
||||||
map.getRenderState().reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasMoreWork() {
|
|
||||||
return this.hasMoreWork;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@DebugDump
|
|
||||||
public double estimateProgress() {
|
|
||||||
return 0d;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel() {
|
|
||||||
this.hasMoreWork = false;
|
this.hasMoreWork = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// save lowres-tile-manager to clear/flush any buffered data
|
||||||
public boolean contains(RenderTask task) {
|
this.map.getLowresTileManager().save();
|
||||||
if (task == this) return true;
|
|
||||||
if (task instanceof MapStoragePurgeTask) {
|
|
||||||
return map.equals(((MapStoragePurgeTask) task).map);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
try {
|
||||||
|
// purge the map
|
||||||
|
map.getStorage().purgeMap(map.getId(), progressInfo -> {
|
||||||
|
this.progress = progressInfo.getProgress();
|
||||||
|
return !this.cancelled;
|
||||||
|
});
|
||||||
|
|
||||||
|
// reset texture gallery
|
||||||
|
map.resetTextureGallery();
|
||||||
|
} finally {
|
||||||
|
// reset renderstate
|
||||||
|
map.getRenderState().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return "Purge Map " + map.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public boolean hasMoreWork() {
|
||||||
|
return this.hasMoreWork && !this.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DebugDump
|
||||||
|
public double estimateProgress() {
|
||||||
|
return this.progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
this.cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(RenderTask task) {
|
||||||
|
if (task == this) return true;
|
||||||
|
if (task instanceof MapPurgeTask) {
|
||||||
|
return map.equals(((MapPurgeTask) task).map);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Purge Map " + map.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -30,7 +30,9 @@
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public abstract class Storage implements Closeable {
|
public abstract class Storage implements Closeable {
|
||||||
|
|
||||||
@ -52,7 +54,11 @@ public abstract class Storage implements Closeable {
|
|||||||
|
|
||||||
public abstract void deleteMeta(String mapId, String name) throws IOException;
|
public abstract void deleteMeta(String mapId, String name) throws IOException;
|
||||||
|
|
||||||
public abstract void purgeMap(String mapId) throws IOException;
|
public abstract void purgeMap(String mapId, Function<ProgressInfo, Boolean> onProgress) throws IOException;
|
||||||
|
|
||||||
|
public abstract Collection<String> collectMapIds() throws IOException;
|
||||||
|
|
||||||
|
public abstract long estimateMapSize(String mapId) throws IOException;
|
||||||
|
|
||||||
public MapStorage mapStorage(final String mapId) {
|
public MapStorage mapStorage(final String mapId) {
|
||||||
return new MapStorage(mapId);
|
return new MapStorage(mapId);
|
||||||
@ -118,6 +124,20 @@ public Storage getStorage() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ProgressInfo {
|
||||||
|
|
||||||
|
private final double progress;
|
||||||
|
|
||||||
|
public ProgressInfo(double progress) {
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static String escapeMetaName(String name) {
|
public static String escapeMetaName(String name) {
|
||||||
return name.replaceAll("[^\\w\\d.\\-_/]", "_").replace("..", "_.");
|
return name.replaceAll("[^\\w\\d.\\-_/]", "_").replace("..", "_.");
|
||||||
}
|
}
|
||||||
|
@ -27,16 +27,18 @@
|
|||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import de.bluecolored.bluemap.api.debug.DebugDump;
|
import de.bluecolored.bluemap.api.debug.DebugDump;
|
||||||
import de.bluecolored.bluemap.core.storage.*;
|
import de.bluecolored.bluemap.core.storage.*;
|
||||||
import de.bluecolored.bluemap.core.util.FileHelper;
|
|
||||||
import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
|
import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
|
||||||
|
import de.bluecolored.bluemap.core.util.FileHelper;
|
||||||
|
import de.bluecolored.bluemap.core.util.SizeCollectingPathVisitor;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.function.Function;
|
||||||
import java.util.Optional;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@DebugDump
|
@DebugDump
|
||||||
public class FileStorage extends Storage {
|
public class FileStorage extends Storage {
|
||||||
@ -188,8 +190,48 @@ public void deleteMeta(String mapId, String name) throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void purgeMap(String mapId) throws IOException {
|
public void purgeMap(String mapId, Function<ProgressInfo, Boolean> onProgress) throws IOException {
|
||||||
Files.walkFileTree(getFilePath(mapId), DeletingPathVisitor.INSTANCE);
|
final Path directory = getFilePath(mapId);
|
||||||
|
final int subFilesCount;
|
||||||
|
final LinkedList<Path> subFiles;
|
||||||
|
|
||||||
|
// collect sub-files to be able to provide progress-updates
|
||||||
|
try (Stream<Path> pathStream = Files.walk(directory, 3)) {
|
||||||
|
subFiles = pathStream.collect(Collectors.toCollection(LinkedList::new));
|
||||||
|
}
|
||||||
|
subFilesCount = subFiles.size();
|
||||||
|
|
||||||
|
// delete subFiles first to be able to track the progress and cancel
|
||||||
|
while (!subFiles.isEmpty()) {
|
||||||
|
Path subFile = subFiles.getLast();
|
||||||
|
Files.walkFileTree(subFile, DeletingPathVisitor.INSTANCE);
|
||||||
|
subFiles.removeLast();
|
||||||
|
|
||||||
|
if (!onProgress.apply(
|
||||||
|
new ProgressInfo(1d - (subFiles.size() / (double) subFilesCount))
|
||||||
|
)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure everything is deleted
|
||||||
|
if (Files.exists(directory))
|
||||||
|
Files.walkFileTree(directory, DeletingPathVisitor.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> collectMapIds() throws IOException {
|
||||||
|
try (Stream<Path> fileStream = Files.list(root)) {
|
||||||
|
return fileStream
|
||||||
|
.filter(Files::isDirectory)
|
||||||
|
.map(path -> path.getFileName().toString())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long estimateMapSize(String mapId) throws IOException {
|
||||||
|
SizeCollectingPathVisitor visitor = new SizeCollectingPathVisitor();
|
||||||
|
Files.walkFileTree(getFilePath(mapId), visitor);
|
||||||
|
return visitor.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getFilePath(String mapId, int lod, Vector2i tile){
|
public Path getFilePath(String mapId, int lod, Vector2i tile){
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class SQLStorage extends Storage {
|
public class SQLStorage extends Storage {
|
||||||
|
|
||||||
@ -375,7 +376,7 @@ public void deleteMeta(String mapId, String name) throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void purgeMap(String mapId) throws IOException {
|
public void purgeMap(String mapId, Function<ProgressInfo, Boolean> onProgress) throws IOException {
|
||||||
try {
|
try {
|
||||||
recoveringConnection(connection -> {
|
recoveringConnection(connection -> {
|
||||||
executeUpdate(connection,
|
executeUpdate(connection,
|
||||||
@ -395,12 +396,29 @@ public void purgeMap(String mapId) throws IOException {
|
|||||||
"WHERE m.`map_id` = ?",
|
"WHERE m.`map_id` = ?",
|
||||||
mapId
|
mapId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
executeUpdate(connection,
|
||||||
|
"DELETE t " +
|
||||||
|
"FROM `bluemap_map`" +
|
||||||
|
"WHERE `map_id` = ?",
|
||||||
|
mapId
|
||||||
|
);
|
||||||
}, 2);
|
}, 2);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new IOException(ex);
|
throw new IOException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> collectMapIds() throws IOException {
|
||||||
|
return Collections.emptyList(); //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long estimateMapSize(String mapId) throws IOException {
|
||||||
|
return 0; //TODO
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("UnusedAssignment")
|
@SuppressWarnings("UnusedAssignment")
|
||||||
public void initialize() throws IOException {
|
public void initialize() throws IOException {
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package de.bluecolored.bluemap.core.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
public class SizeCollectingPathVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
|
||||||
|
private final AtomicLong size = new AtomicLong(0);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
size.addAndGet(attrs.size());
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user