Skip to content

Commit 60a8f5f

Browse files
committed
Close storages that failed to load and misconfigured maps dont stop loading the others
1 parent 478ccf5 commit 60a8f5f

File tree

3 files changed

+134
-87
lines changed

3 files changed

+134
-87
lines changed

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

Lines changed: 114 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
5454
import org.spongepowered.configurate.loader.HeaderMode;
5555

56+
import java.io.Closeable;
5657
import java.io.IOException;
5758
import java.lang.reflect.Type;
5859
import java.net.URL;
@@ -67,7 +68,7 @@
6768
* This is the attempt to generalize as many actions as possible to have CLI and Plugins run on the same general setup-code.
6869
*/
6970
@DebugDump
70-
public class BlueMapService {
71+
public class BlueMapService implements Closeable {
7172
private final ServerInterface serverInterface;
7273
private final BlueMapConfigProvider configs;
7374

@@ -154,106 +155,112 @@ public synchronized void createOrUpdateWebApp(boolean force) throws Configuratio
154155
}
155156
}
156157

157-
public synchronized Map<String, World> getWorlds() throws ConfigurationException, InterruptedException {
158+
public synchronized Map<String, World> getWorlds() throws InterruptedException {
158159
if (worlds == null) loadWorldsAndMaps();
159160
return worlds;
160161
}
161162

162-
public synchronized Map<String, BmMap> getMaps() throws ConfigurationException, InterruptedException {
163+
public synchronized Map<String, BmMap> getMaps() throws InterruptedException {
163164
if (maps == null) loadWorldsAndMaps();
164165
return maps;
165166
}
166167

167-
private synchronized void loadWorldsAndMaps() throws ConfigurationException, InterruptedException {
168+
private synchronized void loadWorldsAndMaps() throws InterruptedException {
168169
maps = new HashMap<>();
169170
worlds = new HashMap<>();
170171

171172
for (var entry : configs.getMapConfigs().entrySet()) {
172-
MapConfig mapConfig = entry.getValue();
173+
try {
174+
loadMapConfig(entry.getKey(), entry.getValue());
175+
} catch (ConfigurationException ex) {
176+
Logger.global.logError(ex);
177+
}
178+
}
179+
180+
worlds = Collections.unmodifiableMap(worlds);
181+
maps = Collections.unmodifiableMap(maps);
182+
}
173183

174-
String id = entry.getKey();
175-
String name = mapConfig.getName();
176-
if (name == null) name = id;
184+
private synchronized void loadMapConfig(String id, MapConfig mapConfig) throws ConfigurationException, InterruptedException {
185+
String name = mapConfig.getName();
186+
if (name == null) name = id;
177187

178-
Path worldFolder = mapConfig.getWorld();
188+
Path worldFolder = mapConfig.getWorld();
179189

180-
// if there is no world configured, we assume the map is static, or supplied from a different server
181-
if (worldFolder == null) {
182-
Logger.global.logInfo("The map '" + name + "' has no world configured. The map will be displayed, but not updated!");
183-
continue;
184-
}
190+
// if there is no world configured, we assume the map is static, or supplied from a different server
191+
if (worldFolder == null) {
192+
Logger.global.logInfo("The map '" + name + "' has no world configured. The map will be displayed, but not updated!");
193+
return;
194+
}
185195

186-
if (!Files.isDirectory(worldFolder)) {
187-
throw new ConfigurationException("Failed to load map '" + id + "': \n" +
188-
"'" + worldFolder.toAbsolutePath().normalize() + "' does not exist or is no directory!\n" +
189-
"Check if the 'world' setting in the config-file for that map is correct, or remove the entire config-file if you don't want that map.");
190-
}
196+
if (!Files.isDirectory(worldFolder)) {
197+
throw new ConfigurationException(
198+
"'" + worldFolder.toAbsolutePath().normalize() + "' does not exist or is no directory!\n" +
199+
"Check if the 'world' setting in the config-file for that map is correct, or remove the entire config-file if you don't want that map.");
200+
}
191201

192-
String worldId;
202+
String worldId;
203+
try {
204+
worldId = getWorldId(worldFolder);
205+
} catch (IOException ex) {
206+
throw new ConfigurationException(
207+
"Could not load the ID for the world (" + worldFolder.toAbsolutePath().normalize() + ")!\n" +
208+
"Make sure BlueMap has read and write access/permissions to the world-files for this map.",
209+
ex);
210+
}
211+
212+
World world = worlds.get(worldId);
213+
if (world == null) {
193214
try {
194-
worldId = getWorldId(worldFolder);
215+
world = new MCAWorld(worldFolder, mapConfig.getWorldSkyLight(), mapConfig.isIgnoreMissingLightData());
216+
worlds.put(worldId, world);
195217
} catch (IOException ex) {
196-
throw new ConfigurationException("Failed to load map '" + id + "': \n" +
197-
"Could not load the ID for the world!\n" +
198-
"Make sure BlueMap has read and write access/permissions to the world-files for this map.",
218+
throw new ConfigurationException(
219+
"Failed to load world '" + worldId + "' (" + worldFolder.toAbsolutePath().normalize() + ")!\n" +
220+
"Is the level.dat of that world present and not corrupted?",
199221
ex);
200222
}
223+
}
201224

202-
World world = worlds.get(worldId);
203-
if (world == null) {
204-
try {
205-
world = new MCAWorld(worldFolder, mapConfig.getWorldSkyLight(), mapConfig.isIgnoreMissingLightData());
206-
worlds.put(worldId, world);
207-
} catch (IOException ex) {
208-
throw new ConfigurationException("Failed to load world (" + worldId + ") for map '" + id + "'!\n" +
209-
"Is the level.dat of that world present and not corrupted?",
210-
ex);
211-
}
212-
}
213-
214-
Storage storage = getStorage(mapConfig.getStorage());
225+
Storage storage = getStorage(mapConfig.getStorage());
215226

216-
try {
217-
BmMap map = new BmMap(
218-
id,
219-
name,
220-
worldId,
221-
world,
222-
storage,
223-
getResourcePack(),
224-
mapConfig
225-
);
226-
maps.put(id, map);
227-
228-
// load marker-config by converting it first from hocon to json and then loading it with MarkerGson
229-
ConfigurationNode markerSetNode = mapConfig.getMarkerSets();
230-
if (markerSetNode != null && !markerSetNode.empty()) {
231-
String markerJson = GsonConfigurationLoader.builder()
232-
.headerMode(HeaderMode.NONE)
233-
.lenient(false)
234-
.indent(0)
235-
.buildAndSaveString(markerSetNode);
236-
Gson gson = MarkerGson.addAdapters(new GsonBuilder())
237-
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
238-
.create();
239-
Type markerSetType = new TypeToken<Map<String, MarkerSet>>() {}.getType();
240-
Map<String, MarkerSet> markerSets = gson.fromJson(markerJson, markerSetType);
241-
map.getMarkerSets().putAll(markerSets);
242-
}
227+
try {
243228

244-
} catch (ConfigurateException | JsonParseException ex) {
245-
throw new ConfigurationException("Failed to load map '" + id + "': \n" +
246-
"Failed to create the markers for this map!\n" +
247-
"Make sure your marker-configuration for this map is valid.",
248-
ex);
249-
} catch (IOException ex) {
250-
throw new ConfigurationException("Failed to load map '" + id + "'!", ex);
229+
BmMap map = new BmMap(
230+
id,
231+
name,
232+
worldId,
233+
world,
234+
storage,
235+
getResourcePack(),
236+
mapConfig
237+
);
238+
maps.put(id, map);
239+
240+
// load marker-config by converting it first from hocon to json and then loading it with MarkerGson
241+
ConfigurationNode markerSetNode = mapConfig.getMarkerSets();
242+
if (markerSetNode != null && !markerSetNode.empty()) {
243+
String markerJson = GsonConfigurationLoader.builder()
244+
.headerMode(HeaderMode.NONE)
245+
.lenient(false)
246+
.indent(0)
247+
.buildAndSaveString(markerSetNode);
248+
Gson gson = MarkerGson.addAdapters(new GsonBuilder())
249+
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
250+
.create();
251+
Type markerSetType = new TypeToken<Map<String, MarkerSet>>() {}.getType();
252+
Map<String, MarkerSet> markerSets = gson.fromJson(markerJson, markerSetType);
253+
map.getMarkerSets().putAll(markerSets);
251254
}
252255

256+
} catch (ConfigurateException | JsonParseException ex) {
257+
throw new ConfigurationException(
258+
"Failed to create the markers for map '" + id + "'!\n" +
259+
"Make sure your marker-configuration for this map is valid.",
260+
ex);
261+
} catch (IOException | ConfigurationException ex) {
262+
throw new ConfigurationException("Failed to load map '" + id + "'!", ex);
253263
}
254-
255-
worlds = Collections.unmodifiableMap(worlds);
256-
maps = Collections.unmodifiableMap(maps);
257264
}
258265

259266
public synchronized Storage getStorage(String storageId) throws ConfigurationException {
@@ -272,8 +279,20 @@ public synchronized Storage getStorage(String storageId) throws ConfigurationExc
272279
storage = storageConfig.createStorage();
273280
storage.initialize();
274281
} catch (Exception ex) {
275-
throw new ConfigurationException("Failed to load and initialize the storage '" + storageId + "'!",
276-
ex);
282+
ConfigurationException confEx = new ConfigurationException(
283+
"Failed to load and initialize the storage '" + storageId + "'!",
284+
ex
285+
);
286+
287+
if (storage != null) {
288+
try {
289+
storage.close();
290+
} catch (Exception closeEx) {
291+
confEx.addSuppressed(closeEx);
292+
}
293+
}
294+
295+
throw confEx;
277296
}
278297

279298
storages.put(storageId, storage);
@@ -404,4 +423,23 @@ public BlueMapConfigProvider getConfigs() {
404423
return configs;
405424
}
406425

426+
@Override
427+
public void close() throws IOException {
428+
IOException exception = null;
429+
430+
for (Storage storage : storages.values()) {
431+
try {
432+
if (storage != null) {
433+
storage.close();
434+
}
435+
} catch (IOException ex) {
436+
if (exception == null) exception = ex;
437+
else exception.addSuppressed(ex);
438+
}
439+
}
440+
441+
if (exception != null)
442+
throw exception;
443+
}
444+
407445
}

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -372,19 +372,17 @@ public void unload() {
372372
if (webServer != null) webServer.close();
373373
webServer = null;
374374

375-
//close storages
376-
if (maps != null) {
377-
maps.values().forEach(map -> {
378-
try {
379-
map.getStorage().close();
380-
} catch (IOException ex) {
381-
Logger.global.logWarning("Failed to close map-storage for map '" + map.getId() + "': " + ex);
382-
}
383-
});
375+
//close bluemap
376+
if (blueMap != null) {
377+
try {
378+
blueMap.close();
379+
} catch (IOException ex) {
380+
Logger.global.logError("Failed to close a bluemap-service!", ex);
381+
}
384382
}
385-
386-
//clear resources and configs
387383
blueMap = null;
384+
385+
//clear resources
388386
worlds = null;
389387
maps = null;
390388

BlueMapCore/src/main/java/de/bluecolored/bluemap/core/logger/Logger.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ public abstract class Logger {
2828

2929
public static Logger global = stdOut();
3030

31+
public void logError(Throwable throwable) {
32+
logError(throwable.getMessage(), throwable);
33+
}
34+
3135
public abstract void logError(String message, Throwable throwable);
3236

3337
public abstract void logWarning(String message);
@@ -56,6 +60,13 @@ public abstract class Logger {
5660
*/
5761
public abstract void noFloodDebug(String key, String message);
5862

63+
/**
64+
* Only log the error if no message has been logged before with the same content.
65+
*/
66+
public void noFloodError(Throwable throwable){
67+
noFloodError(throwable.getMessage(), throwable);
68+
}
69+
5970
/**
6071
* Only log the error if no message has been logged before with the same content.
6172
*/

0 commit comments

Comments
 (0)