Announcement Announcement Module
Collapse
No announcement yet.
Problem using transactions with AOP. Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Problem using transactions with AOP.

    Hi, Im trying to make a business function transactional, but I cant make it work.

    I have a Bean called PriceHandler (implements IPriceHandler) defined in the appliction context, in wich i have a function called addPrices wich make two diferents calls to PriceDAO (implements IPriceDAO) bean.
    If the second call fails, the rollback is not excecuted. How can i do this?

    I simply define the PriceHandler and PriceDAO in my application context as:


    <bean id="priceDAO" class="com.databusiness.dbuweb.dao.PriceDAO">
    <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="priceHandler" class="com.databusiness.dbuweb.business.PriceHandl er"/>


    Then my action (im using struts) calls the priceHandler method as follows:

    ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml");
    IPriceHandler ph = (IPriceHandler) ctx.getBean("priceHandler");
    ph.addPrices(price1, price2);
    Then the addPrice (in business.PriceHandler) Function is like:

    public void addPrices(Price p1, Price p2) throws DAOException{
    ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml");
    IPriceDAO pd = (IPriceDAO) ctx.getBean("priceDAO");
    pd.persistPrice1(p1);
    pd.persistPrice2(p2); ---> this throws DAOException,
    but p1 is not rolledback
    }


    AOP Configuration:

    <tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    <tx:method name="*" rollback-for="DAOException"/>
    </tx:attributes>
    </tx:advice>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSou rceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="txAdvisor" class="org.springframework.aop.support.RegexpMetho dPointcutAdvisor">
    <property name="advice"><ref local="txAdvice"/></property>
    <property name="pattern">
    <value>com.databusiness.dbuweb.business.*</value>
    </property>
    </bean>

    Whats wrong ???
    Thanks a lot... ive spent too much time in this

  • #2
    You can only apply transactions on spring managed beans. I assume here that your PriceHandler is an object instantied by a Struts action or either is a Struts action. Either way it isn't going to be under the management of Spring.

    On a side note, you don't want to do new ApplicationContext each time the method is called. You will run out of resources and performance quite quick! I strongly suggest you take a look at integrating Struts with Spring.

    Comment


    • #3
      Thanks for your answer, as you can imagine, Im new using Spring.

      PriceHandler is not an action, it is instantiated by an action doing:

      ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml");
      IPriceHandler ph = (IPriceHandler) ctx.getBean("priceHandler");

      I think that doing that, i was getting the pricehandler from Spring. Is this wrong? how do I get the bean from Spring?

      As im new in this, i really appreciate your help, do you know any good tutorial about integrating spring 2.0 and struts 2.0? or any idea how to solve the problem you say ?

      Thanks.

      Comment


      • #4
        Hi, Another thing. When you say "you don't want to do new ApplicationContext each time the method is called".

        Why i will run out of resources and performance quite quick? this applicationContext is not destroyed when the excectution of the action finish?

        Thanks...

        Comment


        • #5
          Well you get the bean from Spring but you don't want to create a new application context each time you need a bean. Inject the bean. Which is also part of the problem you are having right now.

          Strust
          Code:
          ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml"); 
          IPriceHandler ph = (IPriceHandler) ctx.getBean("priceHandler");
          ph.addPrices(price1, price2);
          The above code creates a new ApplicationContext retrieve the bean

          Your price handler
          Code:
          ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml");
          IPriceDAO pd = (IPriceDAO) ctx.getBean("priceDAO");
          pd.persistPrice1(p1);
          pd.persistPrice2(p2);
          The above code creates yet ANOTHER ApplicationContext. Effectivly duplicating everything, datasource/transactionmanager/dao/etc. AND starting new transactions for each dao call.

          You seem to be missing a lot of basic about spring here, I strongly recommend that you start reading the reference guide and take a look at the sample applications shipped with Spring.

          Here are some pointers.

          Change your PriceHandler, INJECT the dao don't retrieve it from the context by creating a new ApplicationContext. Check the code below.

          Code:
          public class PriceHandler implements IPriceHandler {
            
            private IPriceDao priceDao;
            
            public void setPriceDao(IPriceDao priceDao) {
              this.priceDao=priceDao;
            }
            
            public void addPrices(Price p1, Price p2) throws DAOException {
              priceDao.persistPrice1(p1);
              priceDao.persistPrice2(p2);
            }
          
          }
          Your configuration you are mixing a hell of a lot of notiations, ideas but don't seem to understand what it does. As a suggestion use one technology/path and don't mix. It keeps things clear. If you want to use the tx name space also use the aop namespace they fit together.

          Your configuration remove the txAdvisor, after changing the PriceHandler clas inject the dao.

          Code:
          <bean id="priceDAO" class="com.databusiness.dbuweb.dao.PriceDAO">
             <constructor-arg ref="dataSource"/>
          </bean>
          
          <bean id="priceHandler" class="com.databusiness.dbuweb.business.PriceHandler">
            <property name="priceDao" ref="priceDAO"/>
          </bean>
          
          <tx:advice id="txAdvice" transaction-manager="txManager">
              <tx:attributes>
                  <tx:method name="*" rollback-for="DAOException"/>
              </tx:attributes>
          </tx:advice>
          
          <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource"/>
          </bean>
          
          <aop:config>
            <aop:pointcut id="businessMethod" expression="execution(* com.databusiness.dbuweb.business..*.*(..))"/>
            <aop:advisor id="transactions" pointcut-ref="businessMethod" advice-ref="txAdvisor"/>
          </aop:config>
          However I express again that I strongly recommend you read-up on the reference guide, and take a look at the samples shipped with spring

          Why i will run out of resources and performance quite quick? this applicationContext is not destroyed when the excectution of the action finish?
          Not with one http request, no imagine 25 httprequest, that would mean that 50 ApplicationContexts get created! Need I say more? From Struts you probably have access to the HttpServletRequest then use the RequestContextUtils or the WebApplicationContextUtilsto retrieve the ApplicationContext. That way you will only have 1 ApplicationContext for the duration of your application.

          If you want a tighter integration between Struts2 and Spring there are a lot of resource, Google is your friend. This and this might be an interesting read from the Struts site.

          A simple integration would/could be to implement the ServletRequestAware interface on your Struts2 actions, that way you have a ServletRequest and use that to retrieve the application context.

          Example

          Code:
          public class PriceHandlerAction implements ServletRequestAware {
            
            private final HttpServletRequest request;
            private final ApplicationContext context;
          
            public void setServletRequest(HttpServletRequest request) {
                this.request = request;
                this.context = RequestContextUtils.getWebApplicationContext(request);
            }
          
            public void handlePrice(Price price1, Price price2) {
              IPriceHandler ph = (IPriceHandler) ctx.getBean("priceHandler");
              ph.addPrices(price1,price2);
            }
          }
          Add the loading of the ApplicationContext to your web.xml


          Code:
          <context-param>
          	<param-name>contextConfigLocation</param-name>
          	<param-value>appContext.xml</param-value>
          	</param-value>
          </context-param>
          
          <listener>
          	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
          Not the most elegant solution (no injection into Struts (2) actions) but a workable one with some minor integration nontheless.
          Last edited by Marten Deinum; Sep 12th, 2007, 09:42 AM. Reason: Added another link

          Comment


          • #6
            Thanks again for you answer, Ill study all your sugestions, so i think I will asking some things in this learining process.

            Thanks.

            Comment


            • #7
              mdeinum, Im trying to solve the first problem, the application.context.

              In the example I post, I have the code for getting the application context in a simple class.

              Code:
              ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml");
              What happens if i make that class static with a static instance of applicationContext? is this okay?

              Other solution: All My actions implements actionBase class, so I can make actionBase implement ServletRequestAware as you suggest and thats all?


              Thanks.

              Comment


              • #8
                Other solution: All My actions implements actionBase class, so I can make actionBase implement ServletRequestAware as you suggest and thats all?
                Yes. You can then provide a getApplicationContext() method so that subclasses can access the context.

                What happens if i make that class static with a static instance of applicationContext? is this okay?
                Evil singleton, it would work. Why hack around trying to solve something which can be solved by using the normal building blocks...

                Comment


                • #9
                  Ok, I understand.

                  One more basic thing about this topic. In the second solution (Using singleton), I get the application context using:

                  ApplicationContext ctx = new FileSystemXmlApplicationContext("appContext.xml");

                  This Creates a new application context from my appContext.xml that ill use in the rest of my web app. But I has also defined in my web.config a listener:

                  <listener>
                  <listener-class>
                  org.springframework.web.context.ContextLoaderListe ner
                  </listener-class>
                  </listener>

                  so, I think, Spring will create another applicationContext. So Im going to have two? If I implment this solution (singleton), i have to remove this listener from web.config?

                  Thanks a lot. (this one is the last question I promise )

                  Comment


                  • #10
                    As I suggested you can use the singleton although I wouldn't advice it. You already have the ContextLoaderListener then also use it.

                    Your application context will be loaded at server startup, use that context. Implement the interface I pointed you to and implement the code I suggested you (Using the RequestContextUtils to get the application context). It saves you from a lot of trouble (instantiating the context yourself etc.) use the building blocks already available don't reinvent the wheel... Again...

                    Code:
                    public abstract class ActionBase implements ServletRequestAware {
                      
                      private ApplicationContext context;
                    
                      public void setServletRequest(HttpServletRequest request) {
                          this.context = RequestContextUtils.getWebApplicationContext(request);
                      }
                    
                      protected final ApplicationContext getApplicationContext() {
                         return this.context;
                      }
                    }
                    So no need for a Singleton. Throw it away, burry it deep very deep. In your other actions call getApplicationContext() instead of YourClass.getInstace().getApplicationContext();
                    Last edited by Marten Deinum; Sep 13th, 2007, 07:21 AM.

                    Comment


                    • #11
                      mdeinum thanks a lot for your help. Now I go for my initial problem... the transactions.

                      Comment


                      • #12
                        If you use this configuration, removed the creation of all the new application contexts and injecting the Dao then that should be solved too.

                        Comment


                        • #13
                          You are right ! ...

                          I tryed the same code for transaction than yesterday, but with the new implementation for getting the applicationContext, and works perfect !!

                          I think i must understand how and when beans are created by spring ....


                          Thanks a lot for your help.

                          Comment


                          • #14
                            Your welcome, glad that it works.

                            Checkout the sample applications and check the configurations of those. Also readup on the reference guide that should clear things up. You can always buy a book on Spring ofcourse.

                            Comment


                            • #15
                              wich one do you suggest?

                              Comment

                              Working...
                              X