From 1b45e419ca2af0170a694b974a8b39deac7bc33e Mon Sep 17 00:00:00 2001 From: Score2 Date: Tue, 17 Mar 2026 01:09:34 +1100 Subject: [PATCH] fix(FileWatcher): gracefully handle inotify exhaustion instead of crashing When the OS inotify instance limit is reached, FileWatcher constructor threw RuntimeException causing ExceptionInInitializerError on . This permanently broke FileWatcher.INSTANCE for the ClassLoader lifetime, causing ConfigLoader to crash before registering config files, which then triggered false "@ConfigNode file not defined" warnings for all plugins using autoReload=true on the same server. - Catch IOException in constructor, set watchService=null with a warning - Skip scheduling and DISABLE callback when watchService is null - addSimpleListener is a no-op when watchService is null - INSTANCE is always a valid (possibly degraded) object --- .../java/taboolib/common5/FileWatcher.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/common-legacy-api/src/main/java/taboolib/common5/FileWatcher.java b/common-legacy-api/src/main/java/taboolib/common5/FileWatcher.java index 9c8db39ee..dd927faa4 100755 --- a/common-legacy-api/src/main/java/taboolib/common5/FileWatcher.java +++ b/common-legacy-api/src/main/java/taboolib/common5/FileWatcher.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory; import taboolib.common.LifeCycle; +import taboolib.common.PrimitiveIO; import taboolib.common.TabooLib; import taboolib.common.platform.Ghost; @@ -50,8 +51,18 @@ public class FileWatcher { private final WatchService watchService; public FileWatcher(int interval) { + WatchService ws; try { - this.watchService = FileSystems.getDefault().newWatchService(); + ws = FileSystems.getDefault().newWatchService(); + } catch (IOException e) { + PrimitiveIO.warning(PrimitiveIO.t( + "FileWatcher 初始化失败,文件自动重载功能不可用: " + e.getMessage(), + "FileWatcher failed to initialize, auto-reload is unavailable: " + e.getMessage() + )); + ws = null; + } + this.watchService = ws; + if (this.watchService != null) { this.executorService.scheduleAtFixedRate(() -> { WatchKey key; while ((key = watchService.poll()) != null) { @@ -76,8 +87,6 @@ public FileWatcher(int interval) { }, 1000, interval, TimeUnit.MILLISECONDS); // 注册关闭回调 TabooLib.registerLifeCycleTask(LifeCycle.DISABLE, 0, this::release); - } catch (IOException e) { - throw new RuntimeException(e); } } @@ -99,6 +108,9 @@ public void addSimpleListener(File file, Consumer runnable) { * @param runImmediately 是否在添加监听器时立即执行一次 */ public void addSimpleListener(File file, Consumer runnable, boolean runImmediately) { + if (watchService == null) { + return; + } if (runImmediately) { runnable.accept(file); }