Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 30

Thread: Binding to Enum

  1. #11
    Join Date
    May 2005
    Location
    California, US
    Posts
    735

    Default

    In your jsp you have
    Code:
    <form:radiobutton path="title" value="${currentTitle}"/>
    what's the form backing object? I.e., what's the spring : bind class look like, and what's the type of title in it?

  2. #12
    Join Date
    May 2005
    Location
    California, US
    Posts
    735

    Default

    Sorry, I'm confused by my wacky code; I'm using a List of strings for the form. My form search class is SearchCriteria and Affiliations is the enum that I'm using for the check boxes:
    Code:
        public SearchCriteria() {
            final List<String> affilList = new ArrayList<String>();
            final Set<Affiliations> affilsSet = EnumSet.allOf(Affiliations.class);
    
            for (final Affiliations affil : affilsSet)
                affilList.add(affil.name());
    
            affiliations = affilList.toArray(new String[0]);
        }
    
    ...
    
        public String[] getAffiliations() {
            return (affiliations);
        }
    
        public void setAffiliations(final String[] _affiliations) {
            log.error("affiliations: {}", _affiliations);
    
            this.affiliations = _affiliations;
        }

  3. #13
    Join Date
    May 2005
    Location
    California, US
    Posts
    735

    Default

    Ok, my code really is/was wacky; in my jsp I have
    Code:
    <form:form modelAttribute="searchCriteria" id="searchForm">
    ...
        <form:checkboxes
            id="affiliations"
            path="affiliations"
            items="${affiliationsList}"
        />
    ...
    Then in my flow definition I have
    Code:
        <view-state id="enterSearchCriteria" view="enterSearchCriteria" model="searchCriteria">
            <on-render>
                <evaluate
                    expression="affiliationList.asList()"
                    result="viewScope.affiliationsList"
                    result-type="java.util.List"
                />
    (To add to the confusion I've changed the name of my class that returns a list of Affiliations enums to the singular AffiliationList from the plural AffiliationsList, and changed the enum from the plural Affiliations to the singular Affiliation, following the convention of naming the enum after the singular, like you're doing as well.)

    What was confusing me is that in my jsp I'd previously had
    Code:
            items="${affiliations}"
    and in the flow definition I'd previously had
    Code:
                    result="viewScope.affiliations"
    So that the name everywhere was, confusingly, "affiliations". But by changing it to affiliationsList it clarifies who's who; affiliationsList is the List of enums, and affiliations is the array of String in my form object SearchCriteria.

    So it looks like I'm cheating and using String for what comes back from the form, not enums.

  4. #14

    Default

    So to make things clear...

    I have an enum in a separate class file called Title.java

    Code:
    package ro.billoo.app.entity;
    
    public enum Title {
        MR, MS;
        public String getName() {
            return toString();
        }
    }
    In a Person class I have

    Code:
    public class Person implements Serializable {
        long id;
    
        Title title;
        String firstName;
        String lastName;
    
        public Title[] getAvailableTitles() {
            return Title.values();
        }
       
        public Title getTitle() {
            return title;
        }
    
        public void setTitle(Title title) {
            this.title = title;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    }
    and a Customer class

    Code:
    public class Customer implements Serializable {
        
        long id;
        Person  privateCustomer;
    
        public Person getPrivateCustomer() {
            return privateCustomer;
        }
    
        public void setPrivateCustomer(Person privateCustomer) {
            this.privateCustomer = privateCustomer;
        }
    }
    The jsp binding part:
    Code:
    <form:form commandName="customer">
            <spring:nestedPath path="privateCustomer">
                <jsp:include page="/snippets/personForm.jsp"/>
            </spring:nestedPath>      
            <input type="submit" name="_eventId_submit" value="<spring:message code="common.button.submit"/>">  
    </form:form>
    and personForm.jsp that contains

    Code:
      <c:forEach var="currentTitle" items="${availableTitles}">
                <form:radiobutton path="title" value="${currentTitle}"/>
                <spring:message code="common.label.title-${currentTitle}"/>
       </c:forEach>
    I registered a conversion service:
    Code:
    public class ApplicationConversionService extends DefaultConversionService {
    
        @Override
        protected void addDefaultConverters() {
            super.addDefaultConverters();
            addConverter(new StringToTitle());
        }
    
        @Override
        protected void addDefaultAliases() {
            super.addDefaultAliases();
            addAlias("title", Title.class);
        }
    }
    where StringToTitle:
    Code:
    public class StringToTitle extends StringToObject {
    
        public StringToTitle() {
            super(Title.class);
        }
    
        protected Object toObject(String string, Class objectClass)  {
            return Enum.valueOf(Title.class, string);
        }
    
        protected String toString(Object object)  {
            return object.toString();
        }
    }
    With this configuration I get no error but when I submit the form, nothing happends, it doesn't go to the next page. Also I used the debugger to check if toObject or toString are ever called... the answer is no.

    What can I do?

  5. #15
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    I would turn on logging of org.springframework.webflow and org.springframework.binding and see what is logged on postback when your form does not transition properly. Specifically, look for any odd data mapping errors in the log.

    Also, did you register your conversion service with the flow configuration system like this?

    Code:
    	<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
    		<webflow:flow-location path="/WEB-INF/hotels/booking/booking.xml" />
    	</webflow:flow-registry>
    	
    	<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator" conversion-service="conversionService"/>
    Where 'conversionService' is the id of your ApplicationConversionService bean.
    Keith Donald
    Core Spring Development Team

  6. #16
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Thinking about this more...

    I suspect what is happening is your StringToTitle converter is running when asked to format a Title Enum for display, but not running when doing the binding on form postback. I believe the reason is because the ConversionService used by the mapping Expression that calls your setTitle method is not your ApplicationConversionService, but rather a DefaultConversionService instance. You can confirm this by using a debugger to introspect the state of your ExpressionParser implementation.

    If this is the case, the workaround you'll need to employ is to manually configure your ExpressionParser instance and wire it in as a flow-builder-service. If you are using Ognl, you'll need to configure an instance of WebflowOgnlExpressionParser; if using EL, you'll need to configure an instance of WebflowELExpressionParser. Both of those classes have a conversionService property.

    In 2.0.4 we should do a couple of things: first, provide direct support for Java 5 enums in a generic fashion so you don't have to register your own specific converter; second, have the ConversionService used by the default ExpressionParser instances be the same one as you setup in your spring context.

    Let me know if you are able to resolve your problem.

    Keith
    Keith Donald
    Core Spring Development Team

  7. #17

    Default

    Yes I registered it. I also had some problems with the Expression Langauge. I had to add the com.springsource.org.jboss.el-2.0.0.GA.jar library.
    I changed the conversion service to

    Code:
    @Component("conversionService")
    public class ApplicationConversionService extends DefaultConversionService {
    
        @Override
        protected void addDefaultConverters() {
            super.addDefaultConverters();
            addConverter("title", new StringToTitle());
        }
    }
    and now my convertor gets added to the customConverter hashmap from the GenericConversionService, but when I try to load the form I still get

    Code:
    org.springframework.binding.convert.ConversionExecutorNotFoundException: No ConversionExecutor found for converting from sourceClass [ro.billoo.app.entity.Title] to target class [java.lang.String]
    	org.springframework.binding.convert.service.GenericConversionService.getConversionExecutor(GenericConversionService.java:162)
    	org.springframework.webflow.engine.builder.support.FlowBuilderContextImpl$ParentConversionServiceProxy.getConversionExecutor(FlowBuilderContextImpl.java:132)
    	org.springframework.binding.convert.service.GenericConversionService.getConversionExecutor(GenericConversionService.java:160)
    	org.springframework.webflow.mvc.view.BindingModel.getConverter(BindingModel.java:171)
    	org.springframework.webflow.mvc.view.BindingModel.getFormattedValue(BindingModel.java:144)
    	org.springframework.webflow.mvc.view.BindingModel.getFieldValue(BindingModel.java:128)
    	org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
    	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:172)
    	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:192)
    	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:158)

  8. #18
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Well, now your ConversionService is implemented incorrectly. You just registered a custom Converter implementation with id "title" -- that's not what you want; you want the default converter installed to converter between String and Title... just use addConverter(new StringToTitle()) -- see the JavaDocs of addConverter(Converter) and addConverter(String, Converter) for more information.

    I think you are close; fixing the bug in your Converter registration and wiring your ApplicationConversionService with a WebFlowELExpressionParser bean should hopefully resolve the problem.
    Keith Donald
    Core Spring Development Team

  9. #19

    Default

    Thank you Keith, you were right. It works now, it does the binding

    The conversion service:

    Code:
    public class ApplicationConversionService extends DefaultConversionService {
    
        @Override
        protected void addDefaultConverters() {
            super.addDefaultConverters();
            addConverter(new StringToTitle());
        }
    }
    StringToTitle:
    Code:
    public class StringToTitle extends StringToObject {
    
        public StringToTitle() {
            super(Title.class);
        }
    
        protected Object toObject(String string, Class objectClass)  {
            return Title.valueOf(string);
        }
    
        protected String toString(Object object)  {
            return object.toString();        
        }
    }
    and the configuration

    Code:
         <webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator"
                                       conversion-service="conversionService" expression-parser="expressionParser"/>
    
         <bean id="conversionService" class="ro.billoo.app.web.conversion.ApplicationConversionService"/>
    
        <bean id="expressionFactory" class="org.jboss.el.ExpressionFactoryImpl"/>
        
        <bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
            <constructor-arg index="0" ref="expressionFactory"/>
            <property name="conversionService" ref="conversionService"/>
        </bean>
    using the com.springsource.org.jboss.el-2.0.0.GA.jar library

  10. #20

    Default

    With the above configuration I noticed another thing
    Code:
    Caused by: javax.el.PropertyNotFoundException: Property 'execution' not found on type ro.billoo.app.entity.Customer
    	at javax.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:193)
    	at javax.el.BeanELResolver$BeanProperties.access$400(BeanELResolver.java:170)
    	at javax.el.BeanELResolver.property(BeanELResolver.java:279)
    	at javax.el.BeanELResolver.getType(BeanELResolver.java:84)
    	at javax.el.CompositeELResolver.getType(CompositeELResolver.java:112)
    	at org.springframework.binding.expression.el.DefaultELResolver.getType(DefaultELResolver.java:70)
    	at org.jboss.el.parser.AstIdentifier.getType(AstIdentifier.java:32)
    	at org.jboss.el.ValueExpressionImpl.getType(ValueExpressionImpl.java:174)
    	at org.springframework.binding.expression.el.ELExpression.getValueType(ELExpression.java:112)
    	... 60 more
    Why does it try to bind to the model the execution parameter from the request? How can I stop it?

Posting Permissions

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