【问题标题】:Nashorn in JDK10 Java.type() strange behaviorJDK10 Java.type() 中的 Nashorn 奇怪行为
【发布时间】:2019-01-16 07:05:33
【问题描述】:

大家好,
我正在编写一个应用程序来根据存储在旧 .dbf 文件中的信息进行计算,这些信息来自用于 MS-DOS 系统的旧 fox-pro,这些计算会生成用于新簿记 - ERP 系统的日记帐分录;用户应该能够即时添加/删除/修改条目的生成。所以,我的解决方案是使用 Nashorn 脚本引擎:我在应用程序设置中定义了一个文件夹,然后应用程序扫描该文件夹内的所有 .js 文件,该文件夹扩展了一个生成条目的抽象类(Totalizer),瞧。
一切听起来都不错,只是它不起作用 B-)
Totalizer抽象类的源码是:

public abstract class Totalizer {

    private List<Expense> expenses;
    private List<Employee> distribution;

    /**
     * 
     * @return Id of the totalizer.
     */
    public abstract String getId();

    /**
     * @return Descriptive title of the totalizer.
     */
    public abstract String getTitle();

    /**
     * Creates the JournalEntry based upon a single DBF record.
     *
     * @param record dbf record.
     * @return List of journal entries.
     * @throws java.lang.Exception if any exception is thrown.
     */
    public abstract List<JournalEntry> totalize(
            DbfRecord record) throws Exception;

    public void setExpenseMapping(List<Expense> expenses) {
        this.expenses = expenses;
    }

    public void setDistribution(List<Employee> distribution) {
        this.distribution = distribution;
    }

}

然后,我有脚本文件,我分享其中一个,因为它们都相似:

/**
* Vacations expenses entry:
*/
var BenefVacaciones = Java.extend(Totalizer, {
    getId : function(){
        return "diar.BenefVaca";
    },
    getTitle : function(){
        return "Beneficios: Vacaciones.";
    },

    totalize : function(record){
        //TODO: implement this method.
        return null;
    }
});

/*
* Creates a new instance of BenefVacaciones.
*/
function createInstance() {
    return new BenefVacaciones();
}

然后,“加载”脚本的类是 Task 的实现,它执行以下操作:

//Loads a script and returns totalizer implementation.
public Totalizer loadScript(File aFile) throws IOException, ScriptException{
    //creates the engine.
    var engine = new NashornScriptEngineFactory().getScriptEngine();
    //reads script from FileReader
    try(var reader = new FileReader(aFile)){
        var obj = engine.eval(reader);
        if(obj instanceof Invocable){//if obj is invocable
            var inv = (Invocable)obj;//casts to invocable
            var ret = inv.invokeFunction("createInstance");//invoke
            if(ret instanceof Totalizer){//if invoked function is Totalizer
                return (Totalizer)ret;//return that totalizer
            }else if(ret == null){//if invoked function returns null
                throw new NullPointerException("return value is null.");
            }else{//if returns non-null value but isn't Totalizer
                throw new ScriptException("Function createInstance won't return Totalizer");
            }
        }else if(obj == null){//WATCH: If engine.eval returns null
            throw new NullPointerException("engine.eval returns null.");
        }else{//If engine.eval returns non-invocable value.
            throw new ScriptException("Cannot get invocable object.");
        }
    }
}

当然,这段代码应该抛出一个

ScriptException, ReferenceError: "Totalizer" 未在 中定义 在线...

但有趣的是,如果我在 .js 脚本文件中添加以下行:

var Totalizer = Java.type('com.fossc.diar.onfalo.jsapi.Totalizer');

然后,engine.eval 返回 null,代码执行落入带有注释“WATCH:如果 engine.eval 返回 null”的 Totalizer 行,当我希望获得一个 Invocable 对象或任何其他对象的实例时。 没有抛出异常,所以我没有任何信息。 我还尝试在引擎声明后立即在 loadScript 方法中添加以下行:

engine.put(
    "Totalizer",
    jdk.dynalink.beans.StaticClass.forClass(Totalizer.class));

但它返回 null。还尝试添加一个始终返回 true 的类过滤器,但这都不起作用。我一直在寻找解决方案,但似乎没有任何效果,当我在任何脚本中使用 Java.type() 时,engine.eval 返回一个空对象而不是 Invocable 实例,甚至测试 engine.eval("var x = Java.type(\'java.lang.String\');") 返回 null。
出于用户界面的原因,我从 JavaFX 任务实现中调用 loadScript 函数,所以我发现了 https://stackoverflow.com/questions/33945507/java-nashorn-classnotfoundexception-java-type 的帖子,并且我尝试将 currentThread.contextClassLoader 设置为 Totalizer.class.getClassLoader()
我做错了吗?
我正在使用以下软件:

  • IDE:Apache Netbeans 9.0(构建 incubator-netbeans-release-334-on-20180708)。
  • 项目类型:JavaFX 应用程序项目。
  • 甲骨文 JDK 10
    • java --version: java 10.0.1 2018-04-17
      • Java(TM) SE 运行时环境 18.3(内部版本 10.0.1+10)
      • Java HotSpot(TM) 64 位服务器 VM 18.3(内部版本 10.0.1+10,混合模式)
    • javac --version: javac 10.0.1
  • S.O.:Manjaro Linux 17.1.11 Hakoila。

【问题讨论】:

    标签: javascript java nashorn


    【解决方案1】:

    好吧,这有点尴尬,但我发现了错误。

    var obj = engine.eval(reader);
    

    在这种情况下总是返回 null,如果我写这样的东西,它只会返回一个值:

    engine.eval("2+2");
    

    错误是引擎本身是 Invocable 对象。所以,我只需要转换引擎,而不是评估结果:

    engine.eval(reader);
    var inv = (Invocable)engine;
    var ret = inv.invokeFunction("createInstance");
    if(ret instanceof Totalizer){
        return (Totalizer) ret;
    }else if(ret == null){
        throw new NullPointerException("createInstance returns null.");
    }else{
        throw new ScriptException("createInstance dont return Totalizer.");
    }
    

    一切正常。当然,在这种情况下,如果我在没有engine.put 或Java.type 的情况下执行engine.eval,那么会出现ReferenceError,因为脚本端不存在Totalizer,看起来我只需要“导入”该类.但是该脚本的 engine.eval 应该总是返回 null,所以当错误被修复时,它就是这样做的:返回 null。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-06
      • 2012-09-01
      • 2012-02-02
      相关资源
      最近更新 更多