Define and reuse MVEL function

no id picture no id · Sep 28, 2012 · Viewed 7.2k times · Source

Is it possible to define some custom precompiled function with MVEL and use one in another compiled expressions? If I try the following:

MVEL.compileExpression("function hello() { System.out.println(\"Hello!\"); hello(); return x * y;");

I get Exception in thread "main" [Error: duplicate function: hello] when try to execure it second time.

It looks like I should declare the function not at the expression itself, but some kind of context. I played with ParsedContext for a while, but always got Exception in thread "main" [Error: unable to access property (null parent): hello].

Internet is full of tutorials that cover different MVEL language usage topics, but such embedding topics a very poorly documented. Could you please give me some clue what I'm doing wrong?

P.S. I don't want to execute some function from Java. I need to define them dynamically with MVEL.

Answer

Mike Brock picture Mike Brock · Sep 29, 2012

This is a bit trickier than some common use-cases, although it is possible.

Fundamentally, what you need to do is create a script which defines your functions, like so:

VariableResolverFactory functionFactory = new MapVariableResolverFactory();
MVEL.eval("def foo() { System.out.println("foo"); }; def bar() { System.out.println("bar") };", functionFactory);

Then, what you do is back-chain this factory to any expression you run:

VariableResolverFactory myVarFactory = new MapVariableResolverFactory();
myVarFactory.setNextFactory(functionFactory);

Serializable s = MVEL.compileExpression("foo(); bar();");

MVEL.executeExpression(s, myVarFactory);

Functions are recorded as references on the variable table, so you can use regular variable factory back-chaining to share them in MVEL.

Note: You shouldn't re-share 'myVarFactory'. You want to create a new one every time. You only want to recycle 'functionFactory' and back-chain it to the per-run factories as shown above. Otherwise you'll end up sharing local var state between executions -- and that's not thread-safe