2011-09-25

Camel 2.9 - Much Improved Simple Language

I\m sitting outside and enjoying the last sun and summer we have here in Scandinavia. So I might as well take time to write this blog entry which has been on my todo list for a while.

Camel has a built-in expression language called Simple. It has been around for a very long time. It started out as a "templatish" (String interpolation) language, which allows you to define dynamic strings, using token placeholders.

For example you could set a mail subject header as follows:
.setHeader("subject", "Your order from ${header.company}")

 In Camel 2.0 we added support for operators in the Simple language. This was a good addition, as it allows end users to use Simple languages for predicates. In other words you have a built-in language in camel-core, which you can use in the Camel routes with EIPs that uses predicates.

For example you could route messages depending on the content using the Content Based Router:
.choice()
  .when().simple("${header.priority} > 10")
    .to("activemq:queue:high")
  .when().simple("${header.priority} > 5")
    .to("activemq:queue:med")
   .otherwise()
     .to("activemq:queue:low")
  .end()

As the roots of the Simple language was based on its templatish behavior, there parser was implemented using regular expressions. However as people may know, using regular expressions comes with a cost of complexity and being able to read and understand the fairly long patterns. So we have probably reached how far we could go with the Simple language.

Another problem with using regular expression is that, as an end user, you must enter the input correctly. If you had some typos in the input, then the regular expressions was not always capable of detecting those errors, and being able to report this as an error. Instead the predicate would fallback and pass as true. This leads to confusion and frustration by the end users, as they do not understand, why all their messages goes to the first predicate in their content based router etc.

Knowing this, I took the time to work on a new and improved Simple language. To avoid disturbing the existing source code in the trunk. I setup a new project at github, and asked people in the community to participate. Taariq responded and we worked on this project. A reason for using a repository outside the Camel trunk, was that this was an experiment, and we did not knew when we set sail, if this would become useable and better.

The new improved Simple language is based on the principles of a recursive descent parser, with a grammar, syntax parser, and ast (abstract source tree). We set a goal to not depend on any 3rd party frameworks such as ANTLR or the likes. The Simple language is simple, and do not need to overhead and complexity of ANTRL or Eclipse XText. There is already sufficient 3rd party languages you can use instead such as JavaScript, Groovy, Mvel etc. However maybe in the future we may develop a new Camel language outside camel-core, that uses ANTRL/XText if the community is looking for this kind, and its feasible to implement.

With the new syntax parser we are capable of detecting all syntax errors, and give a precise error report what is the problem. For example if you forgot to use == in the equals operator, and only have =, then you get a error as follows:

org.apache.camel.language.simple.SimpleIllegalSyntaxException:
unexpected character symbol at location 15
${header.high} = true
               ^
And there is a ^ pointer, which points you to the location of the problem.

This re-architecture of the Simple language, also allows us to add new operators and functions to the language without the implications of the old regular expression approach. The community was asking for being able to increment a counter using the Simple language purely from the XML DSLs. So this is now possible as follows:

<setHeader header="myCounter">
  <simple>${header.myCounter}++</simple>
</setHeader>

We have support for unary operators specified on the postfix side of the operator. So we could potential add other operators, if it makes sense.

I have also experimented with ternary operators, so you can do the conditional operator such as:
<setHeader header="bigSpender">
  <simple>${header.amount} > 1000 ? true : false</simple>
</setHeader>

Likewise we may add support for the elvis operator (?:) as a shorthand for the conditional operator:
<setHeader header="name">
  <simple>${header.username} ?: "Anonymous"</simple>
</setHeader>

Anyway what I wanted to say with this blog entry, is that the Simple language is improved in the upcoming Camel 2.9 release, so it will give you better error details when you have syntax errors, and all your troubles with why the content based router is rouging all messages to the first predicate is solved as well.

7 comments:

jamie archibald said...

Excellent. the Simple language is awesome but one of its drawbacks I always had was understanding why your syntax wouldn't work. It's great to see some new mechanisms in place for helping developers!

Andrew Fink said...

Another one new expression language ;-(


Why not
http://mvel.codehaus.org/ (very fast)
or
http://static.springsource.org/spring/docs/3.0.7.RELEASE/spring-framework-reference/html/expressions.html (spring brand name)

Claus Ibsen said...

Andrew, we got mvel and spel already. See the languages page at Camel
http://camel.apache.org/languages

The Simple language is for end users who do not want to add new JARs and use the built-in simple language from camel-core, which in most use-cases is fine.

You can chose whatever you want to use.

AndyJT said...

Hi Claus, we have an issue where we may want to use simple to query the header using the following pseudo code:

if( (header.test == 'a' or header.test == 'B') && header.test2 = "a")

I understand that as of Camel 2.5 onwards there is support in simple for multiple operators, but does it also support operator precedence as shown by the brackets above, if so could you give an example or point me in the right direction to an alternative if this is not supported. (We are currently using 2.9.0)

Claus Ibsen said...

Andy.

Operator precedence is not yet supported.
I will log a JIRA so we can add it for the future.

You may work around using a regular expression for the first conditions.

header.test regexp '[ab]' && header.test2 == 'b'

Claus Ibsen said...

Andy, there is also alternative languages such as groovy, javascript etc. which is a real programming language, and they would support operator precedence.

See the languages page at the Camel website

AndyJT said...

Thanks Claus, thanks for the two suggestions, I hadn't considered the regex approach and had gone down the alternate language route, but I will have a think.