Announcement Announcement Module
Collapse
No announcement yet.
Spring 3.1 Method Validation and Caching Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring 3.1 Method Validation and Caching

    Hi all

    I have just begun cutting my teeth using Method parameter Validation and Caching using Spring 3.1.

    I have hit an interesting wall

    It appears that if i declare validation and caching on the same method i have no way of forcing the validation advice to execute before the caching advice. The order property, although configurable for caching is not configurable for validation (by way of MethodValidationPostProcessor). This is hard coded to Integer.Max value which is causing it to be executed after the caching advice.

    Ideally, i would like the "before" advice order on my method to be:
    1. Validation
    2. Caching


    The whole debacle i am in, is that i am getting NPE's on my SPEL cache key expressions because the SPEL expression is being evaluated before validation. Short of doing conditional SPEL expressions, is there something more fluid, simpler.... ?

    Perhaps its something obvious, would appreciate any pointers

    Kind Regards
    JJ

  • #2
    There must be a reason for it as the code contains documentation that it is hardcoded.

    Code:
    public int getOrder() {
    		// This should run after all other post-processors, so that it can just add
    		// an advisor to existing proxies rather than double-proxy.
    		return LOWEST_PRECEDENCE;
    }
    You can always workaround this by NOT using the namespace but declaring/configuring everything by hand that way you can control whatever you want.

    Comment


    • #3
      Hi Marten

      Thank you for replying.

      By namespace, are you referring to the cache namespace configuration? (see code insert)
      Code:
      <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="false" mode="proxy"  order="2"/>
      I am not using the namespace for method validation, instead i am using the bean post processor...
      Code:
      <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
      I also tried subclassing the MethodValidationPostProcessor and overriding the getOrder method amongst other things to return a lower (configurable) number than the caching namespace order configuration. This resulted in the following configuration and code.

      Code:
      <bean class="org.jjvester.spring.validation.InventoryMethodValidationPostProcessor">
      	<property name="order" value="1"></property>
      </bean>
      
      public final class InventoryMethodValidationPostProcessor extends MethodValidationPostProcessor {
      
          private static final long serialVersionUID = 1L;
      
          private int order;
      
          /**
           * {@inheritDoc}
           */
          @Override
          public int getOrder() {
              return order;
          }
      
          /**
           * Sets the order.
           * 
           * @param order the new <code>order</code>
           */
          public void setOrder(final int order) {
              this.order = order;
          }
      }

      This is the first time i have needed to override a packaged PostProcessor so not sure if this will work the way i am hoping.

      Unfortunately, even though the InventoryMethodValidationPostProcessor does load it appears as though i have the same problem where the SPEL expression is being evaluated before the validation prompting me to think that the cache advice still seems to fire before the validation advice.

      The following partial stacktrace can be observed when enabling caching and validation on the same joinPoint.

      Code:
      org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 9): Field or property 'sku' cannot be found on null
      	at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:205)
      	at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:72)
      	at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:57)
      	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:93)
      	at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:88)
      	at org.springframework.cache.interceptor.ExpressionEvaluator.key(ExpressionEvaluator.java:80)
      	at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:464)
      	at org.springframework.cache.interceptor.CacheAspectSupport.inspectCacheables(CacheAspectSupport.java:291)
      	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:198)
      	at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:66)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      	at $Proxy31.save(Unknown Source)
      	at org.jjvester.inventory.IntegrationInventoryServiceValidationTest.testSaveNullProduct(IntegrationInventoryServiceValidationTest.java:20)
      One can clearly see the CacheAspectSupport firing and the resultant SPEL exception

      Conversely if I disable caching and only enable validation the result is somewhat expected.

      Code:
      org.hibernate.validator.method.MethodConstraintViolationException: The following constraint violations occurred: [MethodConstraintViolationImpl [method=public abstract org.jjvester.shared.inventory.ProductVO org.jjvester.inventory.InventoryService.save(org.jjvester.shared.inventory.ProductVO), parameterIndex=0, parameterName=arg0, kind=PARAMETER, message=Product required., messageTemplate=Product required., rootBean=org.jjvester.inventory.Inventory@360d50a6, rootBeanClass=class org.jjvester.inventory.Inventory, leafBean=org.jjvester.inventory.Inventory@360d50a6, invalidValue=null, propertyPath=InventoryService#save(arg0), constraintDescriptor=ConstraintDescriptorImpl{annotation=javax.validation.constraints.NotNull, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, elementType=PARAMETER, definedOn=DEFINED_IN_HIERARCHY, groups=[interface javax.validation.groups.Default, interface org.jjvester.inventory.InventoryService], attributes={message=Product required., payload=[Ljava.lang.Class;@191e31ea, groups=[Ljava.lang.Class;@d5f0688}}]]
      	at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:91)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
      	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
      	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
      	at $Proxy31.save(Unknown Source)
      	at org.jjvester.inventory.IntegrationInventoryServiceValidationTest.testSaveNullProduct(IntegrationInventoryServiceValidationTest.java:20)

      being a bit rusty with Spring of late and new to these features i would not dismiss the obvious silly oversight Just a pity i have no idea what that is lol
      Last edited by jjvester; Oct 12th, 2012, 06:01 AM. Reason: fixed code tags

      Comment


      • #4
        AS I mentioned before the order here is NOT the order in which the advices are applied it is the order in which the PostProcessor is applied PostProcessor is NOT the advice... (although one can influence the other ).

        You can still use the annotation-driven with a namespace (and set the order) and configure the Interceptor for validation (again NOT the post processor!) yourself by using a Aop:config/aop:aspect block and write an expression for that that way you can control the order of advice execution...

        Code:
        <bean id="methodValidator" class="MethodValidationInterceptor" />
        
        <aop:config>
          <aop:advisor advice-ref="methodValidator" expression="execution(org.springframework.validation.annotation.Validated * *(..))" order="1" />
        </aop:config>
        Something like the above (from the top of my head that is).
        Last edited by Marten Deinum; Oct 12th, 2012, 06:27 AM.

        Comment


        • #5
          Hi Marten

          Thanks again for coming to the rescue.

          I took your advice and it worked like a charm. Had it not been for me being so hell bent on using the PostProcessor perhaps i would have been more receptive to your earlier advice.

          In short my solution now is as you prescribed to use the MethodValidationInterceptor with a configurable advice / order.

          Configuration is now:
          Code:
          <bean id="methodValidator" class="org.springframework.validation.beanvalidation.MethodValidationInterceptor" />
          	<aop:config>
          	  <aop:advisor advice-ref="methodValidator" pointcut="within(@org.springframework.validation.annotation.Validated *)" order="1" />  
          	</aop:config>
          
          <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="false" mode="proxy"  order="2"/>
          Method validation and caching now play happily together

          Thank you

          Kind Regards
          JJ

          Comment


          • #6
            Maybe I should be clearer in my descriptions but alas. Although in retrospective I still find it strange as that your custom InventoryMethodValidationPostProcessor doesn't work (as in theory it should have worked, I guess your overriding and the hardcoded getOrder are interfering somehow).

            Maybe you could register a JIRA for an enhancement to get the order property configurable on this class also.

            Comment


            • #7
              Ok will do, thanks again for you help!

              Comment

              Working...
              X