Red Hat Training

A Red Hat training course is available for Red Hat Fuse

Chapter 30. The Simple Language

Abstract

The simple language is a language that was developed in Apache Camel specifically for the purpose of accessing and manipulating the various parts of an exchange object. The language is not quite as simple as when it was originally created and it now features a comprehensive set of logical operators and conjunctions.

30.1. Java DSL

Simple expressions in Java DSL

In the Java DSL, there are two styles for using the simple() command in a route. You can either pass the simple() command as an argument to a processor, as follows:

from("seda:order")
  .filter(simple("${in.header.foo}"))
  .to("mock:fooOrders");

Or you can call the simple() command as a sub-clause on the processor, for example:

from("seda:order")
  .filter()
  .simple("${in.header.foo}")
  .to("mock:fooOrders");

Embedding in a string

If you are embedding a simple expression inside a plain text string, you must use the placeholder syntax, ${Expression}. For example, to embed the in.header.name expression in a string:

simple("Hello ${in.header.name}, how are you?")

Customizing the start and end tokens

From Java, you can customize the start and end tokens ({ and }, by default) by calling the changeFunctionStartToken static method and the changeFunctionEndToken static method on the SimpleLanguage object.

For example, you can change the start and end tokens to [ and ] in Java, as follows:

// Java
import org.apache.camel.language.simple.SimpleLanguage;
...
SimpleLanguage.changeFunctionStartToken("[");
SimpleLanguage.changeFunctionEndToken("]");
Note

Customizing the start and end tokens affects all Apache Camel applications that share the same camel-core library on their classpath. For example, in an OSGi server this might affect many applications; whereas in a Web application (WAR file) it would affect only the Web application itself.

30.2. XML DSL

Simple expressions in XML DSL

In the XML DSL, you can use a simple expression by putting the expression inside a simple element. For example, to define a route that performs filtering based on the contents of the foo header:

<route id="simpleExample">
  <from uri="seda:orders"/>
  <filter>
    <simple>${in.header.foo}</simple>
    <to uri="mock:fooOrders"/>
  </filter>
</route>

Alternative placeholder syntax

Sometimes — for example, if you have enabled Spring property placeholders or OSGi blueprint property placeholders — you might find that the ${Expression} syntax clashes with another property placeholder syntax. In this case, you can disambiguate the placeholder using the alternative syntax, $simple{Expression}, for the simple expression. For example:

<simple>Hello $simple{in.header.name}, how are you?</simple>

Customizing the start and end tokens

From XML configuration, you can customize the start and end tokens ({ and }, by default) by overriding the SimpleLanguage instance. For example, to change the start and end tokens to [ and ], define a new SimpleLanguage bean in your XML configuration file, as follows:

<bean id="simple" class="org.apache.camel.language.simple.SimpleLanguage">
  <constructor-arg name="functionStartToken" value="["/>
  <constructor-arg name="functionEndToken" value="]"/>
</bean>
Note

Customizing the start and end tokens affects all Apache Camel applications that share the same camel-core library on their classpath. For example, in an OSGi server this might affect many applications; whereas in a Web application (WAR file) it would affect only the Web application itself.

Whitespace and auto-trim in XML DSL

By default, whitespace preceding and following a simple expression is automatically trimmed in XML DSL. So this expression with surrounding whitespace:

<transform>
  <simple>
    data=${body}
  </simple>
</transform>

is automatically trimmed, so that it is equivalent to this expression (no surrounding whitespace):

<transform>
  <simple>data=${body}</simple>
</transform>

If you want to include newlines before or after the expression, you can either explicitly add a newline character, as follows:

<transform>
  <simple>data=${body}\n</simple>
</transform>

or you can switch off auto-trimming, by setting the trim attribute to false, as follows:

<transform trim="false">
  <simple>data=${body}
</simple>
</transform>

30.3. Invoking an External Script

Overview

It is possible to execute Simple scripts that are stored in an external resource, as described here.

Syntax for script resource

Use the following syntax to access a Simple script that is stored as an external resource:

resource:Scheme:Location

Where the Scheme: can be either classpath:, file:, or http:.

For example, to read the mysimple.txt script from the classpath,

simple("resource:classpath:mysimple.txt")

30.4. Expressions

Overview

The simple language provides various elementary expressions that return different parts of a message exchange. For example, the expression, simple("${header.timeOfDay}"), would return the contents of a header called timeOfDay from the incoming message.

Note

