首页 > 解决方案 > 当 RHS 值解析集(varname,rhs)在 Apache JEXL3 中遇到运行时异常时如何访问 varname?

问题描述

对于 jexl 脚本中的以下语句,

routeX = Telemat.locate(map, vehicle, newLatLong);

使用该 JEXL 按以下顺序继续

  1. 解决 Telemat.locate(map, vehicle, newLatLong)。
  2. 查询 has('routeX'),以便我的 Java 代码知道声明了哪个 varname,并且如果 varname 已经存在,可以反馈给 JEXL。
  3. 调用方法 set('routeX', <resolved value of Telemat.locate(map, vehicle, newLatLong)> )

也就是说,RHS 之前已解析,调用了 has 或 get,因此我的 Java 代码在评估 RHS 时将不知道 LHS varname。

假设当地图无法访问时,我在 RunTimeException 中嵌入了 InAccessibleMapException 原因。碰巧的是,在某个情况下,地图无法访问。这导致 RunTimeException(InAccessibleMapException) 被抛出。

我有try-catch

try {
   jexlScript.execute(script);
} catch (RunTimeException rte) {
// but my code does not know the varname.
}

当 JEXL 抛出异常时,我的 Java 代码需要向我工作的工程师反馈一条错误消息,其中包括导致脚本错误的 varname。

什么是最好的方法或策略,当在 RHS 解析期间引发异常时,我可以使用捕获 varname,以便异常捕获器可以在日志和错误消息中反馈除错误之外的 varname?

标签: javajexl

解决方案


最简单的方法是使用异常中的 JexlInfo 实例来尝试确定分配失败的变量。它不漂亮,但它应该在简单的情况下工作。


    /**
     * Finds the variable name (if any) when failing on assignment
     * @param xany the exception
     * @param script the script
     * @return the variable name or "" if not found
     */
    private static String seekVariable(JexlException xany, JexlScript script) {
        JexlInfo info = xany.getInfo();
        int line = info.getLine();
        int column = info.getColumn();
        String src = script.getSourceText();
        int end = src.length();
        int pos = 0;
        // find absolute position
        while (pos < end){
            char c = src.charAt(pos);
            if (line == 1) {
                pos += column;
                break;
            }
            if (c == '\n') {
                line -= 1;
            }
            pos += 1;
        }
        boolean fail = true;
        // crawl backwards to find '='
        while(pos > 0) {
            char c = src.charAt(pos--);
            if (c == '=') {
                fail = false;
                break;
            }
        }
        if (fail) return "";
        fail = true;
        // skip potential spaces
        while(pos > 0) {
            char c = src.charAt(pos);
            if (!Character.isSpaceChar(c)) {
                break;
            }
            pos -= 1;
        }
        int endName = pos;
        int beginName = --pos;
        // read varname
        while(pos > 0) {
            char c = src.charAt(pos);
            if (!Character.isJavaIdentifierPart(c)) {
                break;
            }
            beginName = --pos;
        }
        return src.substring(beginName, endName + 1);
    }
    @Test
    public void test339() throws Exception {
        Map<String,Object> telemap = new HashMap<String,Object>() {
            @Override public Object get(Object name) {
                throw new IllegalStateException("inaccessible map");
            }
        };
        JexlContext ctxt = new MapContext();
        ctxt.set("tele", telemap);
        ctxt.set("route", null);
        JexlEngine jexl = new JexlBuilder().create();
        String src = "route = tele.get('11')";
        JexlScript script = jexl.createScript(src);
        Object result;
        try {
            result = script.execute(ctxt);
        } catch(JexlException xany) {
            String varname = seekVariable(xany, script);
            Assert.assertEquals("route", varname);
        }
    }

推荐阅读