MVEL started out as an expression evaluator for
the Valhalla project by Mike Brock. Valhalla itself was an early Seam like framework for automating
"out of the box" web applications, and while Valhalla itself is now dormant, MVEL continues forward
as an actively developed project. It was originally compared to projects such as OGNL, JEXL and
JUEL; however, it has since far surpassed them in both terms of performance, features and ease of
use – particularly with regards to integration. It does not try to be Yet Another JVM Language, but
instead focuses on solving the problems of embedded scripting

MVEL is particularly ideal for restrictive
environments that can't use bytecode generation due to memory restrictions or sand boxing. Instead
of trying to re-invent Java, it instead aims to provide a familiar syntax for Java programmers
while also adding syntactic sugar for short and concise expressions.

MVEL is an integral part of the Drools rule
engine, http://www.jboss.org/drools/, and many of the
tight integration points were developed in partnership with the Drools team. At the time other
scripting languages where reviewed, but exhibited the following issues:

Those without bytecode generation where very slow at runtime execution.

Too much memory consumption.

Large jar/dependency size

Shameless plug: Come and learn about Drools & jBPM, and other AI technologies at Rules Fest,
http://rulesfest.org/html/home.html. Red Hat will
be co-located at Rules Fest and are providing two free days - one dedicated Drools and & jBPM
technology, the other focused on Healthcare and Open Source. For more details on those two days
see, http://community.jboss.org/wiki/DroolsJBPMBootCampHealthcareFocusSanFranciscoApril2011.

Property Accessors and Data
Structures

MVEL supports a JavaScript-like syntax that we
know and love from other expression languages, with support for simplified property accessors,
inline maps and lists:

[1, 2, 3, 4] // array

["key1" : 1, "key2" : 3 ] // map

person.name // simple property accessor

person.pets["rover"].age // map
accessor

person.pets["rover"].?age // null safe
accessor

Using the 'with' statement and inline constructor
setters, the following are functionally equivalent:

with ( new Person() ) {

name = "Bobba Fet", age = "50"

}

new Person().{

name = "Bobba Fet", age = "50"

}

This can be used with the inline maps and lists
syntax for complex data structure creation:

[ people : [

new Person().{ name = "Bobba Fet" },

new Person().{ name = "Darth Vader" }

],

places : [

new Place().{ name = "Death Star" }

]

]

Like some other languages the return values in
MVEL default to the last value in any routine. However, you can also use the 'return' keyword to
return from a routine manually.

if (val) { “foo”}

else

{ “bar”};

Is the same as:

if (val) { return “foo” }

else

{ return “bar” };

This example returns a Map with the key's "room"
and "doors", the Door references two previously created instances from the room Map:

rooms = [

"basement" : new Room("basement"),

"lounge" : new Room("kitchen")

];

// return is implicit,

// but can be explicitly added if preferred

doors = [

new Door(

rooms["kitchen"],
rooms["basement"]

)

];

[ "rooms" : room, "doors" : doors ];

'switch',
'for', 'foreach', 'if', 'do while', 'do until', 'while' and 'until' block statements are all
available and examples can found at the MVEL website.

External Variable
Dictionaries

MVEL can evaluate a given expression using a Map
to provide the dictionary of available variables:

Map vars = new HashMap();

vars.put( "person", p );

// p is a previous created instance

// p.pets references a Map.

Dog dog =

( Dog ) MVEL.eval( "person.pets['rover']", vars
);

The above will compile and execute the String in a
single pass, using MVEL's "interpreted" mode. It is also possible to pre-compile scripts for faster
execution.

MVEL was developed with a performance-optimized
parser, which keeps the jar size small and means MVEL does not require any additional external
dependencies. The jar is around just 700kb, including debug information. The compiled statement is
still interpreted, using an optimized internal execution graph. And for performance-sensitive code,
MVEL's optimizer can emit bytecode to accelerate field and method access. Work is under-way for an
optional secondary layer to fully compile executable statements to bytecode for “fast as Java”
execution.

Example of compilation:

ExecutableStatement stmt =

MVEL.compileExpression( "person.pets['rover']"
);

MVEL.executeExpression( stmt, vars );

A context object can be used to execute scripts
against that provided object. Notice in the example below we can access the 'pet's property
directly, this would work for method's on Person too:

Dog dog =

( Dog ) MVEL.eval( "pets['rover']", p
);

Context objects and variable dictionaries can be
used together:

vars.put( "otherPet", patch )

// patch is another pet

MVEL.eval(

"pets['rover'].age == otherPet.age", p,
vars

);

Import statements are possible and '*' package
imports can be used too:

"import org.domain.Dog; pets['rover'] == new Dog('patch',
7)"

