Announcement Announcement Module
Collapse
No announcement yet.
DefaultBeanValidator error Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • DefaultBeanValidator error

    I've been using the DefaultBeanValidator successfully for several months. Suddenly I am getting the following exception:

    Code:
    java.lang.IllegalArgumentException: Validator [class org.springmodules.validation.commons.DefaultBeanValidator] does not support [class com.silversky.scheduler.domain.Organization_$$_javassist_36] at org.springframework.validation.ValidationUtils.invokeValidator(ValidationUtils.java:60) at org.springframework.web.servlet.mvc.BaseCommandController.bindAndValidate(BaseCommandController.java:395) at org.springframework.web.servlet.mvc.AbstractFormController.handleRequestInternal(AbstractFormController.java:263) at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153) 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
    It appears that my Organization domain class has been replaced by a javassist proxy which is not recognized by the DefaultBeanValidator. I wasn't aware that I was using javassist, but assume that it is buried deep inside Spring somewhere.

    I have not changed any of my validator configuration, but here is what I have:

    Spring configuration file:
    Code:
    	<bean id="validatorFactory"
    	      class="org.springmodules.validation.commons.DefaultValidatorFactory">
    	  <property name="validationConfigLocations">
    	    <list>
    	      <value>/WEB-INF/validator/validator-rules.xml</value>
    	      <value>/WEB-INF/validator/validation.xml</value>
    	    </list>
    	  </property>
    	</bean>
    	
    	<bean id="beanValidator" class="org.springmodules.validation.commons.DefaultBeanValidator">
    	  <property name="validatorFactory" ref="validatorFactory"/>
    	</bean>
    validation.xml:
    Code:
    <form-validation>
      <!--
        Define global validation config in validation-global.xml
      -->
      <formset>
          <form name="organization">
                  <field property="id"
                         depends="required">
    
                      <arg0 key="organization.id"/>
                  </field>
                  <field property="name"
                         depends="required">
    
                      <arg0 key="organization.name"/>
                  </field>
                  <field property="status"
                         depends="required">
    
                      <arg0 key="organization.status"/>
                  </field>
                  <field property="startDate"
                         depends="required">
    
                      <arg0 key="organization.startDate"/>
                  </field>
                  <field property="endDate"
                         depends="required">
    
                      <arg0 key="organization.endDate"/>
                  </field>
          </form>
    ...
    Should the domain object be replaced by the proxy like that, or is this a configuration problem?

  • #2
    It's been so long since I've worked on this, that I forgot that the validation.xml file is being generated in my Ant script by the xdoclet.modules.web.WebDocletTask from the @spring.validator annotations. So the question becomes, why isn't this particular domain class being picked up when others are? It has the same @spring.validator annotations as the other domain classes.

    I realize that there are newer ways to accomplish this, but I think I'm stuck with it because those annotations are used by my LabelTag to mark required fields on my forms. If there's a more modern way to accomplish all this without ripping the guts out of my web tier, I'm all ears.

    Comment


    • #3
      OK, the WebDoclet task is working correctly. Here is the generated validation.xml entry for the class:

      Code:
            <form name="organization">
                    <field property="name"
                           depends="required">
                        <msg
                          name="required"
                          key="Name is required."
                          resource="false"/>
      
                        <arg0 key="organization.name"/>
                    </field>
                    <field property="status"
                           depends="required">
                        <msg
                          name="required"
                          key="Status is required."
                          resource="false"/>
      
                        <arg0 key="organization.status"/>
                    </field>
                    <field property="street1"
                           depends="required">
                        <msg
                          name="required"
                          key="Street is required."
                          resource="false"/>
      
                        <arg0 key="organization.street1"/>
                    </field>
                    <field property="city"
                           depends="required">
                        <msg
                          name="required"
                          key="City is required."
                          resource="false"/>
      
                        <arg0 key="organization.city"/>
                    </field>
                    <field property="state"
                           depends="required">
                        <msg
                          name="required"
                          key="State is required."
                          resource="false"/>
      
                        <arg0 key="organization.state"/>
                    </field>
                    <field property="postalCode"
                           depends="required">
                        <msg
                          name="required"
                          key="Postal code is required."
                          resource="false"/>
      
                        <arg0 key="organization.postalCode"/>
                    </field>
                    <field property="country"
                           depends="required">
                        <msg
                          name="required"
                          key="Country is required."
                          resource="false"/>
      
                        <arg0 key="organization.country"/>
                    </field>
                    <field property="phoneNumber"
                           depends="required">
                        <msg
                          name="required"
                          key="Phone number is required."
                          resource="false"/>
      
                        <arg0 key="organization.phoneNumber"/>
                    </field>
                    <field property="industry"
                           depends="required">
                        <msg
                          name="required"
                          key="Industry is required."
                          resource="false"/>
      
                        <arg0 key="organization.industry"/>
                    </field>
            </form>
      So the problem appears to be in the DefaultBeanValidator.

      Comment


      • #4
        Solution

        I wrote my own version of DefaultBeanValidator to solve the problem. It's a hack, but it works with the proxy as well as the real domain classes.

        Code:
        public class SstDefaultBeanValidator extends AbstractBeanValidator {
        
            private Log logger = LogFactory.getLog( getClass() );
        
            /**
             * If <code>true</code> the full class name of each bean will be used as the form name when looking for
             * a <code>Validator</code>. If <code>false</code> the uncapitalized, short name of the class will be used.
             */
            private boolean useFullyQualifiedClassName = false;
        
            /**
             * Sets the value of the <code>useFullyQualifiedClassName</code>.
             */
            public void setUseFullyQualifiedClassName(boolean useFullyQualifiedClassName) {
                this.useFullyQualifiedClassName = useFullyQualifiedClassName;
            }
        
            /**
             * If <code>useFullyQualifiedClassName</code> is false (default value), this function returns a
             * string containing the uncapitalized, short name for the given class
             * (e.g. myBean for the class com.domain.test.MyBean). Otherwise, it  returns the value
             * returned by <code>Class.getName()</code>.
             * Modified to strip out proxy class name suffix.
             *
             * @param cls <code>Class</code> of the bean to be validated.
             * @return the bean name.
             */
            protected String getFormName(Class cls) {
            	String proxySuffix = "_$$";
            	String className = cls.getSimpleName();
            	String formName = className;
            	if (className.contains(proxySuffix)){
            		formName = className.substring(0, className.indexOf(proxySuffix));
            	}
            	
            	// decapitalize the form name
            	formName = formName.substring(0, 1).toLowerCase() + formName.substring(1);
            	
            	logger.debug("getFormName() className = " + className + " formName = " + formName);
            	
                return (this.useFullyQualifiedClassName) ? cls.getName() : formName;
            }
        
        }

        Comment

        Working...
        X