Remove all usages of java.io.File and bad usages of Path.of()

This commit is contained in:
Lukas Rieger (Blue) 2024-05-21 16:32:28 +02:00
parent ce25eb52e3
commit d7dd8931a5
No known key found for this signature in database
GPG Key ID: AA33883B1BBA03E6
12 changed files with 346 additions and 232 deletions

View File

@ -200,7 +200,7 @@ private synchronized void loadMap(String id, MapConfig mapConfig) throws Configu
dimension = DataPack.DIMENSION_THE_END; dimension = DataPack.DIMENSION_THE_END;
} else if ( } else if (
worldFolder.getNameCount() > 3 && worldFolder.getNameCount() > 3 &&
worldFolder.getName(worldFolder.getNameCount() - 3).equals(Path.of("dimensions")) worldFolder.getName(worldFolder.getNameCount() - 3).toString().equals("dimensions")
) { ) {
String namespace = worldFolder.getName(worldFolder.getNameCount() - 2).toString(); String namespace = worldFolder.getName(worldFolder.getNameCount() - 2).toString();
String value = worldFolder.getName(worldFolder.getNameCount() - 1).toString(); String value = worldFolder.getName(worldFolder.getNameCount() - 1).toString();

View File

@ -32,21 +32,15 @@
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson; import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
import de.bluecolored.bluemap.core.util.FileHelper; import de.bluecolored.bluemap.core.util.FileHelper;
import org.apache.commons.io.FileUtils;
import java.io.BufferedReader; import java.io.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class WebFilesManager { public class WebFilesManager {
@ -114,39 +108,17 @@ public boolean filesNeedUpdate() {
} }
public void updateFiles() throws IOException { public void updateFiles() throws IOException {
URL fileResource = getClass().getResource("/de/bluecolored/bluemap/webapp.zip"); URL zippedWebapp = getClass().getResource("/de/bluecolored/bluemap/webapp.zip");
File tempFile = File.createTempFile("bluemap_webroot_extraction", null); if (zippedWebapp == null) throw new IOException("Failed to open bundled webapp.");
if (fileResource == null) throw new IOException("Failed to open bundled webapp."); // extract zip to webroot
FileHelper.extractZipFile(zippedWebapp, webRoot, StandardCopyOption.REPLACE_EXISTING);
try { // set version in index.html
FileUtils.copyURLToFile(fileResource, tempFile, 10000, 10000); Path indexFile = webRoot.resolve("index.html");
try (ZipFile zipFile = new ZipFile(tempFile)){ String indexContent = Files.readString(indexFile);
Enumeration<? extends ZipEntry> entries = zipFile.entries(); indexContent = indexContent.replace("%version%", BlueMap.VERSION);
while(entries.hasMoreElements()) { Files.writeString(indexFile, indexContent);
ZipEntry zipEntry = entries.nextElement();
if (zipEntry.isDirectory()) {
File dir = webRoot.resolve(zipEntry.getName()).toFile();
FileUtils.forceMkdir(dir);
} else {
File target = webRoot.resolve(zipEntry.getName()).toFile();
FileUtils.forceMkdirParent(target);
FileUtils.copyInputStreamToFile(zipFile.getInputStream(zipEntry), target);
}
}
}
// set version in index.html
Path indexFile = webRoot.resolve("index.html");
String indexContent = Files.readString(indexFile);
indexContent = indexContent.replace("%version%", BlueMap.VERSION);
Files.writeString(indexFile, indexContent);
} finally {
if (!tempFile.delete()) {
Logger.global.logWarning("Failed to delete file: " + tempFile);
}
}
} }
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal", "unused", "MismatchedQueryAndUpdateOfCollection"}) @SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal", "unused", "MismatchedQueryAndUpdateOfCollection"})

View File

