Spring Framework
  1. Spring Framework
  2. SPR-9017

Allow SpEL to resolve getter method against object of type Class

    Details

    • Last commented by a User:
      false

      Description

      If your context object is a Class<?> then you cannot evaluate an expression that calls a getter on that object.

      For example:

          /**
           * WORKS
           */
          public void testSpelGetterOnBean() {
              final String name = "foo";
              final Object thing = new Object() {
                  public String getName() { return name; }
              };
              
              final ExpressionParser parser = new SpelExpressionParser();
              final StandardEvaluationContext context = new StandardEvaluationContext(thing);
              final Object result = parser.parseExpression("name").getValue(context);
      
              assertEquals(name, result);
          }
      
          /**
           * FAILS
           */
          public void testSpelGetterOnClass() {
              final Class<?> clazz = Object.class;
              final String name = clazz.getName();
              
              final ExpressionParser parser = new SpelExpressionParser();
              final StandardEvaluationContext context = new StandardEvaluationContext(clazz);
              final Object result = parser.parseExpression("name").getValue(context);
      
              assertEquals(name, result);
          }
      

        Activity

        Hide
        Gregor Purdy added a comment -

        (Sorry about the bad name for the issue - I can't see how to edit it )

        Show
        Gregor Purdy added a comment - (Sorry about the bad name for the issue - I can't see how to edit it )
        Hide
        Gregor Purdy added a comment -

        Here's the exception

        org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'name' cannot be found on object of type 'java.lang.Object'
        at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:208)
        at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:72)
        at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:93)
        at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:88)
        at com.apple.store.toolkit.util.VariableExpanderBasicTest.testSpelGetterOnClass(VariableExpanderBasicTest.java:40)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at junit.framework.TestCase.runTest(TestCase.java:164)
        at junit.framework.TestCase.runBare(TestCase.java:130)
        at junit.framework.TestResult$1.protect(TestResult.java:106)
        at junit.framework.TestResult.runProtected(TestResult.java:124)
        at junit.framework.TestResult.run(TestResult.java:109)
        at junit.framework.TestCase.run(TestCase.java:120)
        at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

        Show
        Gregor Purdy added a comment - Here's the exception org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'name' cannot be found on object of type 'java.lang.Object' at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:208) at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:72) at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:93) at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:88) at com.apple.store.toolkit.util.VariableExpanderBasicTest.testSpelGetterOnClass(VariableExpanderBasicTest.java:40) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at junit.framework.TestCase.runTest(TestCase.java:164) at junit.framework.TestCase.runBare(TestCase.java:130) at junit.framework.TestResult$1.protect(TestResult.java:106) at junit.framework.TestResult.runProtected(TestResult.java:124) at junit.framework.TestResult.run(TestResult.java:109) at junit.framework.TestCase.run(TestCase.java:120) at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
        Hide
        Gregor Purdy added a comment -

        The problem is that this line of code in PropertyOrFieldReference's readProperty(ExpressionState, String) method:

        Class<?> contextObjectClass = getObjectClass(contextObject.getValue());

        is getting contextObjectClass as Object, not Class.

        The implementation of getObjectClass() on SpelNodeImpl is doing that on purpose

        public Class<?> getObjectClass(Object obj) {
        if (obj == null)

        { return null; }

        return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
        }

        That would need to be something like this for my stuff to work, presumably:

        public Class<?> getObjectClass(Object obj)

        { return (obj == null) ? null : obj.getClass(); }

        Don't know what might depend on the existing behavior or if there might be more to the issue...

        Show
        Gregor Purdy added a comment - The problem is that this line of code in PropertyOrFieldReference's readProperty(ExpressionState, String) method: Class<?> contextObjectClass = getObjectClass(contextObject.getValue()); is getting contextObjectClass as Object, not Class. The implementation of getObjectClass() on SpelNodeImpl is doing that on purpose public Class<?> getObjectClass(Object obj) { if (obj == null) { return null; } return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass()); } That would need to be something like this for my stuff to work, presumably: public Class<?> getObjectClass(Object obj) { return (obj == null) ? null : obj.getClass(); } Don't know what might depend on the existing behavior or if there might be more to the issue...
        Hide
        Gregor Purdy added a comment -

        I've been looking for a workaround. Since the way I'm calling SpEL goes through a layer of my own code first I can look at the root object and if it is of type Class<?> then I can wrap it in ClassWrapper (class I created to expose a couple of get* methods via delegation) and that should work. However, my actual use case involves a map being passed in, and the value for one of the keys is a Class and so wrapping the root doesn't help. I need a chance to wrap any intermediate result to fully work around this.

        I get lucky one more time because the specific use I found this under involves a code path where I've registered a custom property accessor that makes a Map look like a bean and it does have a chance to map a Class result value to a ClassWrapper. Its not general, but it works.

        Show
        Gregor Purdy added a comment - I've been looking for a workaround. Since the way I'm calling SpEL goes through a layer of my own code first I can look at the root object and if it is of type Class<?> then I can wrap it in ClassWrapper (class I created to expose a couple of get* methods via delegation) and that should work. However, my actual use case involves a map being passed in, and the value for one of the keys is a Class and so wrapping the root doesn't help. I need a chance to wrap any intermediate result to fully work around this. I get lucky one more time because the specific use I found this under involves a code path where I've registered a custom property accessor that makes a Map look like a bean and it does have a chance to map a Class result value to a ClassWrapper. Its not general, but it works.
        Hide
        Rob Winch added a comment -
        Show
        Rob Winch added a comment - Another user encountering this issue http://forum.springsource.org/showthread.php?129497-hasPermission-on-generics
        Show
        Phil Webb added a comment - - edited https://github.com/SpringSource/spring-framework/pull/136
        Hide
        Chris Beams added a comment -

        master:

        commit d28592a6c66f62059c0cf056cc7db04cebb947c3
        Author: Phillip Webb <pwebb@vmware.com>
        Commit: Phillip Webb <pwebb@vmware.com>
        
            SpEL support for methods and properties on class …
            
            Update the ReflectiveMethodResolver and ReflectivePropertyAccessor
            to allow methods and properties of java.lang.Class to be resolved
            when the target object is a class.
            
            Issue: SPR-9017
        

        3.1.x:

        commit 4525527794b75c9d06866d6561f9afb053906d8b
        Author: Phillip Webb <pwebb@vmware.com>
        Commit: Chris Beams <cbeams@vmware.com>
        
            SpEL support for methods and properties on class …
            
            Update the ReflectiveMethodResolver and ReflectivePropertyAccessor
            to allow methods and properties of java.lang.Class to be resolved
            when the target object is a class.
            
            Issue: SPR-9017
            Backport-Commit: d28592a6c66f62059c0cf056cc7db04cebb947c3
        
        Show
        Chris Beams added a comment - master: commit d28592a6c66f62059c0cf056cc7db04cebb947c3 Author: Phillip Webb <pwebb@vmware.com> Commit: Phillip Webb <pwebb@vmware.com> SpEL support for methods and properties on class … Update the ReflectiveMethodResolver and ReflectivePropertyAccessor to allow methods and properties of java.lang.Class to be resolved when the target object is a class. Issue: SPR-9017 3.1.x: commit 4525527794b75c9d06866d6561f9afb053906d8b Author: Phillip Webb <pwebb@vmware.com> Commit: Chris Beams <cbeams@vmware.com> SpEL support for methods and properties on class … Update the ReflectiveMethodResolver and ReflectivePropertyAccessor to allow methods and properties of java.lang.Class to be resolved when the target object is a class. Issue: SPR-9017 Backport-Commit: d28592a6c66f62059c0cf056cc7db04cebb947c3

          People

          • Assignee:
            Phil Webb
            Reporter:
            Gregor Purdy
            Last updater:
            Chris Beams
          • Votes:
            1 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Days since last comment:
              1 year, 26 weeks, 1 day ago