Class imports can be added programmatically, via a
re-usable ParserConfiguration, which is ideal if the imports can be shared with a large number of
expressions during compilation (I'll explain ParserContext in a moment).

ParserConfiguration pconf =

new ParserConfiguration();

pconf.addImport( "Dog", Dog.class );

ParserContext pctx = new ParserContext( pconf );

MVEL.compileExpression(

"pets['rover'] == new Dog('patch', 7)",
pctx

);

Type Information and Type
Safety

The ParserContext can be used to optionally
provide type information at compile time for external variables. Where type information is
available the compiler can make better assumptions, resulting in much faster execution performance.
Local variable assignments support type inference and will also be optimised if they can infer
type.

ParserContext pctx = new ParserContext( );

pctx.addInput( “p”, Person.class );

MVEL.compileExpression( "p.pets['rover']", pctx
);

So far, MVEL has been executing dynamically in a
non type safe way -- in a similar manner to other expression languages, like OGNL or JEXL. But
compilation can be made to ensure full type safety, using optional strong typing. If type
information is not available or cannot be inferred compilation will fail:

ParserContext pctx = ParserContext.create()

.stronglyTyped()

.withInput(“p”, Person.class);

MVEL.compileExpression( "p.pets['rover']", pctx
);

Setting the type of the context object can be done
via the 'this' input:

ParserContext pctx = ParserContext.create()

.stronglyTyped()

.withInput( “this”, Person.class );

MVEL.compileExpression( "this.toString()", pctx );

Full type inference is provided for local
variables, so type declaration or casting while supported are optional. MVEL will also infer type
information from generics where available:

public static class Person {

private Map<String, Pet> pets;

...

}

...

ParserContext pctx = ParserContext.create()

.stronglyTyped();

.withInput( “person”, Person.class );

//Both the following MVEL statements are valid and type
safe:

MVEL.compileExpression( "Pet rover = (Pet)
person[“rover”];\n”+

“return rover.age;", pctx );

MVEL.compileExpression(

"rover = person[“rover”];\n”+“return rover.age;",
pctx

);

An interesting
feature that MVEL provides is the ability to analyse an expression and tell you the external
inputs, i.e. variables not assigned locally. It can also be used to report on the inferred types
for local variables. When determining external inputs strong typing cannot be used (for obvious
reasons) and the inputs types will be resolved as java.lang.Object.

ParserContext pctx = ParserContext.create();

MVEL.compileExpression( "person.pets['rover']", pctx
);

// Map returns [“person” : Object.class]

Map<String, Class> inputs =
pctx.getInputs();

And to get the
type of the local variables:

ParserContext pctx = ParserContext.create()

.stronglyTyped()

.withInput( “person”, Person.class );

MVEL.compileExpression( "pet = person.pets['rover']", pctx
);

// Map returns [“pet” : Pet.class]

Map<String, Class> inputs =
pctx.getVariables();

Indexed Variable
Dictionaries

Using Maps for external variable dictionaries is
the normal way to embed scripting languages, but not optimal for performance. It consumes more
memory and every read or write is a Map put/get. Recent versions of MVEL have brought pre-computed
indexed variables, via the configuration property 'indexAllocation”, so that reading and writing
variables is as fast as accessing an array, and consumes no more memory than that needed for the
array matching the length of the variables. The indexing is also used for local variables, ensuring
high performance through out.

ParserContext pctx = new ParserContext( )

.stronglyTyped()

.withInput(“person”, Person.class)

.withInput(“otherPet”, Person.class)

String[] varNames = new String[] { “person”, “otherPet”
};

pctx.addIndexedInput(varNames);

pctx.setIndexAllocation(true);

String exp = “person.pets['rover'].age ==
otherPet.age”;

SharedVariableSpaceModel model =

VariableSpaceCompiler.compileShared(expr,
ctx);

ExecutableStatement stmt = MVEL.compileExpression( expr, pctx
);

// Notice the values order must

// match the indexedInput variable name order

Object[] values = new Object[] { person, patch };

MVEL.executeExpression(stmt, model.createFactory(values)
);

Other Features

Property Handlers

Allows registration of getter/setter handlers for
a given class type. Once registered all getter/setter invocation is delegated to this instance.

Pluggable Data Converters

Data converters can be registered to handle
to/from data conversions between any class type.

Function and Lambda Definitions

Projections and Folds

Macro's

Macro support in MVEL is a basic facility to allow
the replacement of a token with an expanded source output.

Interceptors

Provide the ability to place interceptors within a
compiled expression. This can be particularly useful for implementing change listeners or
firing external events based from within an expression.

Shell

Command line MVEL scripting shell

Templating

Forget Velocity or other templating systems. MVEL
provides comprehensive templating capabilities with great performance and no additional
dependencies, all contained within the same 700kb jar. The full MVEL expression language is
available inside of the templating tags, providing consistency between your templating and
scripting environments, as well as higher peformance and less memory usage.

Performance
Enhancements

Optional Bytecode generation

Current “spot” generation for accessors

Full optional bytecode generation planned to get “as fast as java” execution

Highly-optimized real-time interpreter

Write Optimisations

Escape analysis and Redundancy Optimisations

Summary

By now you should be thoroughly convinced that as a Java developer you cannot afford to leave
MVEL out of your toolbox, and that it fills a unique gap between complete programming environments
like Groovy and Scala and expression languages like OGNL and JUEL. So while the world debates the
various merits of Gavin King's Ceylon project, they miss JBoss' secret weapon being loaded into the
torpedo's for launching... MVEL :)

The type safety aspect, combined with property accessors, type inference and compact data
structures makes MVEL an ideal testing language for Java developers – we just need someone to
develop the tooling, which can support re-factoring, hint hint IDEA ;)

TechTarget provides technology professionals with the information they need to perform their jobs - from developing strategy, to making cost-effective purchase decisions and managing their organizations technology projects - with its network of technology-specific websites, events and online magazines.