Page 1 of 2 12 LastLast
Results 1 to 10 of 56

Thread: Custom Converter Example

Hybrid View

  1. #1
    Join Date
    Aug 2008
    Posts
    15

    Default Custom Converter Example

    Does anyone have any resources on how to create a custom converter, define it in the configuration, and eventually use it?

    It seems there are some posts in this forum about it, but the information is scattered and there is never a "yes! got it working!" moment. Someone also pointed to the 2.0.3 changelog, but that turned up nothing.

    Much Thanks for your help.

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

    Default

    The best example is in the booking-mvc sample application.

    Steps...

    1. Implement your custom converter, modeling after existing converters in org.springframework.binding.convert.converters (done once per custom converter). For example:

    Code:
    public class StringToMoney extends StringToObject {
    
       public StringToMoney() {
           super(Money.class);
       }
    
       @Override
       protected Object toObject(String string, Class targetClass) throws Exception {
           return Money.valueOf(string);
       }
    
       @Override
       protected String toString(Object object) throws Exception {
           Money amount = (Money) object;
           return amount.toString();
       }
    
    }
    2. Create a custom conversion service implementation that installs your custom converters (done once):

    Code:
    @Component("conversionService")
    public class ApplicationConversionService extends DefaultConversionService {
    
        @Override
        protected void addDefaultConverters() {
           super.addDefaultConverters();
    
           // registers a default converter for Money type
           addConverter(new StringToMoney());
    
           // registers a custom converter reference-able by id and applied when requested
           addConverter("shortDate", new StringToDate());
        }
    
    }
    3. Register the conversion service with Web Flow (done once):

    Code:
        <webflow:flow-builder-services id="flowBuilderServices" conversion-service="conversionService" .../>
    Now there is a bug in 2.0.3 where the default expression parser used during data binding does not apply your custom conversion service. This is fixed in 2.0.4 nightly, but to workaround it on 2.0.3 you must manually configure the expression parser implementation and configure its conversion service e.g.

    Code:
        <webflow:flow-builder-services id="flowBuilderServices" conversion-service="conversionService" expression-parser="expressionParser" .../>
    
         <bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
             <constructor-arg>
                 <bean class="org.jboss.el.ExpressionFactoryImpl"/>    
             </constructor-arg>
             <property name="conversionService" ref="conversionService"/>
         </bean>
    4. Default converters for your custom types should be used automatically anytime you bind to a property of that type. For custom converters to be applied for a specific type you reference the converter when you setup a view-binding:

    Code:
        <view-state id="enterBookingDetails" model="booking">
            <binder>
                <binding property="checkinDate" converter="shortDate" required="true" />
                <binding property="checkoutDate" converter="shortDate" required="true" />
                <binding property="beds" required="true" />
                <binding property="smoking" required="true" />
                <binding property="creditCard" required="true" />
                <binding property="creditCardName" required="true" />
                <binding property="creditCardExpiryMonth" required="true" />
                <binding property="creditCardExpiryYear" required="true" />
                <binding property="amenities" required="false" />
            </binder>
            <transition on="proceed" to="reviewBooking" />
            <transition on="cancel" to="cancel" bind="false" />
        </view-state>
    Last edited by Keith Donald; Aug 21st, 2008 at 08:22 AM.
    Keith Donald
    Core Spring Development Team

  3. #3

    Default

    Hi Keith,

    i try your workaround for 2.0.3 but my converter was not called. Modified your configuration by changing the id of expressionparser as following:
    Code:
     <webflow:flow-builder-services id="flowBuilderServices" conversion-service="conversionService" expression-parser="expressionParser" .../>
    
         <bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
             <constructor-arg>
                 <bean class="org.jboss.el.ExpressionFactoryImpl"/>    
             </constructor-arg>
             <property name="conversionService" ref="conversionService"/>
         </bean>

    I have downloaded the last nigthly build and my converter was not called too.
    Whats wrong?
    See my own Thread please:
    http://forum.springframework.org/showthread.php?t=59007

    I believe the converters are a very nice feature and the community will use it. Please check the following code and give me a suggestion:

    Environment: SWF 2.0.4.CI-573 with facelets

    webflow-config.xml
    Code:
    	<!-- Configures the Spring Web Flow JSF integration -->
    	<faces:flow-builder-services id="facesFlowBuilderServices" 
    		conversion-service="conversionService"/>
    
    	<bean id="conversionService" class="de.myApp.web.springframework.MyConversationService" />

    conversationService:
    Add my own converter and the shortDate from examples to test it too.
    Code:
    package de.myApp.web.springframework;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.binding.convert.converters.StringToDate;
    import org.springframework.binding.convert.service.DefaultConversionService;
    
    import de.myApp.web.springframework.converter.StringToUnterkunftTyp;
    
    public class MyConversationService extends DefaultConversionService {
    	private final static Log LOGGER = LogFactory
    			.getLog(MyConversationService.class);
    
    	public MyConversationService() {
    		super();
    	}
    	
    	protected void addDefaultConverters() {
    		  super.addDefaultConverters();
    		  addConverter("unterkunftTyp",new StringToUnterkunftTyp());
    		  // try the demo Converter from examples.
    		  addConverter("shortDate", new StringToDate());
    	}
    }
    my Flow
    Code:
    	<view-state id="add" model="backingBean">
    		<binder>
    			<binding property="unterkunftTyp" converter="unterkunftTyp" />
    			<binding property="jetzt" converter="shortDate" />
    		</binder>
    		
    		<transition on="save" to="commitAndEnd">
    			<evaluate expression="backingBean.save(flowRequestContext)" />
    		</transition>	
    		<transition on="chooseregion" to="regionchooser">
    		</transition>	
    		<transition on="reload" to="fewoadd">
    		</transition>	
    	</view-state>

    backingBean:
    Code:
    	// with getter and setter
    	private UnterkunftTyp unterkunftTyp;
    	private Date jetzt = new Date();

    my own converter:
    Code:
    package de.myApp.web.springframework.converter;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.binding.convert.converters.InvalidFormatException;
    import org.springframework.binding.convert.converters.StringToObject;
    import org.springframework.util.StringUtils;
    
    import de.myApp.bean.UnterkunftTyp;
    import de.myApp.bean.impl.UnterkunftTypImpl;
    import de.myApp.service.UnterkunftTypService;
    
    public class StringToUnterkunftTyp extends StringToObject {
    
    	@Autowired
    	private transient UnterkunftTypService unterkunftTypService;
    	
    	public StringToUnterkunftTyp() {
    		super(UnterkunftTypImpl.class);
    	}
    	
    	/* (non-Javadoc)
    	 * @see org.springframework.binding.convert.converters.StringToObject#toObject(java.lang.String, java.lang.Class)
    	 */
    	@Override
    	public Object toObject(String string, Class targetClass) throws Exception {
    		if (!StringUtils.hasText(string)) {
    			return null;
    		}
    		try {
    			return unterkunftTypService.getUnterkunftTypById(new Integer(string));
    		} catch (Exception e) {
    			throw new InvalidFormatException(string, "unterkunftTyp", e);
    		}
    	}
    
    	public String toString(Object target) throws Exception {
    		UnterkunftTyp unterkunftTyp = (UnterkunftTyp) target;
    		if (unterkunftTyp == null) {
    			return "";
    		}
    		return unterkunftTyp.getOid().toString();
    	}
    
    	/**
    	 * @param unterkunftTypService the unterkunftTypService to set
    	 */
    	public void setUnterkunftTypService(UnterkunftTypService unterkunftTypService) {
    		this.unterkunftTypService = unterkunftTypService;
    	}
    	
    }
    my facelet
    No converter was called and i saw the jetzt.toString() and unterkunftTyp.toString() implementation.
    Code:
    	<td><h:inputText id="jetzt" value="#{feWoController.jetzt}"/><br/>
    	<h:inputText id="unterunftTyp" value="#{feWoController.unterkunftTyp}"/><br/>
    Thanks,
    alex

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

    Default

    Alex,

    Thanks I corrected the typo in the post.

    I should have pointed out that converters do not yet get applied in a JSF environment, since JSF components use their own converters and handle the binding themselves, and the EL binding system is configured a bit differently. There is a JIRA open: http://jira.springframework.org/browse/SWF-799 to address this - I encourage you to vote / comment on it, and use native JSF converters for the meantime with Faces. If there are limitations that the JSF converters have our converters make considerably easier for you to address, it'd be good to know that as well!

    Keith
    Keith Donald
    Core Spring Development Team

  5. #5

    Default

    I cannot get my Converter to be called as well. I'm using Spring Web, Webflow 2.0.3, applied your workaround, but when sourceClassConverters is checked, it is from the DefaultConversionService, not my ApplicationConversionService.

    ###EDIT
    Got it working, problem was somewhere else
    Last edited by icetbr; Aug 28th, 2008 at 09:20 AM.

  6. #6
    Join Date
    Sep 2008
    Posts
    4

    Default

    I am using webflow 2.0.3 under WebSphere Portal 6.0.0.1 and OGNL parser.

    We have a bunch of Property Editors that essentially do some formatting of Strings. I am trying use webflow 2.0 converters to achieve this but unable to do so. This used to work with one of our previous projects that used Webflow 1.0 because we used the org.springframework.beans.PropertyEditorRegistrar to register our customer editors with FormAction classes of the flows.

    The problem lies in the fact that our property editors formats the source that is of type String to the target which is also a String (but formatted). As far as I know, webflow 2.0.3 is not calling the registered custom converters if the target class is assignable from source class i.e. both of of same type (see
    org.springframework.binding.convert.service.Generi cConversionService#getConversionExecutor(String id, Class sourceClass, Class targetClass) method and the executor that it returns has a 'execute' method which also assume same thing (see
    org.springframework.binding.convert.service.Static ConversionExecutor#execute(Object source) ).

    What is the solution to this? Below is the relevant snippet of my config file:

    Code:
    	<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
    	    <webflow:flow-location-pattern value="/WEB-INF/flows/**/**-flow.xml" />
    	    <webflow:flow-location-pattern value="/WEB-INF/common/**/**-flow.xml" />
    	</webflow:flow-registry>
        
    	<webflow:flow-builder-services id="flowBuilderServices" expression-parser="expressionParser"
    					conversion-service="conversionService" view-factory-creator="mvcViewFactoryCreator"/>
    
    	<bean id="conversionService" class="my.example.MyPropertyConversionService"/>
    
        <bean id="expressionParser" class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser">
            <property name="conversionService" ref="conversionService"/>
        </bean>
    	<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
    	    <property name="useSpringBeanBinding" value="true"/>
    	</bean>
    MyPropertyConversionService is extending DefaultConversionService and adds my property editors using webflow's PropertyEditorConverter class as shown below:
    Code:
    public class MyPropertyConversionService extends DefaultConversionService {
    	protected void addDefaultConverters() {
    		super.addDefaultConverters();
    		addConverter("myConverter", new PropertyEditorConverter(new MyStringPropertyEditor(), java.lang.String.class));
    	}
    ...
    The class MyStringPropertyEditor is extending java.beans.PropertyEditorSupport.

    And my flow has binding properties that use this converter as shown below:
    Code:
    	<view-state id="view1" model="view1Form">
    	    <binder>
    	        <binding property="email" converter="myConverter"/>
    ...
    In summary, is there any way to use a PropertyEditor that has the same source and target types in webflow 2?

  7. #7
    Join Date
    Nov 2004
    Posts
    24

    Default

    Hi,

    I'm struggling with the custom converters.

    This is what I got:

    The flow.xml:
    Code:
    <view-state id="rekeninghouder" model="contractPolis">
    	<binder>
    	         <binding property="rekeningHouder.geboorteDatum" converter="dutchDate"/>  
                <binding property="rekeningHouder.geslacht"/>       
            </binder>
    		<transition on="back" to="basisgegevens" />
    		<transition on="proceed" to="productgegevens" />
    		<transition on="cancel" to="cancel" bind="false" />
    	</view-state>
    Conversion service:
    Code:
    public class ConversionServiceImpl extends DefaultConversionService {
    	
    	@Override
        protected void addDefaultConverters() {
           super.addDefaultConverters();
    
           StringToDate stringToDate = new StringToDate();
           stringToDate.setPattern("dd-MM-yyyy");
           addConverter("dutchDate",stringToDate);
          
        }
    
    
    }
    Application context:
    Code:
    <webflow:flow-builder-services id="flowBuilderServices" conversion-service="conversionService" />
    	
    	<bean id="conversionService" class="com.foo.myfoo.webflow.converters.ConversionServiceImpl"/>
    I'm using Spring Web 2.5.5 and Spring Weblow 2.0.8 in a portlet environment.

    I'm getting the following error:

    org.springframework.binding.convert.ConversionExec utorNotFoundException: No custom ConversionExecutor found with id 'dutchDate' for converting from sourceClass [java.util.Date] to targetClass [java.lang.String]
    at org.springframework.binding.convert.service.Generi cConversionService.getConversionExecutor(GenericCo nversionService.java:182)
    at org.springframework.webflow.engine.builder.support .FlowBuilderContextImpl$ParentConversionServicePro xy.getConversionExecutor(FlowBuilderContextImpl.ja va:154)
    at org.springframework.binding.convert.service.Generi cConversionService.getConversionExecutor(GenericCo nversionService.java:180)
    at org.springframework.webflow.mvc.view.BindingModel. getConverter(BindingModel.java:259)
    at org.springframework.webflow.mvc.view.BindingModel. getFormattedValue(BindingModel.java:236)
    at org.springframework.webflow.mvc.view.BindingModel. getFieldValue(BindingModel.java:141)
    at org.springframework.web.servlet.support.BindStatus .<init>(BindStatus.java:120)
    at org.springframework.web.servlet.tags.form.Abstract DataBoundFormElementTag.getBindStatus(AbstractData BoundFormElementTag.java:172)
    at org.springframework.web.servlet.tags.form.Abstract DataBoundFormElementTag.getPropertyPath(AbstractDa taBoundFormElementTag.java:192)
    at org.springframework.web.servlet.tags.form.Abstract DataBoundFormElementTag.getName(AbstractDataBoundF ormElementTag.java:158)
    at org.springframework.web.servlet.tags.form.Abstract DataBoundFormElementTag.writeDefaultAttributes(Abs tractDataBoundFormElementTag.java:121)
    at org.springframework.web.servlet.tags.form.Abstract HtmlElementTag.writeDefaultAttributes(AbstractHtml ElementTag.java:379)
    at org.springframework.web.servlet.tags.form.InputTag .writeTagContent(InputTag.java:139)
    at org.springframework.web.servlet.tags.form.Abstract FormTag.doStartTagInternal(AbstractFormTag.java:90 )
    at org.springframework.web.servlet.tags.RequestContex tAwareTag.doStartTag(RequestContextAwareTag.java:7 7)
    at com.ibm._jsp._rekeninghouder._jspx_meth_form_input _3(_rekeninghouder.java:567)
    at com.ibm._jsp._rekeninghouder._jspService(_rekening houder.java:253)
    at com.ibm.ws.jsp.runtime.HttpJspBase.service(HttpJsp Base.java:85)
    at javax.servlet.http.HttpServlet.service(HttpServlet .java:856)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.ser vice(ServletWrapper.java:966)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.ser vice(ServletWrapper.java:907)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.d oFilter(WebAppFilterChain.java:118)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain._ doFilter(WebAppFilterChain.java:87)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager .doFilter(WebAppFilterManager.java:696)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager .doFilter(WebAppFilterManager.java:641)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.han dleRequest(ServletWrapper.java:475)
    at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.h andleRequest(ServletWrapper.java:463)
    at com.ibm.wsspi.webcontainer.servlet.GenericServletW rapper.handleRequest(GenericServletWrapper.java:11 5)
    at com.ibm.ws.jsp.webcontainerext.AbstractJSPExtensio nServletWrapper.handleRequest(AbstractJSPExtension ServletWrapper.java:168)
    at com.ibm.ws.webcontainer.webapp.WebAppRequestDispat cher.include(WebAppRequestDispatcher.java:606)
    at org.springframework.web.servlet.view.InternalResou rceView.renderMergedOutputModel(InternalResourceVi ew.java:231)
    at org.springframework.web.servlet.view.AbstractView. render(AbstractView.java:258)
    at org.springframework.web.servlet.ViewRendererServle t.renderView(ViewRendererServlet.java:111)
    at org.springframework.web.servlet.ViewRendererServle t.processRequest(ViewRendererServlet.java:84)
    at org.springframework.web.servlet.ViewRendererServle t.doGet(ViewRendererServlet.java:65)
    at javax.servlet.http.HttpServlet.service(HttpServlet .java:743)
    at javax.servlet.http.HttpServlet.service(HttpServlet .java:856)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.ser vice(ServletWrapper.java:966)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.ser vice(ServletWrapper.java:907)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.d oFilter(WebAppFilterChain.java:118)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain._ doFilter(WebAppFilterChain.java:87)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager .doFilter(WebAppFilterManager.java:696)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager .doFilter(WebAppFilterManager.java:641)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.han dleRequest(ServletWrapper.java:475)
    at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.h andleRequest(ServletWrapper.java:463)
    at com.ibm.ws.webcontainer.webapp.WebAppRequestDispat cher.include(WebAppRequestDispatcher.java:606)
    at com.ibm.ws.portletcontainer.core.impl.PortletReque stDispatcherImpl.include(PortletRequestDispatcherI mpl.java:79)
    ... 95 more


    Can please somebody help me with this because I'm entering the state of madness?

    I know the converter is added. The problem is that it can't find it by id.

    Thanks in forward!
    Last edited by mkamer; Aug 12th, 2009 at 08:14 AM. Reason: Forgot to mention the portlet environment

  8. #8
    Join Date
    Nov 2004
    Posts
    24

    Default

    I debugged in the GenericConversionService and discovered that the customConverter property is populated with the customconverter (with id!) at start-up but is gone when it tries to retrieve it.

  9. #9
    Join Date
    Nov 2004
    Posts
    24

    Default

    Finally solved the problem.

    Completely missed the required part in

    Code:
    <webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">

  10. #10

    Default Still cannot make sense of it

    Hi All,

    I have been reading all this thread and looked around the web for a solution but so far I am still in the darkness.

    I still face the problem:

    No ConversionExecutor found for converting from sourceClass [com.mysite.MyDate] to target class [java.lang.String]
    org.springframework.binding.convert.ConversionExec utorNotFoundException: No ConversionExecutor found for converting from sourceClass [com.mysite.MyDate] to target class [java.lang.String]

    Which is strange to me because I have defined the conversion service with:

    Code:
    @Component("conversionService")
    public class ApplicationConversionService extends DefaultConversionService {
    
       @Override
       protected void addDefaultConverters() {
          super.addDefaultConverters();
          // registers a default converter for MyDate type
          addConverter("myDate", new MyDateConverter());
       }
    }
    and the converter with this class
    Code:
    public class MyDateConverter extends StringToObject {
       public MyDateConverter() {
          super(MyDate.class);
       }
    
       @Override
       protected Object toObject(String string, Class aClass) throws Exception {
          MyDate myDate = new MyDate(string);
          myDate.setDate(string);
          return myDate;
       }
    
       @Override
       protected String toString(Object o) throws Exception {
          MyDate myDate = (MyDate) o;
          return myDate.toString();
       }
    }
    Plus I have added the required params in the webflow-config:

    Code:
    <webflow:flow-builder-services id="flowBuilderServices"
                                      view-factory-creator="mvcViewFactoryCreator"
                                      conversion-service="conversionService"
                                      development="true" />
    in my flow definition I have

    Code:
    <binder>
             <binding property="birthdate" converter="myDate" required="true" />
      </binder>
    I tried to step with debugger inside the ApplicationConversionService class and the addConverter() is sucessfully called. Everything seems to be in order but still no success.

    Any idea why then it's not working ?
    --
    Davide

Tags for this Thread

Posting Permissions

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