Announcement Announcement Module
Collapse
No announcement yet.
Migrating from txProxyTemplate to <aop:config> Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Migrating from txProxyTemplate to <aop:config>

    I'm trying to migrate from the old 1.2.x style to the 2.0 XSD style. Here's what I have in 1.2.8:

    Code:
       
        <!-- Transaction template for Managers, from:
             http://blog.exis.com/colin/archives/2004/07/31/concise-transaction-definitions-spring-11/ -->
        <bean id="txProxyTemplate" abstract="true"
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="transactionManager" ref="transactionManager"/>
            <property name="transactionAttributes">
                <props>
                    <prop key="save*">PROPAGATION_REQUIRED</prop>
                    <prop key="remove*">PROPAGATION_REQUIRED</prop>
                    <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
                </props>
            </property>
        </bean>
    
        <!-- Generic manager that can be used to do basic CRUD operations on any objects -->
        <bean id="manager" parent="txProxyTemplate">
            <property name="target">
                <bean class="org.appfuse.service.impl.BaseManager">
                    <property name="dao" ref="dao"/>
                </bean>
            </property>
        </bean>
        
        <!-- Transaction declarations for business services.  To apply a generic transaction proxy to
             all managers, you might look into using the BeanNameAutoProxyCreator -->
        <bean id="userManager" parent="txProxyTemplate">
            <property name="target">
                <bean class="org.appfuse.service.impl.UserManagerImpl">
                    <property name="userDao" ref="userDao"/>
                </bean>
            </property>
            <!-- Override default transaction attributes b/c of UserExistsException -->
            <property name="transactionAttributes">
                <props>
                    <prop key="save*">PROPAGATION_REQUIRED,-UserExistsException</prop>
                    <prop key="remove*">PROPAGATION_REQUIRED</prop>
                    <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
                </props>
            </property>
            <!-- This property is overriden in applicationContext-security.xml to add
                 method-level role security -->
            <property name="preInterceptors">
                <list>
                    <ref bean="userSecurityInterceptor"/>
                </list>
            </property>
        </bean>
    
        <!-- This interceptor insures that that users can only update themselves, not other users -->
        <bean id="userSecurityInterceptor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="userSecurityAdvice"/>
            <property name="patterns" value=".*saveUser"/>
        </bean>
    
        <bean id="userSecurityAdvice" class="org.appfuse.service.UserSecurityAdvice">
            <property name="userCache" ref="userCache"/>
        </bean>
    Using 2.0, I'm guessing this same advice is applied using the following:

    Code:
    <aop:config>
            <aop:advisor id="serviceMethods" pointcut="execution(* *.service.*Manager.*(..))" advice-ref="txAdvice"/>
            <aop:advisor id="userManagerMethods" pointcut="execution(* *.service.UserManager.*(..))"
                         advice-ref="userManagerTxAdvice"/>
            <aop:advisor id="userManagerSecurity" pointcut="execution(* *.service.UserManager.saveUser(..))"
                         advice-ref="userSecurityAdvice"/>
        </aop:config>
    
        <tx:advice id="txAdvice">
            <tx:attributes>
                <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>
    
        <tx:advice id="userManagerTxAdvice">
            <tx:attributes>
                <tx:method name="save*" rollback-for="UserExistsException"/>
                <tx:method name="remove*"/>
                <tx:method name="*" read-only="true"/>
            </tx:attributes>
        </tx:advice>
    
        <!-- Generic manager that can be used to do basic CRUD operations on any objects -->
        <bean id="manager" class="org.appfuse.service.impl.BaseManager">
            <property name="dao" ref="dao"/>
        </bean>
    
        <bean id="userManager" class="org.appfuse.service.impl.UserManagerImpl">
            <property name="userDao" ref="userDao"/>
        </bean>
    
        <bean id="userSecurityAdvice" class="org.appfuse.service.UserSecurityAdvice">
            <property name="userCache" ref="userCache"/>
        </bean>
    I'd like to say this works, but it doesn't. I get tests that fail because I'm calling save() in a read-only transaction. I'm using Spring 2.0-rc1. I tried using rc2 (the latest version in ibiblio) and everything blows up when initializing.

    I thought removing the read-only flag from userManagerTxAdvice would work, but no dice, the issue still happens.

    Code:
        <tx:advice id="userManagerTxAdvice">
            <tx:attributes>
                <tx:method name="save*" rollback-for="UserExistsException"/>
                <tx:method name="remove*"/>
                <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>
    Stack trace:

    Code:
    [INFO] [talledLocalContainer] Caused by: javax.faces.el.EvaluationException: Exception while invokin
    g expression #{signupForm.save}
    [INFO] [talledLocalContainer]   at org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.
    java:153)
    [INFO] [talledLocalContainer]   at org.apache.myfaces.application.ActionListenerImpl.processAction(A
    ctionListenerImpl.java:63)
    [INFO] [talledLocalContainer]   ... 78 more
    [INFO] [talledLocalContainer] Caused by: org.springframework.dao.InvalidDataAccessApiUsageException:
     Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into Flush
    Mode.AUTO or remove 'readOnly' marker from transaction definition
    [INFO] [talledLocalContainer]   at org.springframework.orm.hibernate3.HibernateTemplate.checkWriteOp
    erationAllowed(HibernateTemplate.java:1082)
    Last edited by mraible; Sep 1st, 2006, 12:45 PM.

  • #2
    It might not be this simple but, have you tried changing your pointcuts to something a little more specific? It looks like all three pointcuts would match UserManager.saveUser joinpoint.

    Code:
    <aop:config>
            <aop:advisor id="serviceMethods" pointcut="execution(* *.service.*Manager.*(..)) and !within(*.service.UserManager)" advice-ref="txAdvice"/>
            <aop:advisor id="userManagerMethods" pointcut="within(*.service.UserManager)"
                         advice-ref="userManagerTxAdvice" order="2"/>
            <aop:advisor id="userManagerSecurity" pointcut="execution(* *.service.UserManager.saveUser(..))"
                         advice-ref="userSecurityAdvice" order="1"/>
        </aop:config>
    I hope this works.

    Comment


    • #3
      As far as I can tell, it appears that AspectJ's pointcut language does not support using wildcards (*) for package names. The following seems to work:

      Code:
          <aop:config>
              <aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* org.appfuse.service.*Manager.*(..))"/>
              <aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="within(org.appfuse.service.UserManager)"/>
              <aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* org.appfuse.service.UserManager.saveUser(..))"/>
          </aop:config>
      It'd be nice to figure out a way to make the "managerTx" advisor more generic so applications that use AppFuse could have this advice applied even when classes are in a different package than org.appfuse.service.

      Comment


      • #4
        Try this:
        http://www.eclipse.org/aspectj/doc/n...ePatterns.html

        I think "*" is for characters and ".." is for packages/sub-packages.

        Maybe an expression like this:
        Code:
        execution(* *..*Manager.*(..))"
        Don't have a way to test it right now.

        Another way is to define a named pointcut outside the advisor and reference it with pointcut-ref. In that case you can choose a type of pointcut - aspectj or regex.

        Something like this:
        Code:
        <aop:config>
          <aop:pointcut id="test" type="regex"  expression="*.UserManager.saveUser"/>
        </aop:config>
        This way you should be able to use 1.2 style regex poincut expression vs. AspectJ.

        Dmitry

        Comment


        • #5
          I tried the following, but no luck:

          Code:
              <aop:config>
                  <aop:pointcut id="serviceMethodsPointcut" type="regex" expression="*Manager.*"/>
                  <aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="execution(* org.appfuse.service.UserManager.*(..))" order="0"/>        
                  <aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* org.appfuse.service.UserManager.saveUser(..))" order="1"/>
                  <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethodsPointcut" order="2"/>    
              </aop:config>
          But this results in:

          Caused by: java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting 'identifier' at character position 0
          *Manager.*
          ^

          The following works, but is missing the benefit of wrapping transactions around any beans with service.*Manager in their package/class name.

          Code:
              <aop:config>
                  <aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="execution(* org.appfuse.service.UserManager.*(..))" order="0"/>        
                  <aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* org.appfuse.service.UserManager.saveUser(..))" order="1"/>
                  <aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* org.appfuse.service..*Manager.*(..))" order="2"/>
              </aop:config>

          Comment


          • #6
            Try this...

            Dmitry's suggestion of execution(* *..*Manager.*(..)) should really work.

            Can you try the following:

            Code:
            <aop:config>
                <aop:pointcut id="serviceMethodsPointcut" expression="execution(* *..*Manager.*(..))"/>
                <aop:advisor id="userManagerTx" advice-ref="userManagerTxAdvice" pointcut="execution(* org.appfuse.service.UserManager.*(..))" order="0"/>        
                <aop:advisor id="userManagerSecurity" advice-ref="userSecurityAdvice" pointcut="execution(* org.appfuse.service.UserManager.saveUser(..))" order="1"/>
                <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethodsPointcut" order="2"/>    
            </aop:config>
            -Ramnivas

            Comment


            • #7
              If I use:

              Code:
              <aop:pointcut id="serviceMethodsPointcut" expression="execution(* *..*Manager.*(..))"/>
              It results in:

              Code:
              [applicationContext-service.xml]: Invocation of init method failed; nested exception is 
              org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.NullPointerException
              Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.NullPointerException
              Caused by: java.lang.NullPointerException
              	at org.springframework.orm.hibernate3.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:431)
              Obviously, the pointcut express is too broad. If I change it to:

              Code:
              expression="execution(* *.service..*Manager.*(..))"
              ... it works great - thanks!

              Comment


              • #8
                You're welcome.

                You probably need the following modification to your pointcut (simply move the '..' portion) Since it is working for you, I suspect you already have this or a similar change.

                Code:
                expression="execution(* *..service.*Manager.*(..))"
                to select the service.*Manager type regardless of the parent package structure.

                -Ramnivas

                Comment

                Working...
                X