@ -40,7 +40,6 @@
import java.util.stream.Stream; import java.util.stream.Stream;
public class WebAppImpl implements WebApp { public class WebAppImpl implements WebApp {
private static final Path IMAGE_ROOT_PATH = Path.of("data", "images");
private final Plugin plugin; private final Plugin plugin;
@ -88,8 +87,8 @@ public String createImage(BufferedImage image, String path) throws IOException {
Path webRoot = getWebRoot().toAbsolutePath(); Path webRoot = getWebRoot().toAbsolutePath();
String separator = webRoot.getFileSystem().getSeparator(); String separator = webRoot.getFileSystem().getSeparator();
Path imageRootFolder = webRoot.resolve(IMAGE_ROOT_PATH); Path imageRootFolder = webRoot.resolve("data").resolve("images");
Path imagePath = imageRootFolder.resolve(Path.of(path.replace("/", separator) + ".png")).toAbsolutePath(); Path imagePath = imageRootFolder.resolve(path.replace("/", separator) + ".png").toAbsolutePath();
FileHelper.createDirectories(imagePath.getParent()); FileHelper.createDirectories(imagePath.getParent());
Files.deleteIfExists(imagePath); Files.deleteIfExists(imagePath);
@ -108,7 +107,7 @@ public Map<String, String> availableImages() throws IOException {
Path webRoot = getWebRoot().toAbsolutePath(); Path webRoot = getWebRoot().toAbsolutePath();
String separator = webRoot.getFileSystem().getSeparator(); String separator = webRoot.getFileSystem().getSeparator();
Path imageRootPath = webRoot.resolve("data").resolve(IMAGE_ROOT_PATH).toAbsolutePath(); Path imageRootPath = webRoot.resolve("data").resolve("images").toAbsolutePath();
Map<String, String> availableImagesMap = new HashMap<>(); Map<String, String> availableImagesMap = new HashMap<>();

View File

@ -46,6 +46,13 @@
@Getter @Getter
public class BlueMapConfigManager implements BlueMapConfiguration { public class BlueMapConfigManager implements BlueMapConfiguration {
public static final String CORE_CONFIG_NAME = "core";
public static final String WEBSERVER_CONFIG_NAME = "webserver";
public static final String WEBAPP_CONFIG_NAME = "webapp";
public static final String PLUGIN_CONFIG_NAME = "plugin";
public static final String MAPS_CONFIG_FOLDER_NAME = "maps";
public static final String STORAGES_CONFIG_FOLDER_NAME = "storages";
private final ConfigManager configManager; private final ConfigManager configManager;
private final CoreConfig coreConfig; private final CoreConfig coreConfig;
@ -92,16 +99,15 @@ private BlueMapConfigManager(
} }
private CoreConfig loadCoreConfig(Path defaultDataFolder, boolean useMetricsConfig) throws ConfigurationException { private CoreConfig loadCoreConfig(Path defaultDataFolder, boolean useMetricsConfig) throws ConfigurationException {
Path configFileRaw = Path.of("core"); Path configFile = configManager.resolveConfigFile(CORE_CONFIG_NAME);
Path configFile = configManager.findConfigPath(configFileRaw);
Path configFolder = configFile.getParent(); Path configFolder = configFile.getParent();
if (!Files.exists(configFile)) { if (!Files.exists(configFile)) {
try { try {
FileHelper.createDirectories(configFolder); FileHelper.createDirectories(configFolder);
Files.writeString( Files.writeString(
configFolder.resolve("core.conf"), configFile,
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/core.conf") configManager.loadConfigTemplate(CORE_CONFIG_NAME)
.setConditional("metrics", useMetricsConfig) .setConditional("metrics", useMetricsConfig)
.setVariable("timestamp", LocalDateTime.now().withNano(0).toString()) .setVariable("timestamp", LocalDateTime.now().withNano(0).toString())
.setVariable("version", BlueMap.VERSION) .setVariable("version", BlueMap.VERSION)
@ -118,7 +124,7 @@ private CoreConfig loadCoreConfig(Path defaultDataFolder, boolean useMetricsConf
} }
} }
return configManager.loadConfig(configFileRaw, CoreConfig.class); return configManager.loadConfig(CORE_CONFIG_NAME, CoreConfig.class);
} }
/** /**
@ -137,16 +143,15 @@ private int suggestRenderThreadCount() {
} }
private WebserverConfig loadWebserverConfig(Path defaultWebroot, Path dataRoot) throws ConfigurationException { private WebserverConfig loadWebserverConfig(Path defaultWebroot, Path dataRoot) throws ConfigurationException {
Path configFileRaw = Path.of("webserver"); Path configFile = configManager.resolveConfigFile(WEBSERVER_CONFIG_NAME);
Path configFile = configManager.findConfigPath(configFileRaw);
Path configFolder = configFile.getParent(); Path configFolder = configFile.getParent();
if (!Files.exists(configFile)) { if (!Files.exists(configFile)) {
try { try {
FileHelper.createDirectories(configFolder); FileHelper.createDirectories(configFolder);
Files.writeString( Files.writeString(
configFolder.resolve("webserver.conf"), configFile,
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webserver.conf") configManager.loadConfigTemplate(WEBSERVER_CONFIG_NAME)
.setVariable("webroot", formatPath(defaultWebroot)) .setVariable("webroot", formatPath(defaultWebroot))
.setVariable("logfile", formatPath(dataRoot.resolve("logs").resolve("webserver.log"))) .setVariable("logfile", formatPath(dataRoot.resolve("logs").resolve("webserver.log")))
.setVariable("logfile-with-time", formatPath(dataRoot.resolve("logs").resolve("webserver_%1$tF_%1$tT.log"))) .setVariable("logfile-with-time", formatPath(dataRoot.resolve("logs").resolve("webserver_%1$tF_%1$tT.log")))
@ -158,20 +163,19 @@ private WebserverConfig loadWebserverConfig(Path defaultWebroot, Path dataRoot)
} }
} }
return configManager.loadConfig(configFileRaw, WebserverConfig.class); return configManager.loadConfig(WEBSERVER_CONFIG_NAME, WebserverConfig.class);
} }
private WebappConfig loadWebappConfig(Path defaultWebroot) throws ConfigurationException { private WebappConfig loadWebappConfig(Path defaultWebroot) throws ConfigurationException {
Path configFileRaw = Path.of("webapp"); Path configFile = configManager.resolveConfigFile(WEBAPP_CONFIG_NAME);
Path configFile = configManager.findConfigPath(configFileRaw);
Path configFolder = configFile.getParent(); Path configFolder = configFile.getParent();
if (!Files.exists(configFile)) { if (!Files.exists(configFile)) {
try { try {
FileHelper.createDirectories(configFolder); FileHelper.createDirectories(configFolder);
Files.writeString( Files.writeString(
configFolder.resolve("webapp.conf"), configFile,
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webapp.conf") configManager.loadConfigTemplate(WEBAPP_CONFIG_NAME)
.setVariable("webroot", formatPath(defaultWebroot)) .setVariable("webroot", formatPath(defaultWebroot))
.build(), .build(),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
@ -181,20 +185,19 @@ private WebappConfig loadWebappConfig(Path defaultWebroot) throws ConfigurationE
} }
} }
return configManager.loadConfig(configFileRaw, WebappConfig.class); return configManager.loadConfig(WEBAPP_CONFIG_NAME, WebappConfig.class);
} }
private PluginConfig loadPluginConfig() throws ConfigurationException { private PluginConfig loadPluginConfig() throws ConfigurationException {
Path configFileRaw = Path.of("plugin"); Path configFile = configManager.resolveConfigFile(PLUGIN_CONFIG_NAME);
Path configFile = configManager.findConfigPath(configFileRaw);
Path configFolder = configFile.getParent(); Path configFolder = configFile.getParent();
if (!Files.exists(configFile)) { if (!Files.exists(configFile)) {
try { try {
FileHelper.createDirectories(configFolder); FileHelper.createDirectories(configFolder);
Files.writeString( Files.writeString(
configFolder.resolve("plugin.conf"), configFile,
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/plugin.conf") configManager.loadConfigTemplate(PLUGIN_CONFIG_NAME)
.build(), .build(),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
); );
@ -203,14 +206,13 @@ private PluginConfig loadPluginConfig() throws ConfigurationException {
} }
} }
return configManager.loadConfig(configFileRaw, PluginConfig.class); return configManager.loadConfig(PLUGIN_CONFIG_NAME, PluginConfig.class);
} }
private Map<String, MapConfig> loadMapConfigs(Collection<ServerWorld> autoConfigWorlds) throws ConfigurationException { private Map<String, MapConfig> loadMapConfigs(Collection<ServerWorld> autoConfigWorlds) throws ConfigurationException {
Map<String, MapConfig> mapConfigs = new HashMap<>(); Map<String, MapConfig> mapConfigs = new HashMap<>();
Path mapFolder = Paths.get("maps"); Path mapConfigFolder = configManager.getConfigRoot().resolve(MAPS_CONFIG_FOLDER_NAME);
Path mapConfigFolder = configManager.getConfigRoot().resolve(mapFolder);
if (!Files.exists(mapConfigFolder)){ if (!Files.exists(mapConfigFolder)){
try { try {
@ -290,8 +292,7 @@ private Map<String, MapConfig> loadMapConfigs(Collection<ServerWorld> autoConfig
try (Stream<Path> configFiles = Files.list(mapConfigFolder)) { try (Stream<Path> configFiles = Files.list(mapConfigFolder)) {
for (var configFile : configFiles.toArray(Path[]::new)) { for (var configFile : configFiles.toArray(Path[]::new)) {
if (!configManager.isConfigFile(configFile)) continue; if (!configManager.isConfigFile(configFile)) continue;
Path rawConfig = configManager.getRaw(configFile); String id = sanitiseMapId(configManager.getConfigName(configFile));
String id = sanitiseMapId(rawConfig.getFileName().toString());
if (mapConfigs.containsKey(id)) { if (mapConfigs.containsKey(id)) {
throw new ConfigurationException("At least two of your map-config file-names result in ambiguous map-id's!\n" + throw new ConfigurationException("At least two of your map-config file-names result in ambiguous map-id's!\n" +
@ -299,7 +300,7 @@ private Map<String, MapConfig> loadMapConfigs(Collection<ServerWorld> autoConfig
"To resolve this issue, rename this file to something else."); "To resolve this issue, rename this file to something else.");
} }
MapConfig mapConfig = configManager.loadConfig(rawConfig, MapConfig.class); MapConfig mapConfig = configManager.loadConfig(configFile, MapConfig.class);
mapConfigs.put(id, mapConfig); mapConfigs.put(id, mapConfig);
} }
} catch (IOException ex) { } catch (IOException ex) {
@ -315,8 +316,7 @@ private Map<String, MapConfig> loadMapConfigs(Collection<ServerWorld> autoConfig
private Map<String, StorageConfig> loadStorageConfigs(Path defaultWebroot) throws ConfigurationException { private Map<String, StorageConfig> loadStorageConfigs(Path defaultWebroot) throws ConfigurationException {
Map<String, StorageConfig> storageConfigs = new HashMap<>(); Map<String, StorageConfig> storageConfigs = new HashMap<>();
Path storageFolder = Paths.get("storages"); Path storageConfigFolder = configManager.getConfigRoot().resolve(STORAGES_CONFIG_FOLDER_NAME);
Path storageConfigFolder = configManager.getConfigRoot().resolve(storageFolder);
if (!Files.exists(storageConfigFolder)){ if (!Files.exists(storageConfigFolder)){
try { try {
@ -345,11 +345,10 @@ private Map<String, StorageConfig> loadStorageConfigs(Path defaultWebroot) throw
try (Stream<Path> configFiles = Files.list(storageConfigFolder)) { try (Stream<Path> configFiles = Files.list(storageConfigFolder)) {
for (var configFile : configFiles.toArray(Path[]::new)) { for (var configFile : configFiles.toArray(Path[]::new)) {
if (!configManager.isConfigFile(configFile)) continue; if (!configManager.isConfigFile(configFile)) continue;
Path rawConfig = configManager.getRaw(configFile); String id = configManager.getConfigName(configFile);
String id = rawConfig.getFileName().toString();
StorageConfig storageConfig = configManager.loadConfig(rawConfig, StorageConfig.Base.class); // load superclass StorageConfig storageConfig = configManager.loadConfig(configFile, StorageConfig.Base.class); // load superclass
storageConfig = configManager.loadConfig(rawConfig, storageConfig.getStorageType().getConfigType()); // load actual config type storageConfig = configManager.loadConfig(configFile, storageConfig.getStorageType().getConfigType()); // load actual config type
storageConfigs.put(id, storageConfig); storageConfigs.put(id, storageConfig);
} }

View File

@ -0,0 +1,45 @@
package de.bluecolored.bluemap.common.config;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.util.Keyed;
import de.bluecolored.bluemap.core.util.Registry;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
import org.spongepowered.configurate.loader.AbstractConfigurationLoader;
import java.util.function.Supplier;
public interface ConfigLoader extends Keyed {
ConfigLoader HOCON = new Impl(Key.bluemap("hocon"), ".conf", HoconConfigurationLoader::builder);
ConfigLoader JSON = new Impl(Key.bluemap("json"), ".json", GsonConfigurationLoader::builder);
ConfigLoader DEFAULT = HOCON;
Registry<ConfigLoader> REGISTRY = new Registry<>(
HOCON,
JSON
);
String getFileSuffix();
AbstractConfigurationLoader.Builder<?, ?> createLoaderBuilder();
@RequiredArgsConstructor
@Getter
class Impl implements ConfigLoader {
private final Key key;
private final String fileSuffix;
private final Supplier<AbstractConfigurationLoader.Builder<?, ?>> builderSupplier;
@Override
public AbstractConfigurationLoader.Builder<?, ?> createLoaderBuilder() {
return builderSupplier.get();
}
}
}

View File

@ -32,8 +32,6 @@
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.spongepowered.configurate.ConfigurateException; import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
import org.spongepowered.configurate.loader.AbstractConfigurationLoader; import org.spongepowered.configurate.loader.AbstractConfigurationLoader;
import org.spongepowered.configurate.loader.ConfigurationLoader; import org.spongepowered.configurate.loader.ConfigurationLoader;
import org.spongepowered.configurate.serialize.SerializationException; import org.spongepowered.configurate.serialize.SerializationException;
@ -43,14 +41,11 @@
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.Objects;
public class ConfigManager { public class ConfigManager {
private static final String[] CONFIG_FILE_ENDINGS = new String[] { private static final String CONFIG_TEMPLATE_RESOURCE_PATH = "/de/bluecolored/bluemap/config/";
".conf",
".json"
};
private final Path configRoot; private final Path configRoot;
@ -58,32 +53,63 @@ public ConfigManager(Path configRoot) {
this.configRoot = configRoot; this.configRoot = configRoot;
} }
public <T> T loadConfig(Path rawPath, Class<T> type) throws ConfigurationException { public <T> T loadConfig(String name, Class<T> type) throws ConfigurationException {
Path path = findConfigPath(rawPath); Path file = resolveConfigFile(name);
ConfigurationNode configNode = loadConfigFile(path); return loadConfig(file, type);
}
public <T> T loadConfig(Path file, Class<T> type) throws ConfigurationException {
ConfigurationNode configNode = loadConfigFile(file);
try { try {
return Objects.requireNonNull(configNode.get(type)); return Objects.requireNonNull(configNode.get(type));
} catch (SerializationException | NullPointerException ex) { } catch (SerializationException | NullPointerException ex) {
throw new ConfigurationException( throw new ConfigurationException(
"BlueMap failed to parse this file:\n" + "BlueMap failed to parse this file:\n" +
path + "\n" + file + "\n" +
"Check if the file is correctly formatted and all values are correct!", "Check if the file is correctly formatted and all values are correct!",
ex); ex);
} }
} }
public ConfigurationNode loadConfig(Path rawPath) throws ConfigurationException { public ConfigTemplate loadConfigTemplate(String name) throws IOException {
Path path = findConfigPath(rawPath); String resource = CONFIG_TEMPLATE_RESOURCE_PATH + name + ConfigLoader.DEFAULT.getFileSuffix();
return loadConfigFile(path);
}
public ConfigTemplate loadConfigTemplate(String resource) throws IOException {
InputStream in = BlueMap.class.getResourceAsStream(resource); InputStream in = BlueMap.class.getResourceAsStream(resource);
if (in == null) throw new IOException("Resource not found: " + resource); if (in == null) throw new IOException("Resource not found: " + resource);
String configTemplate = IOUtils.toString(in, StandardCharsets.UTF_8); String configTemplate = IOUtils.toString(in, StandardCharsets.UTF_8);
return new ConfigTemplate(configTemplate); return new ConfigTemplate(configTemplate);
} }
public Path resolveConfigFile(String name) {
for (ConfigLoader configLoader : ConfigLoader.REGISTRY.values()) {
Path path = configRoot.resolve(name + configLoader.getFileSuffix());
if (Files.isRegularFile(path)) return path;
}
return configRoot.resolve(name + ConfigLoader.DEFAULT.getFileSuffix());
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isConfigFile(Path file) {
String fileName = file.getFileName().toString();
for (ConfigLoader configLoader : ConfigLoader.REGISTRY.values())
if (fileName.endsWith(configLoader.getFileSuffix())) return true;
return false;
}
public String getConfigName(Path file) {
String fileName = file.getFileName().toString();
for (ConfigLoader configLoader : ConfigLoader.REGISTRY.values()) {
String suffix = configLoader.getFileSuffix();
if (fileName.endsWith(suffix))
return fileName.substring(0, fileName.length() - suffix.length());
}
return fileName;
}
public Path getConfigRoot() {
return configRoot;
}
private ConfigurationNode loadConfigFile(Path path) throws ConfigurationException { private ConfigurationNode loadConfigFile(Path path) throws ConfigurationException {
if (!Files.exists(path)) { if (!Files.exists(path)) {
throw new ConfigurationException( throw new ConfigurationException(
@ -110,58 +136,17 @@ private ConfigurationNode loadConfigFile(Path path) throws ConfigurationExceptio
} }
} }
public Path getConfigRoot() { private ConfigurationLoader<? extends ConfigurationNode> getLoader(Path path){
return configRoot; AbstractConfigurationLoader.Builder<?, ?> builder = null;
} for (ConfigLoader loader : ConfigLoader.REGISTRY.values()) {
if (path.getFileName().endsWith(loader.getFileSuffix())) {
public Path findConfigPath(Path rawPath) { builder = loader.createLoaderBuilder();
if (!rawPath.startsWith(configRoot))
rawPath = configRoot.resolve(rawPath);
for (String fileEnding : CONFIG_FILE_ENDINGS) {
if (rawPath.getFileName().endsWith(fileEnding)) return rawPath;
}
for (String fileEnding : CONFIG_FILE_ENDINGS) {
Path path = rawPath.getParent().resolve(rawPath.getFileName() + fileEnding);
if (Files.exists(path)) return path;
}
return rawPath.getParent().resolve(rawPath.getFileName() + CONFIG_FILE_ENDINGS[0]);
}
public boolean isConfigFile(Path path) {
if (!Files.isRegularFile(path)) return false;
String fileName = path.getFileName().toString();
for (String fileEnding : CONFIG_FILE_ENDINGS) {
if (fileName.endsWith(fileEnding)) return true;
}
return false;
}
public Path getRaw(Path path) {
String fileName = path.getFileName().toString();
String rawName = null;
for (String fileEnding : CONFIG_FILE_ENDINGS) {
if (fileName.endsWith(fileEnding)) {
rawName = fileName.substring(0, fileName.length() - fileEnding.length());
break; break;
} }
} }
if (rawName == null) return path; if (builder == null)
return path.getParent().resolve(rawName); builder = ConfigLoader.DEFAULT.createLoaderBuilder();
}
private ConfigurationLoader<? extends ConfigurationNode> getLoader(Path path){
AbstractConfigurationLoader.Builder<?, ?> builder;
if (path.getFileName().endsWith(".json"))
builder = GsonConfigurationLoader.builder();
else
builder = HoconConfigurationLoader.builder();
return builder return builder
.path(path) .path(path)

View File

@ -167,7 +167,7 @@ private void load(@Nullable ResourcePack preloadedResourcePack) throws IOExcepti
BlueMapConfiguration configProvider = blueMap.getConfig(); BlueMapConfiguration configProvider = blueMap.getConfig();
if (configProvider instanceof BlueMapConfigManager) { if (configProvider instanceof BlueMapConfigManager) {
Logger.global.logWarning("Please check: " + ((BlueMapConfigManager) configProvider).getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize()); Logger.global.logWarning("Please check: " + ((BlueMapConfigManager) configProvider).getConfigManager().resolveConfigFile(BlueMapConfigManager.CORE_CONFIG_NAME).toAbsolutePath().normalize());
} }
Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload"); Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload");

View File

@ -25,14 +25,15 @@
package de.bluecolored.bluemap.common.web; package de.bluecolored.bluemap.common.web;
import de.bluecolored.bluemap.common.web.http.*; import de.bluecolored.bluemap.common.web.http.*;
import de.bluecolored.bluemap.core.logger.Logger;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.DateFormatUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Calendar; import java.util.Calendar;
@ -54,10 +55,16 @@ public FileRequestHandler(Path webRoot) {
public HttpResponse handle(HttpRequest request) { public HttpResponse handle(HttpRequest request) {
if (!request.getMethod().equalsIgnoreCase("GET")) if (!request.getMethod().equalsIgnoreCase("GET"))
return new HttpResponse(HttpStatusCode.BAD_REQUEST); return new HttpResponse(HttpStatusCode.BAD_REQUEST);
return generateResponse(request);
try {
return generateResponse(request);
} catch (IOException e) {
Logger.global.logError("Failed to serve file", e);
return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR);
}
} }
private HttpResponse generateResponse(HttpRequest request) { private HttpResponse generateResponse(HttpRequest request) throws IOException {
String path = request.getPath(); String path = request.getPath();
// normalize path // normalize path
@ -76,36 +83,34 @@ private HttpResponse generateResponse(HttpRequest request) {
return new HttpResponse(HttpStatusCode.FORBIDDEN); return new HttpResponse(HttpStatusCode.FORBIDDEN);
} }
File file = filePath.toFile();
// redirect to have correct relative paths // redirect to have correct relative paths
if (file.isDirectory() && !request.getPath().endsWith("/")) { if (Files.isDirectory(filePath) && !request.getPath().endsWith("/")) {
HttpResponse response = new HttpResponse(HttpStatusCode.SEE_OTHER); HttpResponse response = new HttpResponse(HttpStatusCode.SEE_OTHER);
response.addHeader("Location", "/" + path + "/" + (request.getGETParamString().isEmpty() ? "" : "?" + request.getGETParamString())); response.addHeader("Location", "/" + path + "/" + (request.getGETParamString().isEmpty() ? "" : "?" + request.getGETParamString()));
return response; return response;
} }
// default to index.html // default to index.html
if (!file.exists() || file.isDirectory()){ if (!Files.exists(filePath) || Files.isDirectory(filePath)){
file = new File(filePath + "/index.html"); filePath = filePath.resolve("/index.html");
} }
if (!file.exists() || file.isDirectory()) { if (!Files.exists(filePath) || Files.isDirectory(filePath)){
return new HttpResponse(HttpStatusCode.NOT_FOUND); return new HttpResponse(HttpStatusCode.NOT_FOUND);
} }
// don't send php files // don't send php files
if (file.getName().endsWith(".php")) { if (filePath.getFileName().toString().endsWith(".php")) {
return new HttpResponse(HttpStatusCode.FORBIDDEN); return new HttpResponse(HttpStatusCode.FORBIDDEN);
} }
// check if file is still in web-root and is not a directory // check if file is still in web-root and is not a directory
if (!file.toPath().normalize().startsWith(webRoot) || file.isDirectory()){ if (!filePath.normalize().startsWith(webRoot) || Files.isDirectory(filePath)){
return new HttpResponse(HttpStatusCode.FORBIDDEN); return new HttpResponse(HttpStatusCode.FORBIDDEN);
} }
// check modified // check modified
long lastModified = file.lastModified(); long lastModified = Files.getLastModifiedTime(filePath).to(TimeUnit.MILLISECONDS);
HttpHeader modHeader = request.getHeader("If-Modified-Since"); HttpHeader modHeader = request.getHeader("If-Modified-Since");
if (modHeader != null){ if (modHeader != null){
try { try {
@ -117,7 +122,10 @@ private HttpResponse generateResponse(HttpRequest request) {
} }
//check ETag //check ETag
String eTag = Long.toHexString(file.length()) + Integer.toHexString(file.hashCode()) + Long.toHexString(lastModified); String eTag =
Long.toHexString(Files.size(filePath)) +
Integer.toHexString(filePath.hashCode()) +
Long.toHexString(lastModified);
HttpHeader etagHeader = request.getHeader("If-None-Match"); HttpHeader etagHeader = request.getHeader("If-None-Match");
if (etagHeader != null){ if (etagHeader != null){
if(etagHeader.getValue().equals(eTag)) { if(etagHeader.getValue().equals(eTag)) {
@ -133,7 +141,7 @@ private HttpResponse generateResponse(HttpRequest request) {
response.addHeader("Cache-Control", "max-age=" + TimeUnit.DAYS.toSeconds(1)); response.addHeader("Cache-Control", "max-age=" + TimeUnit.DAYS.toSeconds(1));
//add content type header //add content type header
String filetype = file.getName(); String filetype = filePath.getFileName().toString();
int pointIndex = filetype.lastIndexOf('.'); int pointIndex = filetype.lastIndexOf('.');
if (pointIndex >= 0) filetype = filetype.substring(pointIndex + 1); if (pointIndex >= 0) filetype = filetype.substring(pointIndex + 1);
String contentType = toContentType(filetype); String contentType = toContentType(filetype);
@ -141,7 +149,7 @@ private HttpResponse generateResponse(HttpRequest request) {
//send response //send response
try { try {
response.setData(new FileInputStream(file)); response.setData(Files.newInputStream(filePath));
return response; return response;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
return new HttpResponse(HttpStatusCode.NOT_FOUND); return new HttpResponse(HttpStatusCode.NOT_FOUND);
@ -155,21 +163,21 @@ private static String timestampToString(long time){
private static long stringToTimestamp(String timeString) throws IllegalArgumentException { private static long stringToTimestamp(String timeString) throws IllegalArgumentException {
try { try {
int day = Integer.parseInt(timeString.substring(5, 7)); int day = Integer.parseInt(timeString.substring(5, 7));
int month = switch (timeString.substring(8, 11)) {
int month = Calendar.JANUARY; case "Jan" -> Calendar.JANUARY;
switch (timeString.substring(8, 11)){ case "Feb" -> Calendar.FEBRUARY;
case "Feb" : month = Calendar.FEBRUARY; break; case "Mar" -> Calendar.MARCH;
case "Mar" : month = Calendar.MARCH; break; case "Apr" -> Calendar.APRIL;
case "Apr" : month = Calendar.APRIL; break; case "May" -> Calendar.MAY;
case "May" : month = Calendar.MAY; break; case "Jun" -> Calendar.JUNE;
case "Jun" : month = Calendar.JUNE; break; case "Jul" -> Calendar.JULY;
case "Jul" : month = Calendar.JULY; break; case "Aug" -> Calendar.AUGUST;
case "Aug" : month = Calendar.AUGUST; break; case "Sep" -> Calendar.SEPTEMBER;
case "Sep" : month = Calendar.SEPTEMBER; break; case "Oct" -> Calendar.OCTOBER;
case "Oct" : month = Calendar.OCTOBER; break; case "Nov" -> Calendar.NOVEMBER;
case "Nov" : month = Calendar.NOVEMBER; break; case "Dec" -> Calendar.DECEMBER;
case "Dec" : month = Calendar.DECEMBER; break; default -> throw new IllegalArgumentException("Invalid timestamp format");
} };
int year = Integer.parseInt(timeString.substring(12, 16)); int year = Integer.parseInt(timeString.substring(12, 16));
int hour = Integer.parseInt(timeString.substring(17, 19)); int hour = Integer.parseInt(timeString.substring(17, 19));
int min = Integer.parseInt(timeString.substring(20, 22)); int min = Integer.parseInt(timeString.substring(20, 22));
@ -183,38 +191,21 @@ private static long stringToTimestamp(String timeString) throws IllegalArgumentE
} }
private static String toContentType(String fileEnding) { private static String toContentType(String fileEnding) {
String contentType = "text/plain"; return switch (fileEnding) {
switch (fileEnding) { case "json" -> "application/json";
case "json" : case "png" -> "image/png";
contentType = "application/json"; case "jpg",
break; "jpeg",
case "png" : "jpe" -> "image/jpeg";
contentType = "image/png"; case "svg" -> "image/svg+xml";
break; case "css" -> "text/css";
case "jpg" : case "js" -> "text/javascript";
case "jpeg" : case "html",
case "jpe" : "htm",
contentType = "image/jpeg"; "shtml" -> "text/html";
break; case "xml" -> "text/xml";
case "svg" : default -> "text/plain";
contentType = "image/svg+xml"; };
break;
case "css" :
contentType = "text/css";
break;
case "js" :
contentType = "text/javascript";
break;
case "html" :
case "htm" :
case "shtml" :
contentType = "text/html";
break;
case "xml" :
contentType = "text/xml";
break;
}
return contentType;
} }
} }

View File

@ -0,0 +1,78 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.util;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public class CopyingPathVisitor extends SimpleFileVisitor<Path> {
private final Path targetPath;
private final CopyOption[] options;
@Nullable private Path sourcePath;
public CopyingPathVisitor(Path target, CopyOption... options) {
this.targetPath = target;
this.options = options;
}
public CopyingPathVisitor(@Nullable Path source, Path target, CopyOption... options) {
this.sourcePath = source;
this.targetPath = target;
this.options = options;
}
@Override
public FileVisitResult preVisitDirectory(Path source, BasicFileAttributes attributes) throws IOException {
// the first directory we visit will be the sourcePath
if (sourcePath == null) sourcePath = source;
Path target = resolveTarget(source);
if (Files.notExists(target)) Files.createDirectory(target);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path source, BasicFileAttributes attributes) throws IOException {
Path target = resolveTarget(source);
Files.copy(source, target, options);
return FileVisitResult.CONTINUE;
}
/**
* Resolves the target file or directory using Path#toString() to make it compatible across different file-systems
*/
private Path resolveTarget(Path source) {
if (sourcePath == null) return targetPath;
return targetPath.resolve(sourcePath.relativize(source).toString());
}
}

View File

@ -28,7 +28,9 @@
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.URL;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttribute;
@ -46,14 +48,14 @@ public static OutputStream createFilepartOutputStream(final Path file) throws IO
return new OnCloseOutputStream(os, () -> { return new OnCloseOutputStream(os, () -> {
if (!Files.exists(partFile)) return; if (!Files.exists(partFile)) return;
FileHelper.createDirectories(folder); FileHelper.createDirectories(folder);
FileHelper.move(partFile, file); FileHelper.atomicMove(partFile, file);
}); });
} }
/** /**
* Tries to move the file atomically, but fallbacks to a normal move operation if moving atomically fails * Tries to move the file atomically, but fallbacks to a normal move operation if moving atomically fails
*/ */
public static void move(Path from, Path to) throws IOException { public static void atomicMove(Path from, Path to) throws IOException {
try { try {
Files.move(from, to, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); Files.move(from, to, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (FileNotFoundException | NoSuchFileException ignore) { } catch (FileNotFoundException | NoSuchFileException ignore) {
@ -77,4 +79,39 @@ public static Path createDirectories(Path dir, FileAttribute<?>... attrs) throws
return Files.createDirectories(dir, attrs); return Files.createDirectories(dir, attrs);
} }
/**
* Extracts the entire zip-file into the given target directory
*/
public static void extractZipFile(URL zipFile, Path targetDirectory, CopyOption... options) throws IOException {
Path temp = Files.createTempFile(null, ".zip");
FileHelper.copy(zipFile, temp);
FileHelper.extractZipFile(temp, targetDirectory, options);
Files.deleteIfExists(temp);
}
/**
* Extracts the entire zip-file into the given target directory
*/
public static void extractZipFile(Path zipFile, Path targetDirectory, CopyOption... options) throws IOException {
try (FileSystem webappZipFs = FileSystems.newFileSystem(zipFile, (ClassLoader) null)) {
CopyingPathVisitor copyAction = new CopyingPathVisitor(targetDirectory, options);
for (Path root : webappZipFs.getRootDirectories()) {
Files.walkFileTree(root, copyAction);
}
}
}
/**
* Copies from a URL to a target-path
*/
public static void copy(URL source, Path target) throws IOException {
try (
InputStream in = source.openStream();
OutputStream out = Files.newOutputStream(target)
) {
in.transferTo(out);
}
}
} }

View File

@ -45,17 +45,16 @@
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream;
@Getter @Getter
@ToString @ToString
@ -162,25 +161,33 @@ private Region getRegion(Vector2i pos) {
@Override @Override
public Collection<Vector2i> listRegions() { public Collection<Vector2i> listRegions() {
File[] regionFiles = getRegionFolder().toFile().listFiles(); try (Stream<Path> stream = Files.list(regionFolder)) {
if (regionFiles == null) return Collections.emptyList(); return stream
.map(file -> {
try {
String fileName = file.getFileName().toString();
List<Vector2i> regions = new ArrayList<>(regionFiles.length); if (RegionType.forFileName(fileName) == null) return null;
if (Files.size(file) <= 0) return null;
for (File file : regionFiles) { String[] filenameParts = fileName.split("\\.");
if (RegionType.forFileName(file.getName()) == null) continue; int rX = Integer.parseInt(filenameParts[1]);
if (file.length() <= 0) continue; int rZ = Integer.parseInt(filenameParts[2]);
try { return new Vector2i(rX, rZ);
String[] filenameParts = file.getName().split("\\."); } catch (IOException ex) {
int rX = Integer.parseInt(filenameParts[1]); Logger.global.logError("Failed to read region-file: " + file, ex);
int rZ = Integer.parseInt(filenameParts[2]); return null;
} catch (NumberFormatException ignore) {
regions.add(new Vector2i(rX, rZ)); return null;
} catch (NumberFormatException ignore) {} }
})
.filter(Objects::nonNull)
.toList();
} catch (IOException ex) {
Logger.global.logError("Failed to list regions for world: '" + getId() + "'", ex);
return List.of();
} }
return regions;
} }
@Override @Override

View File

@ -48,11 +48,11 @@
import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.commons.lang3.time.DurationFormatUtils;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.BindException; import java.net.BindException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
@ -363,7 +363,7 @@ public static void main(String[] args) {
if (blueMap != null) { if (blueMap != null) {
BlueMapConfiguration configProvider = blueMap.getConfig(); BlueMapConfiguration configProvider = blueMap.getConfig();
if (configProvider instanceof BlueMapConfigManager) { if (configProvider instanceof BlueMapConfigManager) {
Logger.global.logWarning("Please check: " + ((BlueMapConfigManager) configProvider).getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize()); Logger.global.logWarning("Please check: " + ((BlueMapConfigManager) configProvider).getConfigManager().resolveConfigFile(BlueMapConfigManager.CORE_CONFIG_NAME).toAbsolutePath().normalize());
} }
} }
System.exit(2); System.exit(2);
@ -461,19 +461,20 @@ private static void printHelp() {
private static String getCliCommand() { private static String getCliCommand() {
String filename = "bluemap-cli.jar"; String filename = "bluemap-cli.jar";
try { try {
File file = new File(BlueMapCLI.class.getProtectionDomain() Path file = Path.of(BlueMapCLI.class.getProtectionDomain()
.getCodeSource() .getCodeSource()
.getLocation() .getLocation()
.getPath()); .toURI());
if (file.isFile()) { if (Files.isRegularFile(file)) {
try { try {
filename = "." + File.separator + new File("").getCanonicalFile().toPath().relativize(file.toPath()); filename = "." + file.getFileSystem().getSeparator() +
Path.of("").toRealPath().relativize(file.toRealPath());
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
filename = file.getAbsolutePath(); filename = file.toAbsolutePath().toString();
} }
} }
} catch (IOException ignore) {} } catch (Exception ignore) {}
return "java -jar " + filename; return "java -jar " + filename;
} }