Since Apache Camel 2.9, you must always use the placeholder syntax, ${Expression}, to return a variable value. It is never permissible to omit the enclosing tokens (${ and }).

Contents of a single variable

You can use the simple language to define string expressions, based on the variables provided. For example, you can use a variable of the form, in.header.HeaderName, to obtain the value of the HeaderName header, as follows:

simple("${in.header.foo}")

Variables embedded in a string

You can embed simple variables in a string expression — for example:

simple("Received a message from ${in.header.user} on ${date:in.header.date:yyyyMMdd}.")

date and bean variables

As well as providing variables that access all of the different parts of an exchange (see Table 30.1, “Variables for the Simple Language”), the simple language also provides special variables for formatting dates, date:command:pattern, and for calling bean methods, bean:beanRef. For example, you can use the date and the bean variables as follows:

simple("Todays date is ${date:now:yyyyMMdd}")
simple("The order type is ${bean:orderService?method=getOrderType}")

Specifying the result type

You can specify the result type of an expression explicitly. This is mainly useful for converting the result type to a boolean or numerical type.

In the Java DSL, specify the result type as an extra argument to simple(). For example, to return an integer result, you could evaluate a simple expression as follows:

...
.setHeader("five", simple("5", Integer.class))

In the XML DSL, specify the result type using the resultType attribute. For example:

<setHeader headerName="five">
  <!-- use resultType to indicate that the type should be a java.lang.Integer -->
  <simple resultType="java.lang.Integer">5</simple>
</setHeader>

Dynamic Header Key

From Camel 2.17, the setHeaderand setExchange properties allows to use a dynamic header key using the Simple language, if the name of the key is a Simple language expression.

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
      <setHeader headerName="$simple{type:org.apache.camel.spring.processor.SpringSetPropertyNameDynamicTest$TestConstans.EXCHANGE_PROP_TX_FAILED}">
        <simple>${type:java.lang.Boolean.TRUE}</simple>
      </setHeader>
    <to uri="mock:end"/>
  </route>
</camelContext>

Nested expressions

Simple expressions can be nested — for example:

simple("${header.${bean:headerChooser?method=whichHeader}}")

Accessing constants or enums

You can access a bean’s constant or enum fields using the following syntax:

type:ClassName.Field

For example, consider the following Java enum type:

package org.apache.camel.processor;
...
public enum Customer {
    GOLD, SILVER, BRONZE
}

