You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2010/03/14 16:45:50 UTC

svn commit: r922880 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/component/bean/ main/java/org/apache/camel/language/bean/ main/java/org/apache/camel/util/ test/java/org/apache/camel/language/

Author: davsclaus
Date: Sun Mar 14 15:45:49 2010
New Revision: 922880

URL: http://svn.apache.org/viewvc?rev=922880&view=rev
Log:
CAMEL-2546: Camel OGNL now supports shorthand method names, eg getter style. Improved compare operator as well to NOT perfer to compare with String types.

Added:
    camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageInvalidOGNLTest.java
      - copied, changed from r922808, camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageOGNLTest.java
Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanProcessor.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/util/IntrospectionSupport.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleTest.java

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java Sun Mar 14 15:45:49 2010
@@ -51,6 +51,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import static org.apache.camel.util.ExchangeHelper.convertToType;
+
 /**
  * Represents the metadata about a bean type created via a combination of
  * introspection and annotations together with some useful sensible defaults
@@ -134,8 +135,8 @@ public class BeanInfo {
 
         String name = exchange.getIn().getHeader(Exchange.BEAN_METHOD_NAME, String.class);
         if (name != null) {
-            if (operations.containsKey(name)) {
-                List<MethodInfo> methods = operations.get(name);
+            if (hasOperations(name)) {
+                List<MethodInfo> methods = getOperations(name);
                 if (methods != null && methods.size() == 1) {
                     // only one method then choose it
                     methodInfo = methods.get(0);
@@ -233,9 +234,9 @@ public class BeanInfo {
             LOG.trace("Adding operation: " + opName + " for method: " + methodInfo);
         }
 
-        if (operations.containsKey(opName)) {
+        if (hasOperations(opName)) {
             // we have an overloaded method so add the method info to the same key
-            List<MethodInfo> existing = operations.get(opName);
+            List<MethodInfo> existing = getOperations(opName);
             existing.add(methodInfo);
         } else {
             // its a new method we have not seen before so wrap it in a list and add it
@@ -672,4 +673,46 @@ public class BeanInfo {
         return clazz;
     }
 
+    /**
+     * Do we have an operation with the given name.
+     * <p/>
+     * Shorthand method names for getters is supported, so you can pass in eg 'name' and Camel
+     * will can find the real 'getName' method instead.
+     *
+     * @param methodName the method name
+     * @return <tt>true</tt> if we have such a method.
+     */
+    private boolean hasOperations(String methodName) {
+        return getOperations(methodName) != null;
+    }
+
+    /**
+     * Get the operation(s) with the given name. We can have multiple when methods is overloaded.
+     * <p/>
+     * Shorthand method names for getters is supported, so you can pass in eg 'name' and Camel
+     * will can find the real 'getName' method instead.
+     *
+     * @param methodName the method name
+     * @return the found method, or <tt>null</tt> if not found
+     */
+    private List<MethodInfo> getOperations(String methodName) {
+        List<MethodInfo> answer = operations.get(methodName);
+        if (answer != null) {
+            return answer;
+        }
+
+        // now try all getters to see if any of those matched the methodName
+        for (Method method : methodMap.keySet()) {
+            if (IntrospectionSupport.isGetter(method)) {
+                String shorthandMethodName = IntrospectionSupport.getGetterShorthandName(method);
+                // if the two names matches then see if we can find it using that name
+                if (methodName.equals(shorthandMethodName)) {
+                    return operations.get(method.getName());
+                }
+            }
+        }
+
+        return null;
+    }
+
 }

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanProcessor.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanProcessor.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanProcessor.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanProcessor.java Sun Mar 14 15:45:49 2010
@@ -42,6 +42,7 @@ public class BeanProcessor extends Servi
     private Method methodObject;
     private String method;
     private BeanHolder beanHolder;
+    private boolean shorthandMethod;
 
     public BeanProcessor(Object pojo, BeanInfo beanInfo) {
         this(new ConstantBeanHolder(pojo, beanInfo));
@@ -205,6 +206,20 @@ public class BeanProcessor extends Servi
         this.method = method;
     }
 
