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

Thread: trouble with spring webflow validation

  1. #1

    Default trouble with spring webflow validation

    I am having trouble with validation that it is not invoked at all.

    Following is my flow.xml:
    Code:
    <view-state id="addNode" model="nodeFlowBean.nodeVo" view="addNode.jspx">
        <on-entry>
            <set name="nodeFlowBean.recordReadOnly" value="false"/>
            <set name="nodeFlowBean.operation" value="'INSERT'"/>
        </on-entry>
        <transition on="save" to="viewEditNode">
            <evaluate expression="nodeFlowBean.insert(messageContext)"/>
        </transition>
    </view-state>
    This is the bean definition in spring container:
    Code:
    <bean id="nodeFlowBean" class="com.network.flowbean.NodeFlowBean" scope="flow">
            <property name="beanResolver">
                <ref bean="beanResolver"/>
            </property>
            <property name="nodeServiceName">
                <value>nodeService</value>
            </property>
            <aop:scoped-proxy/>
        </bean>
    And these are the validate methods inside the above bean:
    Code:
    public class NodeFlowBean implements Serializable  
    {
    	private static final long serialVersionUID = 1L;
        private NodeVO nodeVo;
        private String nodeServiceName;
    	private Boolean recordReadOnly = true;
    	private EEntityOperation operation;
    	private BeanResolver beanResolver;
    	
    	public NodeFlowBean()
    	{
    		this.nodeVo = new NodeVO();
    	}
    	public boolean insert(MessageContext messageContext)
    	{
            try
            {
                INodeService nodeService = (INodeService)this.beanResolver.getBean(this.nodeServiceName);
    	        this.nodeVo.setId(nodeService.addNewNode(this.nodeVo));
    	        messageContext.addMessage(new MessageBuilder().info().source("nodeForm").defaultText("Success").build());
    	        return true;
            }
            catch (RecordAlreadyExistsException e)
            {
    	        // TODO Auto-generated catch block
    	        e.printStackTrace();
    	        messageContext.addMessage(new MessageBuilder().info().source("nodeForm").defaultText("Failed").build());
    	        return false;
            }
    	}
    	
    	public void validateUserId(javax.faces.context.FacesContext context, javax.faces.component.UIComponent comp, Object value)
    	{
    	    if(!this.isUserIdUniqueInDB((String)value))
    	    {
                context.addMessage(comp.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,"Invalid User ID","Node with this User ID already exists.\nPlease use a unique User ID."));
                ((RichInputText)comp).setValid(false);
    	    }
    	}
        
    	public void validateAddNode(ValidationContext validationContext)
    	{
    	    this.validateThisNode(validationContext);
    	}
    	
    	public void validateViewEditNode(ValidationContext validationContext)
        {
    	    this.validateThisNode(validationContext);
        }
    	
    	private void validateThisNode(ValidationContext validationContext)
    	{
    	    MessageContext messageContext = validationContext.getMessageContext();
            if(!this.isUserIdUniqueInDB(this.nodeVo.getExternalNodeId()))
            {
                messageContext.addMessage(new MessageBuilder().error().source("nodeForm").defaultText("Node with this User ID already exists. Please use a unique User ID.").build());
            }
            if(!this.isNodeLatLongUniqueInDB(this.nodeVo))
            {
                messageContext.addMessage(new MessageBuilder().error().source("nodeForm").defaultText("Node with these coordinates already exits. Please enter unique coordinates.").build());
            }
    	}
    	
    	private boolean isUserIdUniqueInDB(final String userId)
    	{
    	    INodeService nodeService = (INodeService)this.beanResolver.getBean(this.nodeServiceName);
    	    NodeVO temp = new NodeVO();
            temp.setExternalNodeId(userId);
            List<NodeVO> exist = nodeService.findByExample(temp);
            if(this.operation == EEntityOperation.INSERT && exist != null && exist.size() > 0)
                return false;
            
            //in case of update need to check if any other node has this user id and since user id HAS to be unique
            //and Not-Null; so exist will at max have ONLY 1 item in it.
            //so check if that is different from current or not
            if(this.operation == EEntityOperation.UPDATE && exist != null && exist.size() == 1 && !exist.contains(this.nodeVo))
                    return false;            
            return true;
    	}
    	
    	private boolean isNodeLatLongUniqueInDB(final NodeVO nodeVoToValidate)
    	{
    	    INodeService nodeService = (INodeService)this.beanResolver.getBean(this.nodeServiceName);
    	    NodeVO temp = new NodeVO();
    	    temp.setLatDeg(nodeVoToValidate.getLatDeg());
    	    temp.setLatMin(nodeVoToValidate.getLatMin());
    	    temp.setLatMin(nodeVoToValidate.getLatSec());
    	    temp.setLatDirection(nodeVoToValidate.getLatDirection());
    	    temp.setLongDeg(nodeVoToValidate.getLongDeg());
    	    temp.setLongMin(nodeVoToValidate.getLongMin());
    	    temp.setLongSec(nodeVoToValidate.getLongSec());
    	    temp.setLongDirection(nodeVoToValidate.getLongDirection());
    	    List<NodeVO> exist = nodeService.findByExample(temp);
    	    if(this.operation == EEntityOperation.INSERT && exist != null && exist.size() > 0)
                return false;
            
            //in case of update need to check if any other node has these coordiates and since coordinates HAVE to be unique
            //and Not-Null; so exist will at max have ONLY 1 item in it.
            //so check if that is different from current or not
            if(this.operation == EEntityOperation.UPDATE && exist != null && exist.size() == 1 && !exist.contains(this.nodeVo))
                    return false;            
            return true;
    	}
    }
    Upon calling the transition in view state the insert method is called directly without calling the validate method. What am I missing here?

  2. #2
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    Your validate method isn't on the model object (nodevo) but on your surrounding object...
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  3. #3

    Default

    I removed all validation code from surrounding object and created a validator as described in webflow docs. But still no validation takes place. The code change is as follows:

    flow.xml:
    Code:
    <view-state id="addNode" model="nodeFlowBean.nodeVO" view="addNode.jspx">
        <on-entry>
            <set name="nodeFlowBean.recordReadOnly" value="false"/>
            <set name="nodeFlowBean.nodeVO.operation" value="'INSERT'"/>
        </on-entry>
        <transition on="save" to="viewEditNode">
            <evaluate expression="nodeFlowBean.insert(messageContext)"/>
        </transition>
    </view-state>
    spring container config
    Code:
    <bean id="nodeFlowBean" class="com.network.flowbean.NodeFlowBean" scope="flow">
            <property name="beanResolver">
                <ref bean="beanResolver"/>
            </property>
            <property name="nodeServiceName">
                <value>nodeService</value>
            </property>
            <aop:scoped-proxy/>
        </bean>
        
        <bean id="nodeVOValidator" class="com.network.validation.NodeVOValidator">
            <property name="nodeService"><ref bean="nodeService"/></property>
        </bean>
    nodeFlowBean insert method:
    Code:
    public class NodeFlowBean implements Serializable  
    {
    	private static final long serialVersionUID = 1L;
        private NodeVO nodeVO;
        private String nodeServiceName;
    	private Boolean recordReadOnly = true;
    	
    	private BeanResolver beanResolver;
    	
    	public NodeFlowBean()
    	{
    		this.nodeVO = new NodeVO();
    	}
    public boolean insert(MessageContext messageContext)
    	{
            try
            {
                INodeService nodeService = (INodeService)this.beanResolver.getBean(this.nodeServiceName);
    	        this.nodeVO.setId(nodeService.addNewNode(this.nodeVO));
    	        messageContext.addMessage(new MessageBuilder().info().source("nodeForm").defaultText("Success").build());
    	        return true;
            }
            catch (RecordAlreadyExistsException e)
            {
    	        // TODO Auto-generated catch block
    	        e.printStackTrace();
    	        messageContext.addMessage(new MessageBuilder().info().source("nodeForm").defaultText("Failed").build());
    	        return false;
            }
    	}
    }
    finally this is the validator:
    Code:
    public class NodeVOValidator
    {
        private INodeService nodeService;
        
    
        public INodeService getNodeService()
        {
            return nodeService;
        }
    
        public void setNodeService(INodeService nodeService)
        {
            this.nodeService = nodeService;
        }
    
        public boolean validateUserId(NodeVO nodeVo)
        {
            return this.isUserIdUniqueInDB(nodeVo);
        }
        
        public void validateAddNode(NodeVO nodeVo, ValidationContext validationContext)
        {
            this.validateThisNode(nodeVo, validationContext);
        }
        
        public void validateViewEditNode(NodeVO nodeVo, ValidationContext validationContext)
        {
            this.validateThisNode(nodeVo, validationContext);
        }
        
        private void validateThisNode(NodeVO nodeVo, ValidationContext validationContext)
        {
            MessageContext messageContext = validationContext.getMessageContext();
            if(!this.isUserIdUniqueInDB(nodeVo))
            {
                messageContext.addMessage(new MessageBuilder().error().source("nodeForm").defaultText("Node with this User ID already exists. Please use a unique User ID.").build());
            }
            if(!this.isNodeLatLongUniqueInDB(nodeVo))
            {
                messageContext.addMessage(new MessageBuilder().error().source("nodeForm").defaultText("Node with these coordinates already exits. Please enter unique coordinates.").build());
            }
        }
        
        private boolean isUserIdUniqueInDB(final NodeVO nodeVo)
        {
            List<NodeVO> exist = this.nodeService.findByExample(nodeVo);
            if(nodeVo.getOperation() == EEntityOperation.INSERT && exist != null && exist.size() > 0)
                return false;
            
            //in case of update need to check if any other node has this user id and since user id HAS to be unique
            //and Not-Null; so exist will at max have ONLY 1 item in it.
            //so check if that is different from current or not
            if(nodeVo.getOperation() == EEntityOperation.UPDATE && exist != null && exist.size() == 1 && !exist.contains(nodeVo))
                    return false;            
            return true;
        }
        
        private boolean isNodeLatLongUniqueInDB(final NodeVO nodeVoToValidate)
        {
            NodeVO temp = new NodeVO();
            temp.setLatDeg(nodeVoToValidate.getLatDeg());
            temp.setLatMin(nodeVoToValidate.getLatMin());
            temp.setLatMin(nodeVoToValidate.getLatSec());
            temp.setLatDirection(nodeVoToValidate.getLatDirection());
            temp.setLongDeg(nodeVoToValidate.getLongDeg());
            temp.setLongMin(nodeVoToValidate.getLongMin());
            temp.setLongSec(nodeVoToValidate.getLongSec());
            temp.setLongDirection(nodeVoToValidate.getLongDirection());
            List<NodeVO> exist = nodeService.findByExample(temp);
            if(nodeVoToValidate.getOperation() == EEntityOperation.INSERT && exist != null && exist.size() > 0)
                return false;
            
            //in case of update need to check if any other node has these coordiates and since coordinates HAVE to be unique
            //and Not-Null; so exist will at max have ONLY 1 item in it.
            //so check if that is different from current or not
            if(nodeVoToValidate.getOperation() == EEntityOperation.UPDATE && exist != null && exist.size() == 1 && !exist.contains(nodeVoToValidate))
                    return false;            
            return true;
        }
    
    }
    One thing i do not understand is why this dependence on names of validators why not flexibility to specify validator in flow.xml

    Anyways what am I missing now?

  4. #4
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    One thing i do not understand is why this dependence on names of validators why not flexibility to specify validator in flow.xml
    The fact that there is a naming/lookup schema doesn't mean you cannot call/define the method yourself! Have you actually tried it?!

    Quote Originally Posted by reference guide
    Validators must be registered as Spring beans employing the naming convention ${model}Validator to be detected and invoked automatically. In the example above, Spring 2.5 classpath-scanning would detect the @Component and automatically register it as a bean with the name bookingValidator. Then, anytime the booking model needs to be validated, this bookingValidator instance would be invoked for you.
    Code:
    <bean id="nodeVOValidator" class="com.network.validation.NodeVOValidator">
      <property name="nodeService"><ref bean="nodeService"/></property>
    </bean>
    Your model isn't 'nodeVO' it is 'nodeFlowBean.nodeVO'.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  5. #5

    Default

    The fact that there is a naming/lookup schema doesn't mean you cannot call/define the method yourself! Have you actually tried it?!
    Tried what? I am trying to follow what is there in SWF docs. Can you please explain what should i try? You mean to say call my validation method just before invoking transition evaluation method? How can that be done?

    Your model isn't 'nodeVO' it is 'nodeFlowBean.nodeVO'.
    So you are saying that i should name my validator class as 'NodeFlowBeanNodeVOValidator' or something else?? Please advise.

  6. #6
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    Quote Originally Posted by referenceguide
    Validators must be registered as Spring beans employing the naming convention ${model}Validator to be detected and invoked automatically. In the example above, Spring 2.5 classpath-scanning would detect the @Component and automatically register it as a bean with the name bookingValidator. Then, anytime the booking model needs to be validated, this bookingValidator instance would be invoked for you.
    So you are saying that i should name my validator class as 'NodeFlowBeanNodeVOValidator' or something else?? Please advise.
    No... Again read... The name of the bean in the xml has to change... As i stated the name of your model (${model}) is 'nodeFlowBean.nodeVO'. With the reference guide snippet above you should be able figure the naming out yourself.

    Tried what? I am trying to follow what is there in SWF docs. Can you please explain what should i try? You mean to say call my validation method just before invoking transition evaluation method? How can that be done?
    By simply adding another evaluate tag before the already existing one. it still is a method, there is nothing magical about it... The convention is there because the community wanted more 'convention over configuration', hence the naming schema. It doesn't mean you aren't able to call methods yourself... You can call yuor validate method 'fooBarBazAtWhatEverLocationYouWant' and add an evaluate statement which calls this method.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  7. #7

    Default

    I have spring webflow 2.1.1 that i believe is the latest stable release from spring website and this is what doc (....\spring-webflow-2.1.1.RELEASE\docs\spring-webflow-reference\html\) says:

    The second way is to define a separate object, called a Validator, which validates your model object. To do this, first create a class whose name has the pattern ${model}Validator, where ${model} is the capitialized form of the model expression, such as booking. Then define a public method with the name validate${state}, where ${state} is the id of your view-state, such as enterBookingDetails. The class should then be deployed as a Spring bean. Any number of validation methods can be defined
    This very clearly says to create a class whose name has pattern ${model}Validator. So if my model is 'nodeFlowBean.nodeVO' then what should be the class name?

    The convention is there because the community wanted more 'convention over configuration',
    Spring has always maintained 'convention' IF AND ONLY IF there is NO 'configuration' giving flexibility to users. Spring is synonymous with flexibility then why give up on that. If no validator specified then use convention but if user wants to specify validator then give him flexibility. That was my point.

    Anyways lets work out convention. As I stated above the docs say to name Class itself and NOT bean. So how should i name my class for model='nodeFlowBean.nodeVO' ?
    Last edited by akshay.jain.7983; Sep 9th, 2010 at 11:05 AM.

  8. #8
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    Read...

    Quote Originally Posted by referece guide
    Validators must be registered as Spring beans employing the naming convention ${model}Validator to be detected and invoked automatically.
    It has to be the NAME of the bean as registered in the context. What you state is only true if you use @Component WITHOUT explicit naming.

    f no validator specified then use convention but if user wants to specify validator then give him flexibility. That was my point.
    Then simply invoke it as I stated 2 times already.. Simpky call the method yourself by putting an evaluate expression in there!

    Code:
    <evaluate expression="myValidator.fooBarBaz(model, messageContext)" />
    Not sure if validationContext is a special variable, I think not. However changes to the messageContext (ie errors/warnings etc) are still detected and thus if errors occur it prevents the transition.
    Last edited by Marten Deinum; Sep 9th, 2010 at 11:25 AM. Reason: Fixed tags.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  9. #9

    Default

    Ohhkk...this annotation stuff is getting on my nerves really...those good old days of xml were great...simple and straight...I'll try a few bean ids as 'nodeFlowBean.nodeVO' or 'nodeFlowBean.NodeVO' or 'nodeFlowBeanNodeVO' or something like this.

    Simpky call the method yourself by putting an evaluate expression in there!
    So if there are two <evaluate> nested inside one <transition> and first one is a validation method call returning boolean value and if it returns false then second <evaluate> is not executed....correct??
    If incorrect then that is the magic with validators they dont execute further in case of errors.
    And if above assumption is correct then how do i pass ValidationContext into the method...I can pass MessageContext yes but does it provide same functionality...that is adding a error message meaning 'validation failed'?

    I must thank you for being patient with me... but i am new to SWF..

  10. #10
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    You don't have to return true/false you only need to add messages to the MessageContext. Which provides almost the same functionality as the ValidationContext (the ValidationContext contains the MessageContext). When validation fails the processing stops.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

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
  •