Skip to content

Commit 81f309b

Browse files
committed
Create directories with symlinks in mind, fixes #349
1 parent 4534202 commit 81f309b

File tree

9 files changed

+48
-27
lines changed

9 files changed

+48
-27
lines changed

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
import de.bluecolored.bluemap.core.mca.MCAWorld;
4646
import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
4747
import de.bluecolored.bluemap.core.storage.Storage;
48-
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
48+
import de.bluecolored.bluemap.core.util.FileHelper;
4949
import de.bluecolored.bluemap.core.world.World;
5050
import org.apache.commons.io.FileUtils;
5151
import org.spongepowered.configurate.ConfigurateException;
@@ -315,7 +315,7 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
315315
Path resourcePackFolder = serverInterface.getConfigFolder().resolve("resourcepacks");
316316

317317
try {
318-
Files.createDirectories(resourcePackFolder);
318+
FileHelper.createDirectories(resourcePackFolder);
319319
} catch (IOException ex) {
320320
throw new ConfigurationException(
321321
"BlueMap failed to create this folder:\n" +
@@ -330,11 +330,11 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
330330
try {
331331
Logger.global.logInfo("Downloading " + minecraftVersion.getResource().getClientUrl() + " to " + defaultResourceFile + " ...");
332332

333-
Files.createDirectories(defaultResourceFile.getParent());
333+
FileHelper.createDirectories(defaultResourceFile.getParent());
334334
Path tempResourceFile = defaultResourceFile.getParent().resolve(defaultResourceFile.getFileName() + ".filepart");
335335
Files.deleteIfExists(tempResourceFile);
336336
FileUtils.copyURLToFile(new URL(minecraftVersion.getResource().getClientUrl()), tempResourceFile.toFile(), 10000, 10000);
337-
AtomicFileHelper.move(tempResourceFile, defaultResourceFile);
337+
FileHelper.move(tempResourceFile, defaultResourceFile);
338338
} catch (IOException ex) {
339339
throw new ConfigurationException("Failed to download resources!", ex);
340340
}
@@ -346,7 +346,7 @@ public synchronized ResourcePack getResourcePack() throws ConfigurationException
346346

347347
try {
348348
Files.deleteIfExists(resourceExtensionsFile);
349-
Files.createDirectories(resourceExtensionsFile.getParent());
349+
FileHelper.createDirectories(resourceExtensionsFile.getParent());
350350
URL resourceExtensionsUrl = Objects.requireNonNull(
351351
Plugin.class.getResource(
352352
"/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() +

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import de.bluecolored.bluemap.core.BlueMap;
3030
import de.bluecolored.bluemap.core.logger.Logger;
3131
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
32+
import de.bluecolored.bluemap.core.util.FileHelper;
3233
import org.apache.commons.io.FileUtils;
3334

3435
import java.io.BufferedReader;
@@ -64,7 +65,7 @@ public void loadSettings() throws IOException {
6465
}
6566

6667
public void saveSettings() throws IOException {
67-
Files.createDirectories(getSettingsFile().getParent());
68+
FileHelper.createDirectories(getSettingsFile().getParent());
6869
try (BufferedWriter writer = Files.newBufferedWriter(getSettingsFile(),
6970
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
7071
ResourcesGson.addAdapter(new GsonBuilder())

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/WebAppImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import de.bluecolored.bluemap.api.WebApp;
44
import de.bluecolored.bluemap.common.plugin.Plugin;
5+
import de.bluecolored.bluemap.core.util.FileHelper;
56

67
import javax.imageio.ImageIO;
78
import java.awt.image.BufferedImage;
@@ -51,7 +52,7 @@ public String createImage(BufferedImage image, String path) throws IOException {
5152
Path imageRootFolder = webRoot.resolve(IMAGE_ROOT_PATH);
5253
Path imagePath = imageRootFolder.resolve(Path.of(path.replace("/", separator) + ".png")).toAbsolutePath();
5354

54-
Files.createDirectories(imagePath.getParent());
55+
FileHelper.createDirectories(imagePath.getParent());
5556
Files.deleteIfExists(imagePath);
5657
Files.createFile(imagePath);
5758

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import de.bluecolored.bluemap.common.serverinterface.ServerInterface;
77
import de.bluecolored.bluemap.core.BlueMap;
88
import de.bluecolored.bluemap.core.logger.Logger;
9+
import de.bluecolored.bluemap.core.util.FileHelper;
910
import de.bluecolored.bluemap.core.util.Tristate;
1011

1112
import java.io.IOException;
@@ -101,7 +102,7 @@ private synchronized CoreConfig loadCoreConfig(Path defaultDataFolder) throws Co
101102
presetRenderThreadCount = 3;
102103

103104
try {
104-
Files.createDirectories(configFolder);
105+
FileHelper.createDirectories(configFolder);
105106
Files.writeString(
106107
configFolder.resolve("core.conf"),
107108
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/core.conf")
@@ -129,7 +130,7 @@ private synchronized WebserverConfig loadWebserverConfig(Path defaultWebroot) th
129130

130131
if (!Files.exists(configFile)) {
131132
try {
132-
Files.createDirectories(configFolder);
133+
FileHelper.createDirectories(configFolder);
133134
Files.writeString(
134135
configFolder.resolve("webserver.conf"),
135136
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webserver.conf")
@@ -152,7 +153,7 @@ private synchronized WebappConfig loadWebappConfig(Path defaultWebroot) throws C
152153

153154
if (!Files.exists(configFile)) {
154155
try {
155-
Files.createDirectories(configFolder);
156+
FileHelper.createDirectories(configFolder);
156157
Files.writeString(
157158
configFolder.resolve("webapp.conf"),
158159
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webapp.conf")
@@ -175,7 +176,7 @@ private synchronized PluginConfig loadPluginConfig() throws ConfigurationExcepti
175176

176177
if (!Files.exists(configFile)) {
177178
try {
178-
Files.createDirectories(configFolder);
179+
FileHelper.createDirectories(configFolder);
179180
Files.writeString(
180181
configFolder.resolve("plugin.conf"),
181182
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/plugin.conf")
@@ -198,7 +199,7 @@ private synchronized Map<String, MapConfig> loadMapConfigs() throws Configuratio
198199

199200
if (!Files.exists(mapConfigFolder)){
200201
try {
201-
Files.createDirectories(mapConfigFolder);
202+
FileHelper.createDirectories(mapConfigFolder);
202203
var worlds = serverInterface.getLoadedWorlds();
203204
if (worlds.isEmpty()) {
204205
Files.writeString(
@@ -281,7 +282,7 @@ private synchronized Map<String, StorageConfig> loadStorageConfigs(Path defaultW
281282

282283
if (!Files.exists(storageConfigFolder)){
283284
try {
284-
Files.createDirectories(storageConfigFolder);
285+
FileHelper.createDirectories(storageConfigFolder);
285286
Files.writeString(
286287
storageConfigFolder.resolve("file.conf"),
287288
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/storages/file.conf")

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import de.bluecolored.bluemap.core.metrics.Metrics;
4848
import de.bluecolored.bluemap.core.storage.MetaType;
4949
import de.bluecolored.bluemap.core.storage.Storage;
50+
import de.bluecolored.bluemap.core.util.FileHelper;
5051
import de.bluecolored.bluemap.core.world.World;
5152
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
5253
import org.spongepowered.configurate.serialize.SerializationException;
@@ -56,7 +57,6 @@
5657
import java.io.OutputStreamWriter;
5758
import java.io.Writer;
5859
import java.net.UnknownHostException;
59-
import java.nio.file.Files;
6060
import java.nio.file.Path;
6161
import java.util.*;
6262
import java.util.concurrent.TimeUnit;
@@ -160,7 +160,7 @@ public void load() throws IOException {
160160
//create and start webserver
161161
if (webserverConfig.isEnabled()) {
162162
Path webroot = webserverConfig.getWebroot();
163-
Files.createDirectories(webroot);
163+
FileHelper.createDirectories(webroot);
164164

165165
RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler();
166166

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/RegionFileWatchService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import de.bluecolored.bluemap.api.debug.DebugDump;
3131
import de.bluecolored.bluemap.core.logger.Logger;
3232
import de.bluecolored.bluemap.core.map.BmMap;
33+
import de.bluecolored.bluemap.core.util.FileHelper;
3334

3435
import java.io.IOException;
3536
import java.nio.file.*;
@@ -60,7 +61,7 @@ public RegionFileWatchService(RenderManager renderManager, BmMap map, boolean ve
6061
this.scheduledUpdates = new HashMap<>();
6162

6263
Path folder = map.getWorld().getSaveFolder().resolve("region");
63-
Files.createDirectories(folder);
64+
FileHelper.createDirectories(folder);
6465

6566
this.watchService = folder.getFileSystem().newWatchService();
6667

BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/file/FileStorage.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import com.flowpowered.math.vector.Vector2i;
2828
import de.bluecolored.bluemap.api.debug.DebugDump;
2929
import de.bluecolored.bluemap.core.storage.*;
30-
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
30+
import de.bluecolored.bluemap.core.util.FileHelper;
3131
import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
3232

3333
import java.io.*;
@@ -70,7 +70,7 @@ public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IO
7070
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
7171
Path file = getFilePath(mapId, lod, tile);
7272

73-
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
73+
OutputStream os = FileHelper.createFilepartOutputStream(file);
7474
os = new BufferedOutputStream(os);
7575

7676
try {
@@ -140,7 +140,7 @@ public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOExcepti
140140
public OutputStream writeMeta(String mapId, MetaType metaType) throws IOException {
141141
Path file = getFilePath(mapId).resolve(metaType.getFilePath());
142142

143-
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
143+
OutputStream os = FileHelper.createFilepartOutputStream(file);
144144
os = new BufferedOutputStream(os);
145145

146146
return os;

BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/AtomicFileHelper.java renamed to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,32 @@
2828
import java.io.IOException;
2929
import java.io.OutputStream;
3030
import java.nio.file.*;
31+
import java.nio.file.attribute.FileAttribute;
3132

32-
public class AtomicFileHelper {
33+
public class FileHelper {
3334

35+
/**
36+
* Creates an OutputStream that writes to a ".filepart"-file first and then atomically moves (overwrites) to the final target atomically
37+
* once the stream gets closed.
38+
*/
3439
public static OutputStream createFilepartOutputStream(final Path file) throws IOException {
3540
final Path partFile = getPartFile(file);
36-
Files.createDirectories(partFile.getParent());
41+
FileHelper.createDirectories(partFile.getParent());
3742

3843
OutputStream os = Files.newOutputStream(partFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
3944
return new WrappedOutputStream(os, () -> {
4045
if (!Files.exists(partFile)) return;
4146

4247
Files.deleteIfExists(file);
43-
Files.createDirectories(file.getParent());
48+
FileHelper.createDirectories(file.getParent());
4449

45-
AtomicFileHelper.move(partFile, file);
50+
FileHelper.move(partFile, file);
4651
});
4752
}
4853

54+
/**
55+
* Tries to move the file atomically, but fallbacks to a normal move operation if moving atomically fails
56+
*/
4957
public static void move(Path from, Path to) throws IOException {
5058
try {
5159
Files.move(from, to, StandardCopyOption.ATOMIC_MOVE);
@@ -57,6 +65,15 @@ public static void move(Path from, Path to) throws IOException {
5765
}
5866
}
5967

68+
/**
69+
* Same as {@link Files#createDirectories(Path, FileAttribute[])} but accepts symlinked folders.
70+
* @see Files#createDirectories(Path, FileAttribute[])
71+
*/
72+
public static Path createDirectories(Path dir, FileAttribute<?>... attrs) throws IOException {
73+
if (Files.isDirectory(dir)) return dir;
74+
return Files.createDirectories(dir, attrs);
75+
}
76+
6077
private static Path getPartFile(Path file) {
6178
return file.normalize().getParent().resolve(file.getFileName() + ".filepart");
6279
}

implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@
4848
import de.bluecolored.bluemap.core.map.BmMap;
4949
import de.bluecolored.bluemap.core.metrics.Metrics;
5050
import de.bluecolored.bluemap.core.storage.Storage;
51+
import de.bluecolored.bluemap.core.util.FileHelper;
5152
import org.apache.commons.cli.*;
5253
import org.apache.commons.lang3.time.DurationFormatUtils;
5354

5455
import java.io.File;
5556
import java.io.IOException;
56-
import java.nio.file.Files;
5757
import java.nio.file.Path;
5858
import java.util.*;
5959
import java.util.concurrent.TimeUnit;
@@ -181,7 +181,7 @@ public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOExc
181181
Logger.global.logInfo("Starting webserver ...");
182182

183183
WebserverConfig config = blueMap.getConfigs().getWebserverConfig();
184-
Files.createDirectories(config.getWebroot());
184+
FileHelper.createDirectories(config.getWebroot());
185185

186186
RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler();
187187

@@ -274,7 +274,7 @@ public static void main(String[] args) {
274274
cli.configFolder = Path.of("config");
275275
if (cmd.hasOption("c")) {
276276
cli.configFolder = Path.of(cmd.getOptionValue("c"));
277-
Files.createDirectories(cli.configFolder);
277+
FileHelper.createDirectories(cli.configFolder);
278278
}
279279

280280
//minecraft version
@@ -323,7 +323,7 @@ public static void main(String[] args) {
323323
Logger.global.logInfo("Generated default config files for you, here: " + cli.configFolder.toAbsolutePath().normalize() + "\n");
324324

325325
//create resourcepacks folder
326-
Files.createDirectories(cli.configFolder.resolve( "resourcepacks"));
326+
FileHelper.createDirectories(cli.configFolder.resolve( "resourcepacks"));
327327

328328
//print help
329329
BlueMapCLI.printHelp();

0 commit comments

Comments
 (0)