Nashorn在JDK10 Java.type()中的异常行为

时间:2018-08-08 22:21:07

标签: javascript java nashorn

大家好,
我正在编写一个应用程序,该应用程序基于旧的fox-pro for MS-DOS系统中旧的.dbf文件中存储的信息进行计算,这些计算生成日记帐分录以用于新的记帐ERP系统;用户应该能够即时添加/删除/修改条目的生成。因此,我的解决方案是使用Nashorn脚本引擎:我在应用程序设置中定义了一个文件夹,然后应用程序扫描该文件夹内的所有.js文件,该文件扩展了生成条目的抽象类(Totalizer)和voila。
一切听起来不错,除了它不起作用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,当我希望至少获得一个Invocable对象或任何其他对象的实例时,代码执行将落入Totalizer行,并带有注释“ WATCH:If engine.eval返回null”。 没有异常被抛出,所以我没有任何信息。 我还尝试在引擎声明之后立即将以下行添加到loadScript方法:

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

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

  • IDE:Apache Netbeans 9.0(在20180708上构建incubator-netbeans-release-334-on)。
  • 项目类型:JavaFX应用程序项目。
  • Oracle JDK 10
    • java --version:Java 10.0.1 2018年4月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。

1 个答案:

答案 0 :(得分:0)

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

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。