package net.jstom.script; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.HostAccess; import org.graalvm.polyglot.Source; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class ScriptManager { private final File scriptsDir; private final Map loadedScripts = new HashMap<>(); public ScriptManager(File scriptsDir) { this.scriptsDir = scriptsDir; } public void load() { if (!scriptsDir.exists()) { scriptsDir.mkdirs(); } File[] files = scriptsDir.listFiles((dir, name) -> name.endsWith(".js")); if (files != null) { for (File file : files) { loadScript(file); } } } public void loadScript(File file) { String fileName = file.getName(); unloadScript(fileName); // Ensure clean slate if reloading System.out.println("[JStom] Loading script: " + fileName); ScriptApi api = new ScriptApi(); Context context = Context.newBuilder("js") .allowHostAccess(HostAccess.ALL) .allowHostClassLookup(s -> true) .allowIO(true) .build(); context.getBindings("js").putMember("server", api); try { context.eval(Source.newBuilder("js", file).build()); loadedScripts.put(fileName, new ScriptEnv(context, api, file)); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { System.err.println("[JStom] Error executing script " + fileName); e.printStackTrace(); // Cleanup if failed api.cleanup(); context.close(); } } public void unloadScript(String fileName) { ScriptEnv env = loadedScripts.remove(fileName); if (env != null) { env.api.cleanup(); env.context.close(); System.out.println("[JStom] Unloaded script: " + fileName); } } public void unload() { // Copy keys to avoid ConcurrentModificationException for (String fileName : new java.util.ArrayList<>(loadedScripts.keySet())) { unloadScript(fileName); } } public void reload() { unload(); load(); } public void reload(String fileName) { File file = new File(scriptsDir, fileName); if (file.exists()) { loadScript(file); } else { System.err.println("[JStom] File not found: " + fileName); } } private static class ScriptEnv { final Context context; final ScriptApi api; final File file; public ScriptEnv(Context context, ScriptApi api, File file) { this.context = context; this.api = api; this.file = file; } } }