Results 1 to 5 of 5

Thread: Strange bean-property-binding-result in Rest/Castor result

  1. #1
    Join Date
    Nov 2006
    Location
    Utrecht, Netherlands
    Posts
    12

    Default Strange bean-property-binding-result in Rest/Castor result

    Hi,
    I create a very simple Rest interface for a demo application which looks up either all Employees or just 1 Employee by id.

    Code:
    package demo.employee.web;
    
    import java.util.List;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.servlet.ModelAndView;
    
    import eu.diversit.demo.employee.model.Employee;
    
    @RequestMapping("/rest/employee/**")
    @Controller
    public class EmployeeRestController {
    
    	@RequestMapping(method=RequestMethod.GET)
    	public ModelAndView list() {	
    		List<Employee> employees = Employee.findAllEmployees();
    		ModelAndView mav = new ModelAndView("content");
    		mav.addObject("employees", employees);
    		return mav;
    	}
    	
    	@RequestMapping(value="{id}", method=RequestMethod.GET)
    	public ModelAndView get(@PathVariable("id") long eid) {
    		
    		Employee e = Employee.findEmployee(eid);
    		ModelAndView mav = new ModelAndView("content");
    		mav.addObject("employee",e);
    		return mav;
    	}
    }
    In the applicationContext.xml I use the ContentNegotiationViewController to return either JSP or XML and I configured the MarshallingView to use a CastorMashaller.

    Code:
    	<bean name="content" class="org.springframework.web.servlet.view.xml.MarshallingView">
    		<constructor-arg>
    			<bean class="org.springframework.oxm.castor.CastorMarshaller">
    				<property name="mappingLocation" value="classpath:castor-mapping.xml"/>
    			</bean>
    		</constructor-arg>
    	</bean>
        <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
            <property name="mediaTypes">
                <map>
                    <entry key="xml"  value="application/xml"/>
                    <entry key="html" value="text/html"/>
                </map>
            </property>
            <property name="viewResolvers">
                <list>
                    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
                    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                        <property name="prefix" value="/WEB-INF/jsp/"/>
                        <property name="suffix" value=".jsp"/>
                    </bean>
                </list>
            </property>
        </bean>
    When I request all employees, the result is as expected: a list with only 1 employee (which is the only one available):
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <array-list><employee><phone>4943894279473</phone><version>1</version><birthdate>1901-05-10T00:00:00.000+00:19</birthdate><email>myname@employee.org</email><name>MyName</name><title>Nobody</title><id>1</id></employee></array-list>
    When I request 1 employee by ID, I get a completely different result. Even though I just add an Employee instance to the ModelAndView object, the result displays a very long BeanPropertyBindingResult instance:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <bean-property-binding-result field-error-count="0" global-error-count="0" error-count="0"><property-accessor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" extract-old-value-for-editor="true" xsi:type="java:org.springframework.beans.BeanWrapperImpl"><root-instance xsi:type="employee"><phone>4943894279473</phone><version>0</version><birthdate>1973-05-10T00:00:00.000+01:00</birthdate><email>jdboer@diversit.eu</email><name>jdboer</name><title>Architect</title><id>1</id></root-instance><nested-path></nested-path><wrapped-instance xsi:type="employee"><phone>4943894279473</phone><version>0</version><birthdate>1973-05-10T00:00:00.000+01:00</birthdate><email>jdboer@diversit.eu</email><name>jdboer</name><title>Architect</title><id>1</id></wrapped-instance><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>birthdate</display-name><name>birthdate</name><short-description>birthdate</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>class</display-name><name>class</name><short-description>class</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>email</display-name><name>email</name><short-description>email</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>id</display-name><name>id</name><short-description>id</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>name</display-name><name>name</name><short-description>name</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>phone</display-name><name>phone</name><short-description>phone</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>title</display-name><name>title</name><short-description>title</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>version</display-name><name>version</name><short-description>version</short-description></property-descriptors></property-accessor><property-editor-registry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" extract-old-value-for-editor="true" xsi:type="java:org.springframework.beans.BeanWrapperImpl"><root-instance xsi:type="employee"><phone>4943894279473</phone><version>0</version><birthdate>1973-05-10T00:00:00.000+01:00</birthdate><email>jdboer@diversit.eu</email><name>jdboer</name><title>Architect</title><id>1</id></root-instance><nested-path></nested-path><wrapped-instance xsi:type="employee"><phone>4943894279473</phone><version>0</version><birthdate>1973-05-10T00:00:00.000+01:00</birthdate><email>jdboer@diversit.eu</email><name>jdboer</name><title>Architect</title><id>1</id></wrapped-instance><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>birthdate</display-name><name>birthdate</name><short-description>birthdate</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>class</display-name><name>class</name><short-description>class</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>email</display-name><name>email</name><short-description>email</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>id</display-name><name>id</name><short-description>id</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>name</display-name><name>name</name><short-description>name</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>phone</display-name><name>phone</name><short-description>phone</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>title</display-name><name>title</name><short-description>title</short-description></property-descriptors><property-descriptors bound="false" constrained="false" expert="false" hidden="false" preferred="false"><display-name>version</display-name><name>version</name><short-description>version</short-description></property-descriptors></property-editor-registry><model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:org.exolab.castor.mapping.MapItem"><key xsi:type="java:java.lang.String">org.springframework.validation.BindingResult.employee</key></model><model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:org.exolab.castor.mapping.MapItem"><key xsi:type="java:java.lang.String">employee</key><value xsi:type="employee"><phone>4943894279473</phone><version>0</version><birthdate>1973-05-10T00:00:00.000+01:00</birthdate><email>jdboer@diversit.eu</email><name>jdboer</name><title>Architect</title><id>1</id></value></model><message-codes-resolver xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:org.springframework.validation.DefaultMessageCodesResolver"/><target xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="employee"><phone>4943894279473</phone><version>0</version><birthdate>1973-05-10T00:00:00.000+01:00</birthdate><email>jdboer@diversit.eu</email><name>jdboer</name><title>Architect</title><id>1</id></target><nested-path></nested-path><object-name>employee</object-name></bean-property-binding-result>
    Why does this happen?
    How can I fix it?
    Adding an attribute name to the object which is added to the ModelAndView instance does not make any difference.
    Creating a custom castor-mapping also does not make any difference.
    It should just return the xml of 1 Employee instance. Any ideas?
    Joost den Boer
    Freelance Senior Java Architect
    The Future Group
    http://www.the-future-group.com

  2. #2
    Join Date
    Nov 2006
    Location
    Utrecht, Netherlands
    Posts
    12

    Default

    I just tested also with XmlBeans and Xschema but both give the same result. When requesting 1 employee it returns a BeanPropertyBindingResult in stead of just 1 employee instance. So the problem seems to be in Spring itself.
    Any ideas?
    Joost den Boer
    Freelance Senior Java Architect
    The Future Group
    http://www.the-future-group.com

  3. #3
    Join Date
    Nov 2006
    Location
    Utrecht, Netherlands
    Posts
    12

    Default

    I can 'fix' the problem by using BindingResult.MODEL_KEY_PREFIX to the attribute name.
    Code:
    mav.addObject(BindingResult.MODEL_KEY_PREFIX+"employee",e);
    But why is this needed? Shouldn't it be just possible to return a normal object instance which can be marshalled to XML or JSON?
    Joost den Boer
    Freelance Senior Java Architect
    The Future Group
    http://www.the-future-group.com

  4. #4
    Join Date
    Jul 2008
    Location
    Barcelona, Spain
    Posts
    20

    Default

    Same problem here!

    I've used jdober fix and works but, WHY is it happening???

  5. #5
    Join Date
    Nov 2009
    Posts
    2

    Default

    I've found the source of the problem, if indeed this is a problem.

    Line 131 of MarshallingView.java (in 3.0.0.RC2):

    Code:
    for (Object o : model.values()) {
        if (this.marshaller.supports(o.getClass())) {
            return o;
        }
    }
    When I look at the code in a debugger, the "model" object above is a HashMap. This code iterates through a HashMap's values() collection and checks to see if the object is serializable using the provided Marshaller's supports() method. I used XStreamMarshaller, and in its supports() method it checks its "supportedClasses" instance variable (of type Class[]). If the array is null then it just returns true. If not it actually checks the array. Since I hadn't explicitly set any supported classes the array was null and it indiscriminately returned true (btw CastorMarshaller always returns true, so will always encounter this issue), so the first object in the HashMap's values() Collection is returned each time. This would be fine if your POJO was the only object in the HashMap. Unfortunately, when I ran it, the HashMap always contained the BeanPropertyBindingResult object along with one of my POJOs. So, depending on the hashCode() value of the object it may be ordered before or after BeanPropertyBindingResult.

    I checked the "fix" offered earlier and it looks as if it will always work. Line 693 of HandlerMethodInvoker.java:

    Code:
    if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
            (isSessionAttr || isBindingCandidate(attrValue))) {
        String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
        if (mavModel != null && !model.containsKey(bindingResultKey)) {
            WebRequestDataBinder binder = new WebRequestDataBinder(attrValue, attrName);
            initBinder(handler, attrName, binder, webRequest);
            mavModel.put(bindingResultKey, binder.getBindingResult());
        }
    }
    As you can see, this code block is bypassed if your model name attribute starts with BindingResult.MODEL_KEY_PREFIX. This is the block where the BeanPropertyBindingResult object is added to the map.

    If you are using XStreamMarshaller, there is another potential solution. In your Spring context file, you can explicitly set the "supportedClasses" property of the XStreamMarshaller to the list of Classes that you want to check against. So long as you don't include BeanPropertyBindingResult as one of those classes, this should work.

    Code:
    ...
    <property name="supportedClasses">
    	<list>
    		<value>com.somepackage.SomeClass</value>
    		<value>com.somepackage.OtherClass</value>
    	</list>
    </property>
    ...
    Frankly, I'm not happy with either of these solutions because they rely on undocumented behavior and knowledge of source code in order to get a predictable result. I'm hoping that this will be made more obvious when the code is released.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •