CAMEL-11377: Optimise - Bean expression invoking bean can use static method instead of creating new objects
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/32c1f654 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/32c1f654 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/32c1f654 Branch: refs/heads/master Commit: 32c1f6547a5259be168b5a532bcd21c3cbfc7d63 Parents: a05fde8 Author: Claus Ibsen <davscl...@apache.org> Authored: Thu Jun 1 16:29:52 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Thu Jun 1 16:29:52 2017 +0200 ---------------------------------------------------------------------- .../camel/language/bean/BeanExpression.java | 269 +++++++++---------- 1 file changed, 126 insertions(+), 143 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/32c1f654/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java b/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java index 9ff76ba..21255db 100644 --- a/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java +++ b/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java @@ -25,7 +25,6 @@ import org.apache.camel.ExchangePattern; import org.apache.camel.Expression; import org.apache.camel.ExpressionIllegalSyntaxException; import org.apache.camel.Predicate; -import org.apache.camel.Processor; import org.apache.camel.component.bean.BeanExpressionProcessor; import org.apache.camel.component.bean.BeanHolder; import org.apache.camel.component.bean.ConstantBeanHolder; @@ -110,10 +109,8 @@ public class BeanExpression implements Expression, Predicate { if (OgnlHelper.isValidOgnlExpression(method)) { // okay the method is an ognl expression - OgnlInvokeProcessor ognl = new OgnlInvokeProcessor(beanHolder, beanName, method); try { - ognl.process(exchange); - return ognl.getResult(); + return invokeOgnlMethod(beanHolder, beanName, method, exchange); } catch (Exception e) { if (e instanceof RuntimeBeanExpressionException) { throw (RuntimeBeanExpressionException) e; @@ -227,177 +224,163 @@ public class BeanExpression implements Expression, Predicate { * For more advanced OGNL you may have to look for a real framework such as OGNL, Mvel or dynamic * programming language such as Groovy, JuEL, JavaScript. */ - private static final class OgnlInvokeProcessor implements Processor { - - private final String ognl; - private final BeanHolder beanHolder; - private final String beanName; - private Object result; - - private OgnlInvokeProcessor(BeanHolder beanHolder, String beanName, String ognl) { - this.beanHolder = beanHolder; - this.beanName = beanName; - this.ognl = ognl; - // we must start with having bean as the result - this.result = beanHolder.getBean(); + private static Object invokeOgnlMethod(BeanHolder beanHolder, String beanName, String ognl, Exchange exchange) { + + // we must start with having bean as the result + Object result = beanHolder.getBean(); + + // copy the original exchange to avoid side effects on it + Exchange resultExchange = exchange.copy(); + // remove any existing exception in case we do OGNL on the exception + resultExchange.setException(null); + // force to use InOut to retrieve the result on the OUT message + resultExchange.setPattern(ExchangePattern.InOut); + // do not propagate any method name when using OGNL, as with OGNL we + // compute and provide the method name to explicit to invoke + resultExchange.getIn().removeHeader(Exchange.BEAN_METHOD_NAME); + + // current ognl path as we go along + String ognlPath = ""; + + // loop and invoke each method + Object beanToCall = beanHolder.getBean(); + Class<?> beanType = beanHolder.getBeanInfo().getType(); + + // there must be a bean to call with, we currently does not support OGNL expressions on using purely static methods + if (beanToCall == null && beanType == null) { + throw new IllegalArgumentException("Bean instance and bean type is null. OGNL bean expressions requires to have either a bean instance of the class name of the bean to use."); } - public void process(Exchange exchange) throws Exception { - // copy the original exchange to avoid side effects on it - Exchange resultExchange = exchange.copy(); - // remove any existing exception in case we do OGNL on the exception - resultExchange.setException(null); - // force to use InOut to retrieve the result on the OUT message - resultExchange.setPattern(ExchangePattern.InOut); - // do not propagate any method name when using OGNL, as with OGNL we - // compute and provide the method name to explicit to invoke - resultExchange.getIn().removeHeader(Exchange.BEAN_METHOD_NAME); - - // current ognl path as we go along - String ognlPath = ""; + if (ognl != null) { + // must be a valid method name according to java identifier ruling + OgnlHelper.validateMethodName(ognl); + } - // loop and invoke each method - Object beanToCall = beanHolder.getBean(); - Class<?> beanType = beanHolder.getBeanInfo().getType(); + // 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); - // there must be a bean to call with, we currently does not support OGNL expressions on using purely static methods - if (beanToCall == null && beanType == null) { - throw new IllegalArgumentException("Bean instance and bean type is null. OGNL bean expressions requires to have either a bean instance of the class name of the bean to use."); + for (String methodName : methods) { + BeanHolder holder; + if (beanToCall != null) { + holder = new ConstantBeanHolder(beanToCall, exchange.getContext()); + } else if (beanType != null) { + holder = new ConstantTypeBeanHolder(beanType, exchange.getContext()); + } else { + holder = null; } - if (ognl != null) { - // must be a valid method name according to java identifier ruling - OgnlHelper.validateMethodName(ognl); - } + // support the null safe operator + boolean nullSafe = OgnlHelper.isNullSafeOperator(methodName); - // 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); - - for (String methodName : methods) { - BeanHolder holder; - if (beanToCall != null) { - holder = new ConstantBeanHolder(beanToCall, exchange.getContext()); - } else if (beanType != null) { - holder = new ConstantTypeBeanHolder(beanType, exchange.getContext()); - } else { - holder = null; - } + if (holder == null) { + String name = getBeanName(null, beanHolder); + throw new RuntimeBeanExpressionException(exchange, name, ognl, "last method returned null and therefore cannot continue to invoke method " + methodName + " on a null instance"); + } - // support the null safe operator - boolean nullSafe = OgnlHelper.isNullSafeOperator(methodName); + // keep up with how far are we doing + ognlPath += methodName; - if (holder == null) { - String name = getBeanName(null, beanHolder); - throw new RuntimeBeanExpressionException(exchange, name, ognl, "last method returned null and therefore cannot continue to invoke method " + methodName + " on a null instance"); - } + // get rid of leading ?. or . as we only needed that to determine if null safe was enabled or not + methodName = OgnlHelper.removeLeadingOperators(methodName); - // keep up with how far are we doing - ognlPath += methodName; + // are we doing an index lookup (eg in Map/List/array etc)? + String key = null; + KeyValueHolder<String, String> index = OgnlHelper.isOgnlIndex(methodName); + if (index != null) { + methodName = index.getKey(); + key = index.getValue(); + } - // get rid of leading ?. or . as we only needed that to determine if null safe was enabled or not - methodName = OgnlHelper.removeLeadingOperators(methodName); + // only invoke if we have a method name to use to invoke + if (methodName != null) { + Object newResult = invokeBean(holder, beanName, methodName, resultExchange); - // are we doing an index lookup (eg in Map/List/array etc)? - String key = null; - KeyValueHolder<String, String> index = OgnlHelper.isOgnlIndex(methodName); - if (index != null) { - methodName = index.getKey(); - key = index.getValue(); + // check for exception and rethrow if we failed + if (resultExchange.getException() != null) { + throw new RuntimeBeanExpressionException(exchange, beanName, methodName, resultExchange.getException()); } - // only invoke if we have a method name to use to invoke - if (methodName != null) { - Object newResult = invokeBean(holder, beanName, methodName, resultExchange); - - // check for exception and rethrow if we failed - if (resultExchange.getException() != null) { - throw new RuntimeBeanExpressionException(exchange, beanName, methodName, resultExchange.getException()); - } + result = newResult; + } - result = newResult; + // if there was a key then we need to lookup using the key + if (key != null) { + // if key is a nested simple expression then re-evaluate that again + if (SimpleLanguage.hasSimpleFunction(key)) { + key = SimpleLanguage.expression(key).evaluate(exchange, String.class); } - - // if there was a key then we need to lookup using the key if (key != null) { - // if key is a nested simple expression then re-evaluate that again - if (SimpleLanguage.hasSimpleFunction(key)) { - key = SimpleLanguage.expression(key).evaluate(exchange, String.class); - } - if (key != null) { - result = lookupResult(resultExchange, key, result, nullSafe, ognlPath, holder.getBean()); - } - } - - // check null safe for null results - if (result == null && nullSafe) { - return; + result = lookupResult(resultExchange, key, result, nullSafe, ognlPath, holder.getBean()); } + } - // prepare for next bean to invoke - beanToCall = result; - beanType = null; + // check null safe for null results + if (result == null && nullSafe) { + return null; } + + // prepare for next bean to invoke + beanToCall = result; + beanType = null; } - private Object lookupResult(Exchange exchange, String key, Object result, boolean nullSafe, String ognlPath, Object bean) { - ObjectHelper.notEmpty(key, "key", "in Simple language ognl path: " + ognlPath); + return result; + } - // trim key - key = key.trim(); + private static Object lookupResult(Exchange exchange, String key, Object result, boolean nullSafe, String ognlPath, Object bean) { + StringHelper.notEmpty(key, "key", "in Simple language ognl path: " + ognlPath); - // remove any enclosing quotes - key = StringHelper.removeLeadingAndEndingQuotes(key); + // trim key + key = key.trim(); - // try map first - Map<?, ?> map = exchange.getContext().getTypeConverter().convertTo(Map.class, result); - if (map != null) { - return map.get(key); - } + // remove any enclosing quotes + key = StringHelper.removeLeadingAndEndingQuotes(key); + + // try map first + Map<?, ?> map = exchange.getContext().getTypeConverter().convertTo(Map.class, result); + if (map != null) { + return map.get(key); + } - // special for list is last keyword - Integer num = exchange.getContext().getTypeConverter().tryConvertTo(Integer.class, key); - boolean checkList = key.startsWith("last") || num != null; - - if (checkList) { - List<?> list = exchange.getContext().getTypeConverter().convertTo(List.class, result); - if (list != null) { - if (key.startsWith("last")) { - num = list.size() - 1; - - // maybe its an expression to subtract a number after last - String after = ObjectHelper.after(key, "-"); - if (after != null) { - Integer redux = exchange.getContext().getTypeConverter().tryConvertTo(Integer.class, after.trim()); - if (redux != null) { - num -= redux; - } else { - throw new ExpressionIllegalSyntaxException(key); - } + // special for list is last keyword + Integer num = exchange.getContext().getTypeConverter().tryConvertTo(Integer.class, key); + boolean checkList = key.startsWith("last") || num != null; + + if (checkList) { + List<?> list = exchange.getContext().getTypeConverter().convertTo(List.class, result); + if (list != null) { + if (key.startsWith("last")) { + num = list.size() - 1; + + // maybe its an expression to subtract a number after last + String after = StringHelper.after(key, "-"); + if (after != null) { + Integer redux = exchange.getContext().getTypeConverter().tryConvertTo(Integer.class, after.trim()); + if (redux != null) { + num -= redux; + } else { + throw new ExpressionIllegalSyntaxException(key); } } - if (num != null && num >= 0 && list.size() > num - 1 && list.size() > 0) { - return list.get(num); - } - if (!nullSafe) { - // not null safe then its mandatory so thrown out of bounds exception - throw new IndexOutOfBoundsException("Index: " + num + ", Size: " + list.size() - + " out of bounds with List from bean: " + bean + "using OGNL path [" + ognlPath + "]"); - } } - } - - if (!nullSafe) { - throw new IndexOutOfBoundsException("Key: " + key + " not found in bean: " + bean + " of type: " - + ObjectHelper.classCanonicalName(bean) + " using OGNL path [" + ognlPath + "]"); - } else { - // null safe so we can return null - return null; + if (num != null && num >= 0 && list.size() > num - 1 && list.size() > 0) { + return list.get(num); + } + if (!nullSafe) { + // not null safe then its mandatory so thrown out of bounds exception + throw new IndexOutOfBoundsException("Index: " + num + ", Size: " + list.size() + + " out of bounds with List from bean: " + bean + "using OGNL path [" + ognlPath + "]"); + } } } - public Object getResult() { - return result; + if (!nullSafe) { + throw new IndexOutOfBoundsException("Key: " + key + " not found in bean: " + bean + " of type: " + + ObjectHelper.classCanonicalName(bean) + " using OGNL path [" + ognlPath + "]"); + } else { + // null safe so we can return null + return null; } }