+    public boolean isShorthandMethod() {
+        return shorthandMethod;
+    }
+
+    /**
+     * Sets whether to support getter style method name, so you can
+     * say the method is called 'name' but it will invoke the 'getName' method.
+     * <p/>
+     * Is by default turned off.
+     */
+    public void setShorthandMethod(boolean shorthandMethod) {
+        this.shorthandMethod = shorthandMethod;
+    }
+
     // Implementation methods
     //-------------------------------------------------------------------------
     protected void doStart() throws Exception {

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/language/bean/BeanExpression.java Sun Mar 14 15:45:49 2010
@@ -22,6 +22,7 @@ import java.util.Map;
 import org.apache.camel.Exchange;
 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.BeanHolder;
@@ -68,6 +69,12 @@ public class BeanExpression implements E
         
         // invoking the bean can either be the easy way or using OGNL
 
+        // validate OGNL
+        if (OgnlHelper.isInvalidValidOgnlExpression(method)) {
+            ExpressionIllegalSyntaxException cause = new ExpressionIllegalSyntaxException(method);
+            throw new RuntimeBeanExpressionException(exchange, beanName, method, cause);
+        }
+
         if (OgnlHelper.isValidOgnlExpression(method)) {
             // okay the method is an ognl expression
             Object beanToCall = holder.getBean();
@@ -118,6 +125,8 @@ public class BeanExpression implements E
             BeanProcessor processor = new BeanProcessor(beanHolder);
             if (methodName != null) {
                 processor.setMethod(methodName);
+                // enable OGNL like invocation
+                processor.setShorthandMethod(true);
             }
             try {
                 // copy the original exchange to avoid side effects on it

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/util/IntrospectionSupport.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/util/IntrospectionSupport.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/util/IntrospectionSupport.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/util/IntrospectionSupport.java Sun Mar 14 15:45:49 2010
@@ -67,6 +67,23 @@ public final class IntrospectionSupport 
         return params.length == 0 && !type.equals(Void.TYPE);
     }
 
+    public static String getGetterShorthandName(Method method) {
+        if (!isGetter(method)) {
+            return method.getName();
+        }
+
+        String name = method.getName();
+        if (name.startsWith("get")) {
+            name = name.substring(3);
+            name = name.substring(0, 1).toLowerCase() + name.substring(1);
+        } else if (name.startsWith("is")) {
+            name = name.substring(2);
+            name = name.substring(0, 1).toLowerCase() + name.substring(1);
+        }
+
+        return name;
+    }
+
     public static boolean isSetter(Method method) {
         String name = method.getName();
         Class<?> type = method.getReturnType();

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/util/ObjectHelper.java Sun Mar 14 15:45:49 2010
@@ -109,6 +109,18 @@ public final class ObjectHelper {
      */
     @SuppressWarnings("unchecked")
     public static int typeCoerceCompare(TypeConverter converter, Object leftValue, Object rightValue) {
+
+        // prefer to NOT coerce to String so use the type which is not String
+        // for example if we are comparing String vs Integer then prefer to coerce to Interger
+        // as all types can be converted to String which does not work well for comparison
+        // as eg "10" < 6 would return true, where as 10 < 6 will return false.
+        // if they are both String then it doesn't matter
+        if (rightValue instanceof String && (!(leftValue instanceof String))) {
+            // if right is String and left is not then flip order (remember to * -1 the result then)
+            return typeCoerceCompare(converter, rightValue, leftValue) * -1;
+        }
+
+        // prefer to coerce to the right hand side at first
         if (rightValue instanceof Comparable) {
             Object value = converter.convertTo(rightValue.getClass(), leftValue);
             if (value != null) {
@@ -116,6 +128,7 @@ public final class ObjectHelper {
             }
         }
 
+        // then fallback to the left hand side
         if (leftValue instanceof Comparable) {
             Object value = converter.convertTo(leftValue.getClass(), rightValue);
             if (value != null) {

Copied: camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageInvalidOGNLTest.java (from r922808, camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageOGNLTest.java)
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageInvalidOGNLTest.java?p2=camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageInvalidOGNLTest.java&p1=camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageOGNLTest.java&r1=922808&r2=922880&rev=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageOGNLTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/language/BeanLanguageInvalidOGNLTest.java Sun Mar 14 15:45:49 2010
@@ -16,17 +16,30 @@
  */
 package org.apache.camel.language;
 
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.CamelExecutionException;
 import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExpressionIllegalSyntaxException;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.language.bean.RuntimeBeanExpressionException;
 
 /**
  * @version $Revision$
  */
-public class BeanLanguageOGNLTest extends ContextTestSupport {
+public class BeanLanguageInvalidOGNLTest extends ContextTestSupport {
 
-    public void testBeanLanguageOGNL() throws Exception {
-        String reply = template.requestBody("direct:start", "World", String.class);
-        assertEquals("Hello World", reply);
+    public void testBeanLanguageInvalidOGNL() throws Exception {
+        try {
+            String reply = template.requestBody("direct:start", "World", String.class);
+            fail("Should have thrown exception");
+        } catch (CamelExecutionException e) {
+            RuntimeBeanExpressionException rbee = assertIsInstanceOf(RuntimeBeanExpressionException.class, e.getCause());
+            ExpressionIllegalSyntaxException cause = assertIsInstanceOf(ExpressionIllegalSyntaxException.class, rbee.getCause());
+            assertEquals("Illegal syntax: getOther[xx", cause.getMessage());
+            assertEquals("getOther[xx", cause.getExpression());
+        }
     }
 
     @Override
@@ -35,26 +48,19 @@ public class BeanLanguageOGNLTest extend
             @Override
             public void configure() throws Exception {
                 from("direct:start")
-                    .transform().method(MyReallyCoolBean.class, "getOther.greet");
+                    .transform().method(MyReallyCoolBean.class, "getOther[xx");
             }
         };
     }
 
     public static class MyReallyCoolBean {
 
-        private MyOtherReallyCoolBean other = new MyOtherReallyCoolBean();
+        private Map map = new LinkedHashMap();
 
-        public MyOtherReallyCoolBean getOther() {
-            return other;
+        public Map getOther() {
+            return map;
         }
 
     }
 
-    public static class MyOtherReallyCoolBean {
-
-        public String greet(String name) {
-            return "Hello " + name;
-        }
-
-    }
-}
+}
\ No newline at end of file

Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleOperatorTest.java Sun Mar 14 15:45:49 2010
@@ -32,7 +32,7 @@ public class SimpleOperatorTest extends 
         return jndi;
     }
 
-    public void tesValueWithSpace() throws Exception {
+    public void testValueWithSpace() throws Exception {
         exchange.getIn().setBody("Hello Big World");
         assertExpression("${in.body} == 'Hello Big World'", true);
     }

Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleTest.java?rev=922880&r1=922879&r2=922880&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/language/SimpleTest.java Sun Mar 14 15:45:49 2010
@@ -244,6 +244,50 @@ public class SimpleTest extends Language
         assertExpression("${in.body.getAge}", 6);
     }
 
+    public void testBodyOGNLSimpleShorthand() throws Exception {
+        Animal camel = new Animal("Camel", 6);
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${in.body.name}", "Camel");
+        assertExpression("${in.body.age}", 6);
+    }
+
+    public void testBodyOGNLSimpleOperator() throws Exception {
+        Animal tiger = new Animal("Tony the Tiger", 13);
+        Animal camel = new Animal("Camel", 6);
+        camel.setFriend(tiger);
+
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${in.body.getName} contains 'Camel'", true);
+        assertExpression("${in.body.getName} contains 'Tiger'", false);
+        assertExpression("${in.body.getAge} < 10", true);
+        assertExpression("${in.body.getAge} > 10", false);
+        assertExpression("${in.body.getAge} <= '6'", true);
+        assertExpression("${in.body.getAge} > '6'", false);
+
+        assertExpression("${in.body.getAge} < ${body.getFriend.getAge}'", true);
+        assertExpression("${in.body.getFriend.isDangerous} == true", true);
+    }
+
+    public void testBodyOGNLSimpleOperatorShorthand() throws Exception {
+        Animal tiger = new Animal("Tony the Tiger", 13);
+        Animal camel = new Animal("Camel", 6);
+        camel.setFriend(tiger);
+
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${in.body.name} contains 'Camel'", true);
+        assertExpression("${in.body.name} contains 'Tiger'", false);
+        assertExpression("${in.body.age} < 10", true);
+        assertExpression("${in.body.age} > 10", false);
+        assertExpression("${in.body.age} <= '6'", true);
+        assertExpression("${in.body.age} > '6'", false);
+
+        assertExpression("${in.body.age} < ${body.friend.age}'", true);
+        assertExpression("${in.body.friend.dangerous} == true", true);
+    }
+
     public void testBodyOGNLNested() throws Exception {
         Animal tiger = new Animal("Tony the Tiger", 13);
         Animal camel = new Animal("Camel", 6);
@@ -258,6 +302,20 @@ public class SimpleTest extends Language
         assertExpression("${in.body.getFriend.getAge}", "13");
     }
 
+    public void testBodyOGNLNestedShorthand() throws Exception {
+        Animal tiger = new Animal("Tony the Tiger", 13);
+        Animal camel = new Animal("Camel", 6);
+        camel.setFriend(tiger);
+
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${in.body.name}", "Camel");
+        assertExpression("${in.body.age}", 6);
+
+        assertExpression("${in.body.friend.name}", "Tony the Tiger");
+        assertExpression("${in.body.friend.age}", "13");
+    }
+
     public void testBodyOGNLOrderList() throws Exception {
         List<OrderLine> lines = new ArrayList<OrderLine>();
         lines.add(new OrderLine(123, "Camel in Action"));
@@ -273,6 +331,21 @@ public class SimpleTest extends Language
         assertExpression("${in.body.getLines[1].getName}", "ActiveMQ in Action");
     }
 
+    public void testBodyOGNLOrderListShorthand() throws Exception {
+        List<OrderLine> lines = new ArrayList<OrderLine>();
+        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.lines[0].id}", 123);
+        assertExpression("${in.body.lines[0].name}", "Camel in Action");
+
+        assertExpression("${in.body.lines[1].id}", 456);
+        assertExpression("${in.body.lines[1].name}", "ActiveMQ in Action");
+    }
+
     public void testBodyOGNLList() throws Exception {
         List<OrderLine> lines = new ArrayList<OrderLine>();
         lines.add(new OrderLine(123, "Camel in Action"));
@@ -287,6 +360,20 @@ public class SimpleTest extends Language
         assertExpression("${in.body[1].getName}", "ActiveMQ in Action");
     }
 
+    public void testBodyOGNLListShorthand() throws Exception {
+        List<OrderLine> lines = new ArrayList<OrderLine>();
+        lines.add(new OrderLine(123, "Camel in Action"));
+        lines.add(new OrderLine(456, "ActiveMQ in Action"));
+
+        exchange.getIn().setBody(lines);
+
+        assertExpression("${in.body[0].id}", 123);
+        assertExpression("${in.body[0].name}", "Camel in Action");
+
+        assertExpression("${in.body[1].id}", 456);
+        assertExpression("${in.body[1].name}", "ActiveMQ in Action");
+    }
+
     public void testBodyOGNLArray() throws Exception {
         OrderLine[] lines = new OrderLine[2];
         lines[0] = new OrderLine(123, "Camel in Action");
@@ -301,6 +388,20 @@ public class SimpleTest extends Language
         assertExpression("${in.body[1].getName}", "ActiveMQ in Action");
     }
 
+    public void testBodyOGNLArrayShorthand() throws Exception {
+        OrderLine[] lines = new OrderLine[2];
+        lines[0] = new OrderLine(123, "Camel in Action");
+        lines[1] = new OrderLine(456, "ActiveMQ in Action");
+
+        exchange.getIn().setBody(lines);
+
+        assertExpression("${in.body[0].id}", 123);
+        assertExpression("${in.body[0].name}", "Camel in Action");
+
+        assertExpression("${in.body[1].id}", 456);
+        assertExpression("${in.body[1].name}", "ActiveMQ in Action");
+    }
+
     public void testBodyOGNLOrderListOutOfBounds() throws Exception {
         List<OrderLine> lines = new ArrayList<OrderLine>();
         lines.add(new OrderLine(123, "Camel in Action"));
@@ -319,6 +420,24 @@ public class SimpleTest extends Language
         }
     }
 
+    public void testBodyOGNLOrderListOutOfBoundsShorthand() throws Exception {
+        List<OrderLine> lines = new ArrayList<OrderLine>();
+        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);
+
+        try {
+            assertExpression("${in.body.lines[3].id}", 123);
+            fail("Should have thrown an exception");
+        } catch (RuntimeBeanExpressionException e) {
+            IndexOutOfBoundsException cause = assertIsInstanceOf(IndexOutOfBoundsException.class, e.getCause());
+
+            assertTrue(cause.getMessage().startsWith("Index: 3, Size: 2 out of bounds with List from bean"));
+        }
+    }
+
     public void testBodyOGNLOrderListOutOfBoundsWithElvis() throws Exception {
         List<OrderLine> lines = new ArrayList<OrderLine>();
         lines.add(new OrderLine(123, "Camel in Action"));
@@ -330,6 +449,17 @@ public class SimpleTest extends Language
         assertExpression("${in.body?.getLines[3].getId}", "");
     }
 
+    public void testBodyOGNLOrderListOutOfBoundsWithElvisShorthand() throws Exception {
+        List<OrderLine> lines = new ArrayList<OrderLine>();
+        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?.lines[3].id}", "");
+    }
+
     public void testBodyOGNLOrderListNoMethodNameWithElvis() throws Exception {
         List<OrderLine> lines = new ArrayList<OrderLine>();
         lines.add(new OrderLine(123, "Camel in Action"));
@@ -347,6 +477,23 @@ public class SimpleTest extends Language
         }
     }
 
+    public void testBodyOGNLOrderListNoMethodNameWithElvisShorthand() throws Exception {
+        List<OrderLine> lines = new ArrayList<OrderLine>();
+        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);
+
+        try {
+            assertExpression("${in.body.lines[0]?.rating}", "");
+            fail("Should have thrown exception");
+        } catch (RuntimeBeanExpressionException e) {
+            MethodNotFoundException cause = assertIsInstanceOf(MethodNotFoundException.class, e.getCause().getCause());
+            assertEquals("rating", cause.getMethodName());
+        }
+    }
+
     public void testBodyOGNLElvisToAvoidNPE() throws Exception {
         Animal tiger = new Animal("Tony the Tiger", 13);
         Animal camel = new Animal("Camel", 6);
@@ -372,6 +519,32 @@ public class SimpleTest extends Language
         }
     }
 
