自从进行更改后,我突然遇到了 Nashorn 的问题,但我不确定问题的实际根本原因。
我在 Java 应用程序中使用 Nashorn (Nashorn 15.3) Javascript 引擎,允许客户通过使用已知掩码变量定义掩码来根据元数据重命名其音乐文件,大约有 200 个掩码变量。
例如,用户指定掩码为 艺术家 - 专辑
然后对于每首歌曲,我们执行以下操作:
ScriptEngine engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine();
for(SongFieldName next:SongFieldName.values())
{
engine.put(next.getScriptVar(), song.getFieldValue(next.getSongFieldKey());
}
file.setFilename((String)engine.eval(mask));
所以我们得到了脚本引擎,然后对于歌曲,我们放置包含掩码变量(例如艺术家)和歌曲中的实际值(例如Stevie Wonder)的键值对,然后我们评估掩码并使用它来设置文件名,这多年来一直运行良好。
但是,值可以是多个值。例如,艺术家值可能包含多个人,因此我希望有一种简单的方法来获取索引值。因此,对于每个变量,我创建了 10 个版本,每个版本映射到下一个索引值,如果没有值,则为空白,如下所示。
List<String> values = song.getFieldValues(next.getSongFieldKey());
for(int i=0; i < 10;i++)
{
if(values.size()>i)
{
engine.put(next.getScriptVar() + "_index" + i,values.get(i));
}
else
{
engine.put(next.getScriptVar() + "_index" + i, "");
}
}
这看起来不错,但客户报告了一个问题,我已经复制了仅在添加此额外代码时才会出现的问题。
JavascriptEngine 似乎随机抱怨 discno 字段不是已知变量,即使它总是被添加。
javax.script.ScriptException: ReferenceError: "discno" is not defined in <eval> at line number 81
at org.openjdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:463)
at org.openjdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:447)
at org.openjdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399)
at org.openjdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:395)
at org.openjdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:151)
at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262)
所以看来我可能遇到了一些限制,现在它被覆盖了,而不是添加 200 个变量,而是向引擎添加了大约 2000 个变量?
对于每首歌曲,我尝试先使用 engine.getBindings(ScriptContext.ENGINE_SCOPE).clear() 清除所有值对,但仍然失败。
或者我是否需要同步使用 ScriptEngine,因为它是从工厂返回的?
更新
我添加了同步代码,它似乎工作正常,但在大约 10,000 首歌曲时,它开始出错,每首歌曲都有相同的错误。
工厂getScriptEngine()代码如下
公共 ScriptEngine getScriptEngine() { 尝试 { 返回新的 NashornScriptEngine(this, DEFAULT_OPTIONS, getAppClassLoader(), (ClassFilter)null); } catch (RuntimeException var2) { 运行时异常 e = var2; 如果(上下文.DEBUG){ e.printStackTrace(); }
throw e;
}
}
所以我们没有重用相同的脚本引擎,但也许脚本引擎正在共享变量缓存?
您可以为每个评估创建一个新的 ScriptEngine 实例。或者使用不同的数据结构,这是处理问题的好方法。