Results 1 to 8 of 8

Thread: Migrating from txProxyTemplate to <aop:config>

  1. #1
    Join Date
    Aug 2004
    Location
    Denver
    Posts
    249

    Default 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 at 11:45 AM.

  2. #2
    Join Date
    Dec 2005
    Location
    Philadelphia, PA, USA
    Posts
    228

    Default

    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.

  3. #3
    Join Date
    Aug 2004
    Location
    Denver
    Posts
    249

    Default

    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.

  4. #4
    Join Date
    Dec 2005
    Location
    Philadelphia, PA, USA
    Posts
    228

    Default

    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

  5. #5
    Join Date
    Aug 2004
    Location
    Denver
    Posts
    249

    Default

    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>

  6. #6
    Join Date
    Jun 2006
    Location
    SF Bay Area, California
    Posts
    524

    Default 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
    Ramnivas Laddad (Follow me on Twitter)
    AspectJ in Action: Enterprise AOP with Spring Applications (2nd edition). Now available!

  7. #7
    Join Date
    Aug 2004
    Location
    Denver
    Posts
    249

    Default

    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!

  8. #8
    Join Date
    Jun 2006
    Location
    SF Bay Area, California
    Posts
    524

    Default

    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
    Ramnivas Laddad (Follow me on Twitter)
    AspectJ in Action: Enterprise AOP with Spring Applications (2nd edition). Now available!

Posting Permissions

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