You can access the Customer enum fields, as follows:

 from("direct:start")
    .choice()
        .when().simple("${header.customer} ==
          ${type:org.apache.camel.processor.Customer.GOLD}")
            .to("mock:gold")
        .when().simple("${header.customer} ==
          ${type:org.apache.camel.processor.Customer.SILVER}")
            .to("mock:silver")
        .otherwise()
            .to("mock:other");

OGNL expressions

The Object Graph Navigation Language (OGNL) is a notation for invoking bean methods in a chain-like fashion. If a message body contains a Java bean, you can easily access its bean properties using OGNL notation. For example, if the message body is a Java object with a getAddress() accessor, you can access the Address object and the Address object’s properties as follows:

simple("${body.address}")
simple("${body.address.street}")
simple("${body.address.zip}")
simple("${body.address.city}")

Where the notation, ${body.address.street}, is shorthand for ${body.getAddress.getStreet}.

OGNL null-safe operator

You can use the null-safe operator, ?., to avoid encountering null-pointer exceptions, in case the body does not have an address. For example:

simple("${body?.address?.street}")

If the body is a java.util.Map type, you can look up a value in the map with the key, foo, using the following notation:

simple("${body[foo]?.name}")

OGNL list element access

You can also use square brackets notation, [k], to access the elements of a list. For example:

simple("${body.address.lines[0]}")
simple("${body.address.lines[1]}")
simple("${body.address.lines[2]}")

The last keyword returns the index of the last element of a list. For example, you can access the second last element of a list, as follows:

simple("${body.address.lines[last-1]}")

You can use the size method to query the size of a list, as follows:

simple("${body.address.lines.size}")

OGNL array length access

You can access the length of a Java array through the length method, as follows:

String[] lines = new String[]{"foo", "bar", "cat"};
exchange.getIn().setBody(lines);

simple("There are ${body.length} lines")

30.5. Predicates

Overview

You can construct predicates by testing expressions for equality. For example, the predicate, simple("${header.timeOfDay} == '14:30'"), tests whether the timeOfDay header in the incoming message is equal to 14:30.

In addition, whenever the resultType is specified as a Boolean the expression is evaluated as a predicate instead of an expression. This allows the predicate syntax to be used for these expressions.

Syntax

You can also test various parts of an exchange (headers, message body, and so on) using simple predicates. Simple predicates have the following general syntax:

${LHSVariable} Op RHSValue

Where the variable on the left hand side, LHSVariable, is one of the variables shown in Table 30.1, “Variables for the Simple Language” and the value on the right hand side, RHSValue, is one of the following:

  • Another variable, ${RHSVariable}.
  • A string literal, enclosed in single quotes, ' '.
  • A numeric constant, enclosed in single quotes, ' '.
  • The null object, null.

The simple language always attempts to convert the RHS value to the type of the LHS value.

Note

While the simple language will attempt to convert the RHS, depending on the operator the LHS may need to be cast into the appropriate Type before the comparison is made.

Examples

For example, you can perform simple string comparisons and numerical comparisons as follows:

simple("${in.header.user} == 'john'")

simple("${in.header.number} > '100'")  // String literal can be converted to integer

You can test whether the left hand side is a member of a comma-separated list, as follows:

simple("${in.header.type} in 'gold,silver'")

You can test whether the left hand side matches a regular expression, as follows:

simple("${in.header.number} regex '\d{4}'")

You can test the type of the left hand side using the is operator, as follows:

simple("${in.header.type} is 'java.lang.String'")
simple("${in.header.type} is 'String'") // You can abbreviate java.lang. types

You can test whether the left hand side lies in a specified numerical range (where the range is inclusive), as follows:

simple("${in.header.number} range '100..199'")

Conjunctions

You can also combine predicates using the logical conjunctions, && and ||.

For example, here is an expression using the && conjunction (logical and):

simple("${in.header.title} contains 'Camel' && ${in.header.type} == 'gold'")

And here is an expression using the || conjunction (logical inclusive or):

simple("${in.header.title} contains 'Camel' || ${in.header.type} == 'gold'")

30.6. Variable Reference

Table of variables

Table 30.1, “Variables for the Simple Language” shows all of the variables supported by the simple language.

Table 30.1. Variables for the Simple Language

VariableTypeDescription

camelContext

Object

The Camel context. Supports OGNL expressions.

camelId

String

The Camel context’s ID value.

exchangeId

String

The exchange’s ID value.

id

String

The In message ID value.

body

Object

The In message body. Supports OGNL expressions.

in.body

Object

The In message body. Supports OGNL expressions.

out.body

Object

The Out message body.

bodyAs(Type)

Type

The In message body, converted to the specified type. All types, Type, must be specified using their fully-qualified Java name, except for the types: byte[], String, Integer, and Long. The converted body can be null.

mandatoryBodyAs(Type)

Type

The In message body, converted to the specified type. All types, Type, must be specified using their fully-qualified Java name, except for the types: byte[], String, Integer, and Long. The converted body is expected to be non-null.

header.HeaderName

Object

The In message’s HeaderName header. Supports OGNL expressions.

header[HeaderName]

Object

The In message’s HeaderName header (alternative syntax).

headers.HeaderName

Object

The In message’s HeaderName header.

headers[HeaderName]

Object

The In message’s HeaderName header (alternative syntax).

in.header.HeaderName

Object

The In message’s HeaderName header. Supports OGNL expressions.

in.header[HeaderName]

Object

The In message’s HeaderName header (alternative syntax).

in.headers.HeaderName

Object

The In message’s HeaderName header. Supports OGNL expressions.

in.headers[HeaderName]

Object

The In message’s HeaderName header (alternative syntax).

out.header.HeaderName

Object

The Out message’s HeaderName header.

out.header[HeaderName]

Object

The Out message’s HeaderName header (alternative syntax).

out.headers.HeaderName

Object

The Out message’s HeaderName header.

out.headers[HeaderName]

Object

The Out message’s HeaderName header (alternative syntax).

headerAs(Key,Type)

Type

The Key header, converted to the specified type. All types, Type, must be specified using their fully-qualified Java name, except for the types: byte[], String, Integer, and Long. The converted value can be null.

headers

Map

All of the In headers (as a java.util.Map type).

in.headers

Map

All of the In headers (as a java.util.Map type).

property.PropertyName

Object

The PropertyName property on the exchange.

property[PropertyName]

Object

The PropertyName property on the exchange (alternative syntax).

sys.SysPropertyName

String

The SysPropertyName Java system property.

sysenv.SysEnvVar

String

The SysEnvVar system environment variable.

exception

String

Either the exception object from Exchange.getException() or, if this value is null, the caught exception from the Exchange.EXCEPTION_CAUGHT property; otherwise null. Supports OGNL expressions.

exception.message

String

If an exception is set on the exchange, returns the value of Exception.getMessage(); otherwise, returns null.

exception.stacktrace

String

If an exception is set on the exchange, returns the value of Exception.getStackTrace(); otherwise, returns null. Note: The simple language first tries to retrieve an exception from Exchange.getException(). If that property is not set, it checks for a caught exception, by calling Exchange.getProperty(Exchange.CAUGHT_EXCEPTION).

date:command:pattern

String

A date formatted using a java.text.SimpleDateFormat pattern. The following commands are supported: now, for the current date and time; header.HeaderName, or in.header.HeaderName to use a java.util.Date object in the HeaderName header from the In message; out.header.HeaderName to use a java.util.Date object in the HeaderName header from the Out message;

bean:beanID.Method

Object

Invokes a method on the referenced bean and returns the result of the method invocation. To specify a method name, you can either use the beanID.Method syntax; or you can use the beanID?method=methodName syntax.

ref:beanID

Object

Looks up the bean with the ID, beanID, in the registry and returns a reference to the bean itself. For example, if you are using the splitter EIP, you could use this variable to reference the bean that implements the splitting algorithm.

properties:Key

String

The value of the Key property placeholder .

properties:Location:Key

String

The value of the Key property placeholder, where the location of the properties file is given by Location .

threadName

String

The name of the current thread.

routeId

String

Returns the ID of the current route through which the Exchange is being routed.

type:Name[.Field]

Object

References a type or field by its Fully-Qualified-Name (FQN). To refer to a field, append .Field. For example, you can refer to the FILE_NAME constant field from the Exchange class as type:org.apache.camel.Exchange.FILE_NAME

collate(group)

List

From Camel 2.17, the collate function iterates the message body and groups the data into the sub lists of specific size. You can use with the Splitter EIP to split a message body and group or batch the submessages into a group of N sublists.

skip(number)

Iterator

The skip function iterates the message body and skips the first number of items. This can be used with the Splitter EIP to split a message body and skip the first N number of items.

30.7. Operator Reference

Binary operators

The binary operators for simple language predicates are shown in Table 30.2, “Binary Operators for the Simple Language”.

Table 30.2. Binary Operators for the Simple Language

OperatorDescription

==

Equals.

=~

Equals ignore case. Ignore the case when comparing string values.

>

Greater than.

>=

Greater than or equals.

<

Less than.

Less than or equals.

!=

Not equal to.

contains

Test if LHS string contains RHS string.

not contains

Test if LHS string does not contain RHS string.

regex

Test if LHS string matches RHS regular expression.

not regex

Test if LHS string does not match RHS regular expression.

in

Test if LHS string appears in the RHS comma-separated list.

not in

Test if LHS string does not appear in the RHS comma-separated list.

is

Test if LHS is an instance of RHS Java type (using Java instanceof operator).

not is

Test if LHS is not an instance of RHS Java type (using Java instanceof operator).

range

Test if LHS number lies in the RHS range (where range has the format, 'min…​max').

not range

Test if LHS number does not lie in the RHS range (where range has the format, 'min…​max').

starts with

New in Camel 2.18. Test if the LHS string starts with the RHS string.

ends with

New in Camel 2.18. Test if the LHS string ends with the RHS string.

Unary operators and character escapes

The binary operators for simple language predicates are shown in Table 30.3, “Unary Operators for the Simple Language”.

Table 30.3. Unary Operators for the Simple Language

OperatorDescription

++

Increment a number by 1.

--

Decrement a number by 1.

\n

The newline character.

\r

The carriage return character.

\t

The tab character.

\

(Obsolete) Since Camel version 2.11, the backslash escape character is not supported.

Combining predicates

The conjunctions shown in Table 30.4, “Conjunctions for Simple Language Predicates” can be used to combine two or more simple language predicates.

Table 30.4. Conjunctions for Simple Language Predicates

OperatorDescription

&&

Combine two predicates with logical and.

||

Combine two predicates with logical inclusive or.

and

Deprecated. Use && instead.

or

Deprecated. Use || instead.