+    public void testBodyOGNLElvisToAvoidNPEShorthand() throws Exception {
+        Animal tiger = new Animal("Tony the Tiger", 13);
+        Animal camel = new Animal("Camel", 6);
+        camel.setFriend(tiger);
+
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${in.body.name}", "Camel");
+        assertExpression("${in.body.age}", 6);
+
+        // just to mix it a bit
+        assertExpression("${in.body.friend.getName}", "Tony the Tiger");
+        assertExpression("${in.body.getFriend.age}", "13");
+
+        // using elvis to avoid the NPE
+        assertExpression("${in.body.friend?.friend.name}", "");
+        try {
+            // without elvis we get an NPE
+            assertExpression("${in.body.friend.friend.name}", "");
+            fail("Should have thrown exception");
+        } catch (RuntimeBeanExpressionException e) {
+            assertEquals("Failed to invoke method: .friend.friend.name on null due to: java.lang.NullPointerException", e.getMessage());
+            assertIsInstanceOf(NullPointerException.class, e.getCause());
+        }
+    }
+
     public void testBodyOGNLReentrant() throws Exception {
         Animal camel = new Animal("Camel", 6);
         Animal tiger = new Animal("Tony the Tiger", 13);
@@ -388,6 +561,36 @@ public class SimpleTest extends Language
         assertExpression("${body.getFriend.getFriend.getFriend.getFriend.getFriend.getName}", "Big Ella");
     }
 
+    public void testBodyOGNLReentrantShorthand() throws Exception {
+        Animal camel = new Animal("Camel", 6);
+        Animal tiger = new Animal("Tony the Tiger", 13);
+        Animal elephant = new Animal("Big Ella", 48);
+
+        camel.setFriend(tiger);
+        tiger.setFriend(elephant);
+        elephant.setFriend(camel);
+
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${body.friend.friend.friend.name}", "Camel");
+        assertExpression("${body.friend.friend.friend.friend.name}", "Tony the Tiger");
+        assertExpression("${body.friend.friend.friend.friend.friend.name}", "Big Ella");
+    }
+
+    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");
+
+        Animal camel = new Animal("Camel", 6);
+        exchange.getIn().setBody(camel);
+
+        assertExpression("${body.isDangerous}", "false");
+        assertExpression("${body.dangerous}", "false");
+    }
+
     protected String getLanguageName() {
         return "simple";
     }
@@ -418,6 +621,10 @@ public class SimpleTest extends Language
             this.friend = friend;
         }
 
+        public boolean isDangerous() {
+            return name.contains("Tiger");
+        }
+
         @Override
         public String toString() {
             return name;