3

Problem Statement: Say I have a expression (a + b + c), and I want to calculate its value and assign to some variable. Later I want use that variable value in some other logic. This is all done through MVEL. Issue is if anyone out of (a,b,c) is null, MVEL evaluates in a string format.

So to avoid this, I created my own function to pass each object and if it is null, make it zero.

Sample code below

public class MvelTest {

    public static void main(String[] args) {

        Map map = new HashMap();

        VariableResolverFactory functionFactory = new MapVariableResolverFactory(map);
        MVEL.eval("checkNullValue = def (x) { x == null ? 0 : x };", functionFactory);

        map.put("a", null);
        map.put("b", 1);
        map.put("c", 1);

        Serializable str = MVEL.compileExpression("( ( checkNullValue(a) + checkNullValue(b) + checkNullValue(c) ) > 2 ) ? d=2 : d=3");

        MVEL.executeExpression(str, map, functionFactory);
        System.out.println(map);
        System.out.println(map.get("d"));
    }
}

Output

{checkNullValue=function_prototype:null, b=1, c=1, a=null}
null

I am not able to get value of "d" here, and if I remove factory and null check function it behaves and I am able to get the value of "d". But I have to make it null safe for arithmetic operation, Since MVEL cannot handle this.

Also (null * 23), MVEL returns as false.

tobias_k
  • 74,298
  • 11
  • 102
  • 155
Ankur Singhal
  • 23,626
  • 10
  • 70
  • 108
  • I have tried the above with Parser Context also by passing by own class with static method doing same manipulation, but still i could not get the value of "d" – Ankur Singhal Jun 16 '14 at 11:52
  • Not sure why, but it works if you put the assignment to the front of the ternary expression: `MVEL.compileExpression("d = (cnv(a) + cnv(b) + cnv(c) > 2) ? 2 : 3")` – tobias_k Jun 16 '14 at 14:01
  • "...use that variable in some other logic." Variables in a rule have a scope that is limited to a rule - otherwise it would be havoc. So, I have some doubt whether that `d` will be available in another rule, to say nothing of the problem that evaluation (not firing!) order of LHS logic is absolutely unspecified. Passing data between rules should be done with *facts*. – laune Jun 16 '14 at 14:44

2 Answers2

2

The problem is with your ternary operator. I am not sure how MVEL evaluates those (the way you use them would be illegal in Java), but it seems like putting the assignment in the then/else part does not work... or rather, it (for whatever reason) does work for the 'then' part (before :) but fails for the 'else' part (after :).

So if the sum is > 2 it works, whether or not you are using the null-check function, and otherwise it fails.

You should fix your expression and put the assignment in front of the ternary operator:

MVEL.compileExpression("d = cnv(a) + cnv(b) + cnv(c) > 2 ? 2 : 3")

Update: Generally, this is what I observed, independently of a, b, c, and cnv:

MVEL.compileExpression("true  ? d=1 : d=2"); // d ends up as 1
MVEL.compileExpression("false ? d=1 : d=2"); // d is null / unknown
MVEL.compileExpression("d = guard ? 1 : 2"); // always works
tobias_k
  • 74,298
  • 11
  • 102
  • 155
  • But the same works for me, if i remove factory or parser objects. Laune This you can rethink for rule chaining concept, output of one becomes input to another and so on, this does work for me using MVEL API itself, just some issues while using cnv – Ankur Singhal Jun 16 '14 at 14:55
  • @ankur-singhal Test it again. I thouroughly tested it: With `cnv` and without, and for different values of `a, b, c`. When a+b+c>2 (or whatever was the cutoff value) then it worked in both cases, if it was not then it didn't. See my edit. – tobias_k Jun 16 '14 at 15:07
  • public static void main(String[] args) { Map map = new HashMap(); VariableResolverFactory functionFactory = new MapVariableResolverFactory(map); MVEL.eval("checkNullValue = def (x) { x == null ? 0 : x };", functionFactory); map.put("a", 3); map.put("b", 1); map.put("c", 1); Serializable str = MVEL.compileExpression("( ( (a) + (b) + (c) ) > 2 ) ? d=2 : d=3"); MVEL.executeExpression(str, map, functionFactory); System.out.println(map); System.out.println(map.get("d")); } output d=2 – Ankur Singhal Jun 16 '14 at 15:49
  • @ankur-singhal Yes, I know. And now set `a` to `0` and try again. (not `null`, but `0`, or `-1`, or any other value that triggers the `else` case) – tobias_k Jun 16 '14 at 16:09
0

Issue got resolved, i was actually creating the static reference for VariableResolverFactory and was referring the same for every evaluation, i changed it to new instance for every execution and it worked.

 public VariableResolverFactory getMvelFactory(Map contextMap) {
        VariableResolverFactory functionFactory = new MapVariableResolverFactory(contextMap);
        MVEL.eval("checkNullValue = def (x) { x == null ? 0 : x };", functionFactory);

        return functionFactory;
    }
tobias_k
  • 74,298
  • 11
  • 102
  • 155
Ankur Singhal
  • 23,626
  • 10
  • 70
  • 108
  • So... the actual problem was totally unrelated to the code posted in your question, and the 'd is null' problem that you describe in the question was totally unrelated to your actual problem? Just asking... – tobias_k Jun 22 '14 at 20:36
  • Not exactly, actually still figuring out how this static factory causing problem. – Ankur Singhal Jun 23 '14 at 04:16
  • And you are sure that it's not the problem I describe in my answer? Have you _tried_ changing the expression so the assignment is in front of the ternary operator, i.e. `d = ? 2 : 3`? – tobias_k Jun 23 '14 at 07:34
  • Yes i have tried, even if you run my sample code without using VariableResolverFactory , it executes properly. – Ankur Singhal Jun 23 '14 at 10:23
  • I'm sorry I have to insist on this, but that's not what I experienced. When you ran it without the null-check function, did you set `a` to `0`, or to anything other, e.g. `a=3`? Please see the last part of my answer (Update:...) and check whether you get the same results for this. – tobias_k Jun 23 '14 at 10:30
  • Yes correct, re run the same and got the result. My expression was wrong, but in my case, theer was some issue with static reference i suppose. – Ankur Singhal Jun 23 '14 at 10:50
  • Understood. So my answer correctly identified and solved the problem in your question, but you still have some other problem that's not really related to that. – tobias_k Jun 23 '14 at 11:13