Blessed Geek
Blessed Geek

Reputation: 21614

How to access varname when the RHS value resolution set(varname, rhs) encounters a runtime exception, in Apache JEXL3?

For the following a statement in a jexl script,

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

With that JEXL in the following order goes on to

  1. resolve Telemat.locate(map, vehicle, newLatLong).
  2. inquire the has('routeX'), so that my Java code knows which varname is being declared, and could feed back to JEXL if the varname already exists.
  3. calls the method set('routeX', <resolved value of Telemat.locate(map, vehicle, newLatLong)> )

That is, the RHS is resolved before, the has or get is called and therefore my Java code would not know the LHS varname while the RHS is evaluated.

Let's say I embedded an InAccessibleMapException cause in a RunTimeException when the map is inaccessible. And it so happens that at one instance the map was inaccessible. Which led to RunTimeException(InAccessibleMapException) being thrown.

I have try-catch

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

When JEXL throws the exception, my Java code needs to feedback an error message to the engineers I work for that includes the varname that caused the error in their script.

What is the best way or strategy, I could use to capture the varname, when an exception is thrown during the resolution of the RHS, so that the exception catcher could feed back the varname besides the error, in the log and error message?

Upvotes: 0

Views: 71

Answers (1)

henrib
henrib

Reputation: 364

The easiest route is to use the JexlInfo instance in the exception to try and determine the variable whose assignment failed. It ain't pretty but it should work in simple cases.


    /**
     * 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);
        }
    }

Upvotes: 0

Related Questions