Repository: camel Updated Branches: refs/heads/master 96d784229 -> 69d57640e
Allow OGNL after square bracket accessed headers and properties Allows to evaluate an OGNL expression after property extraction with square brackets. The following expressions are now allowed headers[foo].OGNL in.headers[foo].OGNL exchangeProperty[foo].OGNL Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/6b2f245e Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/6b2f245e Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/6b2f245e Branch: refs/heads/master Commit: 6b2f245edc0c6f0ef1571ba8d37fe972c369b2c1 Parents: 96d7842 Author: Marco Collovati <mcollov...@gmail.com> Authored: Sat Jan 28 23:17:27 2017 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Tue Jan 31 10:00:45 2017 +0100 ---------------------------------------------------------------------- .../apache/camel/builder/ExpressionBuilder.java | 55 +++++++------ .../camel/language/simple/SimpleTest.java | 86 +++++++++++++------- 2 files changed, 89 insertions(+), 52 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/6b2f245e/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java b/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java index 472c582..7c449aa 100644 --- a/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java +++ b/camel-core/src/main/java/org/apache/camel/builder/ExpressionBuilder.java @@ -35,7 +35,6 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.apache.camel.CamelContext; import org.apache.camel.Component; import org.apache.camel.Endpoint; @@ -77,7 +76,7 @@ import org.apache.camel.util.StringHelper; /** * A helper class for working with <a href="http://camel.apache.org/expression.html">expressions</a>. * - * @version + * @version */ public final class ExpressionBuilder { @@ -88,7 +87,7 @@ public final class ExpressionBuilder { */ private ExpressionBuilder() { } - + /** * Returns an expression for the inbound message attachments * @@ -359,8 +358,8 @@ public final class ExpressionBuilder { return "exchangePattern"; } }; - } - + } + /** * Returns an expression for an exception set on the exchange * @@ -410,7 +409,7 @@ public final class ExpressionBuilder { } }; } - + /** * Returns the expression for the exchanges exception invoking methods defined * in a simple OGNL notation @@ -424,7 +423,7 @@ public final class ExpressionBuilder { if (exception == null) { exception = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); } - + if (exception == null) { return null; } @@ -606,7 +605,7 @@ public final class ExpressionBuilder { } }; } - + /** * Returns an expression for the property value of exchange with the given name * @@ -653,7 +652,7 @@ public final class ExpressionBuilder { public static Expression propertiesExpression() { return exchangeExceptionExpression(); } - + /** * Returns an expression for the exchange properties of exchange * @@ -689,7 +688,7 @@ public final class ExpressionBuilder { } }; } - + /** * Returns an expression for the property value of the camel context with the given name * @@ -962,7 +961,7 @@ public final class ExpressionBuilder { @Override public String toString() { - return "bodyExpression (" + bodyType + ")"; + return "bodyExpression (" + bodyType + ")"; } }; } @@ -981,7 +980,7 @@ public final class ExpressionBuilder { @Override public String toString() { - return "bodyExpression (" + bodyType + ")"; + return "bodyExpression (" + bodyType + ")"; } }; } @@ -1613,7 +1612,7 @@ public final class ExpressionBuilder { ObjectHelper.notEmpty(path, "path"); return new XMLTokenExpressionIterator(path, mode); } - + public static Expression tokenizeXMLAwareExpression(String path, char mode, int group) { ObjectHelper.notEmpty(path, "path"); return new XMLTokenExpressionIterator(path, mode, group); @@ -1994,7 +1993,7 @@ public final class ExpressionBuilder { } }; } - + public static Expression beanExpression(final String expression) { return new ExpressionAdapter() { public Object evaluate(Exchange exchange) { @@ -2013,13 +2012,13 @@ public final class ExpressionBuilder { } }; } - + public static Expression beanExpression(final Class<?> beanType, final String methodName) { return BeanLanguage.bean(beanType, methodName); } public static Expression beanExpression(final Object bean, final String methodName) { - return BeanLanguage.bean(bean, methodName); + return BeanLanguage.bean(bean, methodName); } public static Expression beanExpression(final String beanRef, final String methodName) { @@ -2281,7 +2280,7 @@ public final class ExpressionBuilder { // getComponent will create a new component if none already exists Component component = exchange.getContext().getComponent("properties"); PropertiesComponent pc = exchange.getContext().getTypeConverter() - .mandatoryConvertTo(PropertiesComponent.class, component); + .mandatoryConvertTo(PropertiesComponent.class, component); // enclose key with {{ }} to force parsing String[] paths = text2.split(","); return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken(), paths); @@ -2290,10 +2289,10 @@ public final class ExpressionBuilder { Component component = exchange.getContext().hasComponent("properties"); if (component == null) { throw new IllegalArgumentException("PropertiesComponent with name properties must be defined" - + " in CamelContext to support property placeholders in expressions"); + + " in CamelContext to support property placeholders in expressions"); } PropertiesComponent pc = exchange.getContext().getTypeConverter() - .mandatoryConvertTo(PropertiesComponent.class, component); + .mandatoryConvertTo(PropertiesComponent.class, component); // enclose key with {{ }} to force parsing return pc.parseUri(pc.getPrefixToken() + text + pc.getSuffixToken()); } @@ -2452,7 +2451,7 @@ public final class ExpressionBuilder { private final String toStringValue; private final KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy; - KeyedOgnlExpressionAdapter(String ognl, String toStringValue, + KeyedOgnlExpressionAdapter(String ognl, String toStringValue, KeyedEntityRetrievalStrategy keyedEntityRetrievalStrategy) { this.ognl = ognl; this.toStringValue = toStringValue; @@ -2466,19 +2465,29 @@ public final class ExpressionBuilder { return property; } + // Split ognl except when this is not a Map, Array // and we would like to keep the dots within the key name List<String> methods = OgnlHelper.splitOgnl(ognl); + String key = methods.get(0); + String keySuffix = ""; + // if ognl starts with a key inside brackets (eg: [foo.bar]) + // remove starting and ending brackets from key + if (key.startsWith("[") && key.endsWith("]")) { + key = StringHelper.removeLeadingAndEndingQuotes(key.substring(1, key.length() - 1)); + keySuffix = StringHelper.after(methods.get(0), key); + } // remove any OGNL operators so we got the pure key name - String key = OgnlHelper.removeOperators(methods.get(0)); + key = OgnlHelper.removeOperators(key); + property = keyedEntityRetrievalStrategy.getKeyedEntity(exchange, key); if (property == null) { return null; } // the remainder is the rest of the ognl without the key - String remainder = ObjectHelper.after(ognl, key); + String remainder = ObjectHelper.after(ognl, key + keySuffix); return new MethodCallExpression(property, remainder).evaluate(exchange); } @@ -2493,6 +2502,6 @@ public final class ExpressionBuilder { public interface KeyedEntityRetrievalStrategy { Object getKeyedEntity(Exchange exchange, String key); } - }; + } } http://git-wip-us.apache.org/repos/asf/camel/blob/6b2f245e/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java b/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java index 2bf3e3a..6e13555 100644 --- a/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java +++ b/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java @@ -25,7 +25,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; - import org.apache.camel.CamelAuthorizationException; import org.apache.camel.CamelExecutionException; import org.apache.camel.Exchange; @@ -42,7 +41,7 @@ import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException; import org.apache.camel.spi.Language; /** - * @version + * @version */ public class SimpleTest extends LanguageTestSupport { @@ -89,7 +88,7 @@ public class SimpleTest extends LanguageTestSupport { public void testRefExpression() throws Exception { assertExpressionResultInstanceOf("ref:myAnimal", Animal.class); assertExpressionResultInstanceOf("${ref:myAnimal}", Animal.class); - + assertExpression("ref:myAnimal", "Donkey"); assertExpression("${ref:myAnimal}", "Donkey"); assertExpression("ref:unknown", null); @@ -165,14 +164,14 @@ public class SimpleTest extends LanguageTestSupport { assertIsInstanceOf(Integer.class, val); assertEquals(123, val); } - + public void testBodyExpressionWithArray() throws Exception { exchange.getIn().setBody(new MyClass()); Expression exp = SimpleLanguage.simple("body.myArray"); assertNotNull(exp); Object val = exp.evaluate(exchange, Object.class); assertIsInstanceOf(Object[].class, val); - + exp = SimpleLanguage.simple("body.myArray.length"); assertNotNull(exp); val = exp.evaluate(exchange, Object.class); @@ -193,7 +192,7 @@ public class SimpleTest extends LanguageTestSupport { exchange.setFromRouteId("myRouteId"); assertExpression("routeId", "myRouteId"); } - + public void testTrimSimpleExpressions() throws Exception { assertExpression(" \texchangeId\n".trim(), exchange.getExchangeId()); assertExpression("\nid\r".trim(), exchange.getIn().getMessageId()); @@ -257,22 +256,22 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${in.body[0][code]}", 4321); assertExpression("${body[0][code]}", 4321); } - + public void testOGNLBodyEmptyList() throws Exception { Map<String, List<String>> map = new HashMap<String, List<String>>(); map.put("list", new ArrayList<String>()); - + exchange.getIn().setBody(map); assertExpression("${in.body?.get('list')[0].toString}", null); } - + public void testOGNLBodyExpression() throws Exception { exchange.getIn().setBody("hello world"); assertPredicate("${body} == 'hello world'", true); assertPredicate("${body.toUpperCase()} == 'HELLO WORLD'", true); } - + public void testOGNLBodyAsExpression() throws Exception { byte[] body = "hello world".getBytes(); exchange.getIn().setBody(body); @@ -318,7 +317,7 @@ public class SimpleTest extends LanguageTestSupport { map.put("cool", "Camel rocks"); map.put("dude", "Hey dude"); exchange.getIn().setHeaders(map); - + assertExpression("${headers.cool.replaceAll(\"rocks\", \"is so cool\")}", "Camel is so cool"); } @@ -359,7 +358,7 @@ public class SimpleTest extends LanguageTestSupport { } assertExpression("${exchangeProperty.unknown[cool]}", null); } - + public void testOGNLPropertyLinesList() throws Exception { List<OrderLine> lines = new ArrayList<OrderLine>(); lines.add(new OrderLine(123, "Camel in Action")); @@ -377,7 +376,7 @@ public class SimpleTest extends LanguageTestSupport { } assertExpression("${property.unknown[cool]}", null); } - + public void testOGNLPropertyMap() throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("cool", "Camel rocks"); @@ -393,7 +392,7 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${property?.unknown[cool]}", null); assertExpression("${property.unknown[cool]}", null); } - + public void testOGNLExchangePropertyMap() throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("cool", "Camel rocks"); @@ -417,7 +416,7 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${exchangeProperty.wicket[this.code]}", "This code"); } - + public void testOGNLPropertyMapNotMap() throws Exception { try { assertExpression("${exchangeProperty.foobar[bar]}", null); @@ -427,7 +426,7 @@ public class SimpleTest extends LanguageTestSupport { assertEquals("Key: bar not found in bean: cba of type: java.lang.String using OGNL path [[bar]]", cause.getMessage()); } } - + public void testOGNLPropertyMapIllegalSyntax() throws Exception { try { assertExpression("${property.foobar[bar}", null); @@ -580,7 +579,7 @@ public class SimpleTest extends LanguageTestSupport { public void testBodyAs() throws Exception { assertExpression("${bodyAs(String)}", "<hello id='m123'>world!</hello>"); assertExpression("${bodyAs('String')}", "<hello id='m123'>world!</hello>"); - + exchange.getIn().setBody(null); assertExpression("${bodyAs('String')}", null); @@ -642,6 +641,35 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${in.headers[foo]}", "abc"); } + public void testOnglOnHeadersWithBracket() throws Exception { + assertOnglOnHeadersWithSquareBrackets("order"); + assertOnglOnHeadersWithSquareBrackets("purchase.order"); + assertOnglOnHeadersWithSquareBrackets("foo.bar.qux"); + assertOnglOnHeadersWithSquareBrackets("purchase order"); + } + + private void assertOnglOnHeadersWithSquareBrackets(String key) { + exchange.getIn().setHeader(key, new OrderLine(123, "Camel in Action")); + assertExpression("headers[" + key + "].name", "Camel in Action"); + assertExpression("${headers[" + key + "].name}", "Camel in Action"); + assertExpression("${in.headers[" + key + "].name}", "Camel in Action"); + assertExpression("${in.headers['" + key + "'].name}", "Camel in Action"); + } + + public void testOnglOnExchangePropertiesWithBracket() throws Exception { + assertOnglOnExchangePropertiesWithBracket("order"); + assertOnglOnExchangePropertiesWithBracket("purchase.order"); + assertOnglOnExchangePropertiesWithBracket("foo.bar.qux"); + assertOnglOnExchangePropertiesWithBracket("purchase order"); + } + + public void assertOnglOnExchangePropertiesWithBracket(String key) throws Exception { + exchange.setProperty(key, new OrderLine(123, "Camel in Action")); + assertExpression("exchangeProperty[" + key + "].name", "Camel in Action"); + assertExpression("${exchangeProperty[" + key + "].name}", "Camel in Action"); + assertExpression("${exchangeProperty['" + key + "'].name}", "Camel in Action"); + } + public void testIsInstanceOfEmptyBody() throws Exception { // set an empty body exchange.getIn().setBody(null); @@ -692,7 +720,7 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${headerAs('bar','Integer')}", 123); assertExpression("${headerAs('bar',\"int\")}", 123); assertExpression("${headerAs(bar,String)}", "123"); - + assertExpression("${headerAs(unknown,String)}", null); try { @@ -701,7 +729,7 @@ public class SimpleTest extends LanguageTestSupport { } catch (ExpressionIllegalSyntaxException e) { assertTrue(e.getMessage().startsWith("Valid syntax: ${headerAs(key, type)} was: headerAs(unknown String)")); } - + try { assertExpression("${headerAs(fool,String).test}", null); fail("Should have thrown an exception"); @@ -836,7 +864,7 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${in.body[foo.bar]}", "Camel"); } - + public void testBodyOGNLAsMapShorthand() throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("foo", "Camel"); @@ -939,7 +967,7 @@ public class SimpleTest extends LanguageTestSupport { lines.add(new OrderLine(123, "Camel in Action")); lines.add(new OrderLine(456, "ActiveMQ in Action")); Order order = new Order(lines); - + exchange.getIn().setBody(order); assertExpression("${in.body.getLines[0].getId}", 123); @@ -1198,7 +1226,7 @@ public class SimpleTest extends LanguageTestSupport { fail("Should have thrown exception"); } catch (RuntimeBeanExpressionException e) { assertEquals("Failed to invoke method: .getFriend.getFriend.getName on org.apache.camel.language.simple.SimpleTest.Animal" - + " due last method returned null and therefore cannot continue to invoke method .getName on a null instance", e.getMessage()); + + " due last method returned null and therefore cannot continue to invoke method .getName on a null instance", e.getMessage()); } } @@ -1224,7 +1252,7 @@ public class SimpleTest extends LanguageTestSupport { fail("Should have thrown exception"); } catch (RuntimeBeanExpressionException e) { assertEquals("Failed to invoke method: .friend.friend.name on org.apache.camel.language.simple.SimpleTest.Animal" - + " due last method returned null and therefore cannot continue to invoke method .name on a null instance", e.getMessage()); + + " due last method returned null and therefore cannot continue to invoke method .name on a null instance", e.getMessage()); } } @@ -1263,7 +1291,7 @@ public class SimpleTest extends LanguageTestSupport { public void testBodyOGNLBoolean() throws Exception { Animal tiger = new Animal("Tony the Tiger", 13); exchange.getIn().setBody(tiger); - + assertExpression("${body.isDangerous}", "true"); assertExpression("${body.dangerous}", "true"); @@ -1370,7 +1398,7 @@ public class SimpleTest extends LanguageTestSupport { assertExpression("${body.getClass.getSimpleName}", "Animal"); assertExpression("${body.class.simpleName}", "Animal"); } - + public void testExceptionClassSimpleName() throws Exception { Animal tiger = new Animal("Tony the Tiger", 13); exchange.getIn().setBody(tiger); @@ -1538,7 +1566,7 @@ public class SimpleTest extends LanguageTestSupport { assertEquals("E", chunk2.get(1)); assertEquals("F", chunk2.get(2)); } - + public void testCollateOdd() throws Exception { List<Object> data = new ArrayList<Object>(); data.add("A"); @@ -1584,10 +1612,10 @@ public class SimpleTest extends LanguageTestSupport { } Expression expression = SimpleLanguage.simple("${random(1, 10)}", Integer.class); assertTrue(min <= expression.evaluate(exchange, Integer.class) && expression.evaluate(exchange, Integer.class) < max); - + Expression expression1 = SimpleLanguage.simple("${random( 10)}", Integer.class); assertTrue(0 <= expression1.evaluate(exchange, Integer.class) && expression1.evaluate(exchange, Integer.class) < max); - + try { assertExpression("${random(10,21,30)}", null); fail("Should have thrown exception"); @@ -1745,7 +1773,7 @@ public class SimpleTest extends LanguageTestSupport { return name; } } - + public static class MyClass { public Object[] getMyArray() { return new Object[]{"Hallo", "World", "!"};