Chapter 30. The Simple Language
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: Or you can call the simple() command as a sub-clause on the processor, for example: Embedding in a string If you are embedding a simple expression inside a plain text string, you must use the placeholder syntax, USD{ Expression } . For example, to embed the in.header.name expression in a string: 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: 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: Alternative placeholder syntax Sometimes - for example, if you have enabled Spring property placeholders or OSGi blueprint property placeholders - you might find that the USD{ Expression } syntax clashes with another property placeholder syntax. In this case, you can disambiguate the placeholder using the alternative syntax, USDsimple{ Expression } , for the simple expression. For example: 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: 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: is automatically trimmed, so that it is equivalent to this expression (no surrounding whitespace): If you want to include newlines before or after the expression, you can either explicitly add a newline character, as follows: or you can switch off auto-trimming, by setting the trim attribute to false , as follows: 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: Where the Scheme : can be either classpath: , file: , or http: . For example, to read the mysimple.txt script from the classpath, 30.4. Expressions Overview The simple language provides various elementary expressions that return different parts of a message exchange. For example, the expression, simple("USD{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, USD{ Expression } , to return a variable value. It is never permissible to omit the enclosing tokens ( USD{ 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: Variables embedded in a string You can embed simple variables in a string expression - for example: 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: 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: In the XML DSL, specify the result type using the resultType attribute. For example: Dynamic Header Key From Camel 2.17, the setHeader and setExchange properties allows to use a dynamic header key using the Simple language, if the name of the key is a Simple language expression. Nested expressions Simple expressions can be nested - for example: Accessing constants or enums You can access a bean's constant or enum fields using the following syntax: For example, consider the following Java enum type: You can access the Customer enum fields, as follows: 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: Where the notation, USD{body.address.street} , is shorthand for USD{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: 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: OGNL list element access You can also use square brackets notation, [k] , to access the elements of a list. For example: 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: You can use the size method to query the size of a list, as follows: OGNL array length access You can access the length of a Java array through the length method, as follows: 30.5. Predicates Overview You can construct predicates by testing expressions for equality. For example, the predicate, simple("USD{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: 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, USD{ 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: You can test whether the left hand side is a member of a comma-separated list, as follows: You can test whether the left hand side matches a regular expression, as follows: You can test the type of the left hand side using the is operator, as follows: You can test whether the left hand side lies in a specified numerical range (where the range is inclusive), as follows: Conjunctions You can also combine predicates using the logical conjunctions, && and || . For example, here is an expression using the && conjunction (logical and): And here is an expression using the || conjunction (logical inclusive or): 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 Variable Type Description 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). exchangeProperty. PropertyName Object The PropertyName property on the exchange. exchangeProperty[ PropertyName ] Object The PropertyName property on the exchange (alternative syntax). exchangeProperty. PropertyName .OGNL Object The PropertyName property on the exchange and invoke its value using a Camel OGNL expression. 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 Operator Description == 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 Operator Description ++ 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 Operator Description && Combine two predicates with logical and . || Combine two predicates with logical inclusive or . and Deprecated . Use && instead. or Deprecated . Use || instead.
from("seda:order") .filter(simple("USD{in.header.foo}")) .to("mock:fooOrders"); from("seda:order") .filter() .simple("USD{in.header.foo}") .to("mock:fooOrders"); simple("Hello USD{in.header.name}, how are you?") // Java import org.apache.camel.language.simple.SimpleLanguage; SimpleLanguage.changeFunctionStartToken("["); SimpleLanguage.changeFunctionEndToken("]"); <route id="simpleExample"> <from uri="seda:orders"/> <filter> <simple>USD{in.header.foo}</simple> <to uri="mock:fooOrders"/> </filter> </route> <simple>Hello USDsimple{in.header.name}, how are you?</simple> <bean id="simple" class="org.apache.camel.language.simple.SimpleLanguage"> <constructor-arg name="functionStartToken" value="["/> <constructor-arg name="functionEndToken" value="]"/> </bean> <transform> <simple> data=USD{body} </simple> </transform> <transform> <simple>data=USD{body}</simple> </transform> <transform> <simple>data=USD{body}\n</simple> </transform> <transform trim="false"> <simple>data=USD{body} </simple> </transform> resource: Scheme : Location simple("resource:classpath:mysimple.txt") simple("USD{in.header.foo}") simple("Received a message from USD{in.header.user} on USD{date:in.header.date:yyyyMMdd}.") simple("Todays date is USD{date:now:yyyyMMdd}") simple("The order type is USD{bean:orderService?method=getOrderType}") .setHeader("five", simple("5", Integer.class )) <setHeader headerName="five"> <!-- use resultType to indicate that the type should be a java.lang.Integer --> <simple resultType="java.lang.Integer" >5</simple> </setHeader> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <setHeader headerName="USDsimple{type:org.apache.camel.spring.processor.SpringSetPropertyNameDynamicTestUSDTestConstans.EXCHANGE_PROP_TX_FAILED}"> <simple>USD{type:java.lang.Boolean.TRUE}</simple> </setHeader> <to uri="mock:end"/> </route> </camelContext> simple("USD{header.USD{bean:headerChooser?method=whichHeader}}") type: ClassName . Field package org.apache.camel.processor; public enum Customer { GOLD, SILVER, BRONZE } from("direct:start") .choice() .when().simple("USD{header.customer} == USD{type:org.apache.camel.processor.Customer.GOLD}") .to("mock:gold") .when().simple("USD{header.customer} == USD{type:org.apache.camel.processor.Customer.SILVER}") .to("mock:silver") .otherwise() .to("mock:other"); simple("USD{body.address}") simple("USD{body.address.street}") simple("USD{body.address.zip}") simple("USD{body.address.city}") simple("USD{body?.address?.street}") simple("USD{body[foo]?.name}") simple("USD{body.address.lines[0]}") simple("USD{body.address.lines[1]}") simple("USD{body.address.lines[2]}") simple("USD{body.address.lines[last-1]}") simple("USD{body.address.lines.size}") String[] lines = new String[]{"foo", "bar", "cat"}; exchange.getIn().setBody(lines); simple("There are USD{body.length} lines") USD{ LHSVariable } Op RHSValue simple("USD{in.header.user} == 'john'") simple("USD{in.header.number} > '100'") // String literal can be converted to integer simple("USD{in.header.type} in 'gold,silver'") simple("USD{in.header.number} regex '\d{4}'") simple("USD{in.header.type} is 'java.lang.String'") simple("USD{in.header.type} is 'String'") // You can abbreviate java.lang. types simple("USD{in.header.number} range '100..199'") simple("USD{in.header.title} contains 'Camel' && USD{in.header.type} == 'gold'") simple("USD{in.header.title} contains 'Camel' || USD{in.header.type} == 'gold'")