Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class YamlConfig {

private final @NotNull File file;

private @NotNull YamlConfiguration cached = new YamlConfiguration();
private volatile @NotNull YamlConfiguration cached = new YamlConfiguration();

/**
* Cached SHA-256 checksum of the configuration file.
Expand All @@ -45,7 +45,7 @@ public class YamlConfig {
*
* <p>This field is updated internally and should be treated as read-only.</p>
*/
private String cachedChecksum = null;
private volatile String cachedChecksum = null;

/**
* Creates a new YAML configuration wrapper.
Expand Down Expand Up @@ -102,8 +102,9 @@ public YamlConfig(@NotNull File file) throws ConfigException {

// try to generate checksum
try {
this.cachedChecksum = FileChecksum.getSha256Checksum(file);
this.cachedChecksum = FileChecksum.computeSha256(file);
} catch (Exception e) {
e.printStackTrace(); // log the exception
this.cachedChecksum = null;
}

Expand Down Expand Up @@ -167,6 +168,10 @@ public void save() throws ConfigFileException, ConfigPermissionException {
/**
* Returns the cached configuration.
*
* <p>
* Modifications affect the cached configuration directly.
* Don't forget to save the cached config after you've made changes.
* </p>
* <p>This method does not perform any file I/O.</p>
*
* @return the cached {@link YamlConfiguration}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ public class YamlConfigWatcher {
*/
private final Map<Path, Long> lastModified = new ConcurrentHashMap<>();

/**
* Maps {@link WatchKey} instances to their corresponding directory {@link Path}.
*
* <p>This reverse mapping allows constant-time (O(1)) resolution of a directory
* from a {@link WatchKey}, avoiding linear scans over registered directories.</p>
*
* <p>The map is populated when directories are registered and cleaned up when
* {@link WatchKey}s become invalid.</p>
*
* @since 1.3.0
*/
private final @NotNull Map<WatchKey, Path> watchKeys = new ConcurrentHashMap<>();

private volatile boolean running = false;

/**
Expand Down Expand Up @@ -95,11 +108,16 @@ public void watch(@NotNull YamlConfig config) throws ConfigException {
try {
directories.computeIfAbsent(directory, dir -> {
try {
return dir.register(
WatchKey key = dir.register(
watchService,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE
);

// for reverse mapping
watchKeys.put(key, dir);

return key;
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -153,6 +171,9 @@ public void stop() {
* @since 1.3.0
*/
private void run() {
var scheduler = Bukkit.getScheduler();
var pluginManager = Bukkit.getPluginManager();

while (running) {
WatchKey key;

Expand All @@ -162,15 +183,7 @@ private void run() {
return;
}

Path dir = null;

for (Map.Entry<Path, WatchKey> entry : directories.entrySet()) {
if (entry.getValue().equals(key)) {
dir = entry.getKey();
break;
}
}

@Nullable Path dir = watchKeys.get(key);
if (dir == null) {
key.reset();
continue;
Expand All @@ -196,8 +209,9 @@ private void run() {
@Nullable String newChecksum;

try {
newChecksum = FileChecksum.getSha256Checksum(config.getFile());
newChecksum = FileChecksum.computeSha256(config.getFile());
} catch (Exception e) {
e.printStackTrace();
continue;
}

Expand All @@ -218,28 +232,33 @@ private void run() {
try {
config.reload();

// get updated checksum
String updatedChecksum = config.getCachedChecksum();

// Fire event on main thread
Bukkit.getScheduler().runTask(javaPlugin, () -> {
Bukkit.getPluginManager().callEvent(
new ConfigReloadEvent(
config.getFile().getName(),
config.get(),
oldChecksum,
updatedChecksum
)
);
});
scheduler.runTask(javaPlugin, () ->
pluginManager.callEvent(
new ConfigReloadEvent(
config.getFile().getName(),
config.get(),
oldChecksum,
updatedChecksum
)
)
);

} catch (Exception e) {
e.printStackTrace();
}
}
}

key.reset();
boolean valid = key.reset();

if (!valid) {
Path removed = watchKeys.remove(key);
if (removed != null) {
directories.remove(removed);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ public ConfigManager(@NotNull JavaPlugin javaPlugin) throws ConfigException {
throw new ConfigException("Config already registered: " + key);
}

YamlConfig config = new YamlConfig(file);

config.create();
config.load();
YamlConfig config = initialize(file);

configs.put(key, config);

Expand Down Expand Up @@ -107,15 +104,33 @@ public void registerFromJar(@NotNull File file,
copyResource(plugin, resourcePath, file);
}

YamlConfig config = new YamlConfig(file);
config.create();
config.load();
YamlConfig config = initialize(file);

configs.put(key, config);

watcher.watch(config);
}

/**
* Initializes a {@link YamlConfig} instance for the given file.
*
* <p>If the file does not exist, it is created. The configuration is then
* loaded and cached.</p>
*
* @param file the configuration file to initialize
* @return the initialized {@link YamlConfig}
*
* @throws ConfigException if file creation fails or the path is invalid
*
* @since 1.3.0
*/
private @NotNull YamlConfig initialize(File file) {
YamlConfig config = new YamlConfig(file);
config.create();
config.load();
return config;
}

/**
* Copies a resource from the plugin JAR to the specified file.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private FileChecksum() {
* <li>an I/O error occurs during reading</li>
* </ul>
*/
public static @NotNull String getSha256Checksum(File file) throws Exception {
public static @NotNull String computeSha256(File file) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");

try (InputStream fis = new FileInputStream(file)) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/paper-plugin.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: ConfigurationAPI
description: $description
description: "Lightweight YAML config API with automatic reload and event-driven updates."
version: '1.3.0'

main: dev.spexx.configurationAPI.ConfigurationAPI
Expand Down
Loading