Announcement Announcement Module
Collapse
No announcement yet.
Finally dumping the J2EE container - is JOTM mature enough? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Finally dumping the J2EE container - is JOTM mature enough?

    Our code is now in a state (thanks to Spring) where the J2EE container could conceivably be dumped. Since we use multiple datasources (DBs) plus JMS in transactions, this would require us to use an open source JTA implementation (JOTM, of course). We would then use a simpler servlet container instead of a full J2EE container (we are thinking of Tomcat). Before we go down this path, I'm wondering if anyone who has experience using JOTM with Spring could share how well this works in practice? What are the "gotchas" we need to watch out for? I should mention that we are using ActiveMQ as our JMS implementation, so we can indeed move away from the J2EE container while still using JMS.
    Also, I would like to avoid having to setup JOTM at the Tomcat level. It would be great if it was configured completely within Spring. We would also configure our datasources completely in Spring (via XAPool) - the goal being to deploy a fully self contained .war to Tomcat without messing with any more Tomcat configuration than absolutely necessary. In fact, if we can avoid JOTM and datasource configuration at the Tomcat level, then we can hopefully completely eliminate any and all JNDI.
    Oh, and we use Spring's Hibernate support as well (Hibernate 3) - so if anyone has observed harmful interactions between these components, I would certainly be interested in knowing that.

    Thanks,
    Andy

  • #2
    Hi Andy,

    We are currently developing an application which uses JOTM, ActiveMQ and a single DB (using XAPool), running under Tomcat, which does not use JNDI for configuration. It took quite some time to get everything up and running, but in the end everything works like a charm (but so far only in a test environment, we have yet to go in production).

    If you want I can mail you the relevant portions of the context XML files, let me know if you're interested.

    Regards,

    Joris

    Comment


    • #3
      As I've seen from the forum quite a lot of people had problems configuring JOTM - can you please post your configuration on the WIKI? Thanks!

      Comment


      • #4
        Sure, no problem! I'll post a message on these forums when it's up (probably within the next 24 hours).

        Regards,

        Joris

        Comment


        • #5
          Hi Costin,

          I tried to post this on the Spring Wiki, but I can't find a way to create an account there. Would you mind posting the cookbook below there for me?

          Thanks,

          Joris

          This cookbook describes how to setup global transactions using Spring's JtaTransactionManager, JOTM, a RDBMS and the ActiveMQ JMS provider. Since all configuration takes place in Spring context files (so no need for JNDI), and JOTM is a standalone global transaction manager, this setup does not require a full blown J2EE container. The setup described below has been tested using Tomcat and MySQL.

          The solution described below is a result of a few hours of trial-and-error, so better solutions might very well exist. If so, please update this page!

          First, set up a JOTM instance, which is easily achieved using the Spring JotmFactoryBean:

          Code:
          <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
          The resulting bean should then be injected into the UserTransaction property of a JtaTransactionManager:

          Code:
          <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
          	<property name="userTransaction"><ref local="jotm"/></property>
          </bean>
          Now, we will set up two transactional resources, a RDBMS and a JMS provider. For this example we will use ActiveMQ.

          We will use Enhydra's XAPool for providing XA connection pooling:

          Code:
          <bean id="dataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
          	<property name="dataSource">
          		<bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
          			<property name="transactionManager" ref="jotm" />
          			<property name="driverName" value="$&#123;jdbc.driverClassName&#125;" />
          			<property name="url" value="$&#123;jdbc.url&#125;" />
          		</bean>
          	</property>
          	<property name="user" value="$&#123;jdbc.username&#125;"/>
          	<property name="password" value="$&#123;jdbc.password&#125;"/>
          </bean>
          Note that a reference to the JOTM bean is injected into the transactionManager property of the StandardXADataSource.

          Next, we will setup a JMS connection factory. ActiveMQ comes with a connection factory (org.activemq.ActiveMQXAConnectionFactory) which provides XA capabale JMS connections. However, these connections do not automatically take part in Spring managed JTA transactions.

          The solution to this problem is to wrap the ActiveMQXAConnectionFactory with a PooledSpringXAConnectionFactory. The code for the latter was kindly provided by Andy DePue, and can be downloaded here.

          Code:
          <bean id="connectionFactory" class="com.marathon.jms.PooledSpringXAConnectionFactory">
          	<property name="connectionFactory">
          		<bean class="org.activemq.ActiveMQXAConnectionFactory">
          			<property name="brokerURL" value="$&#123;jmseventbridge.brokerURL&#125;" />
          		</bean>
          	</property>
          	<property name="transactionManager" ref="jotm"/>
          </bean>
          Note that again, a reference to the JOTM bean is injected into the transactionManager property of the PooledSpringXAConnectionFactory. (BTW, if you need to set a Client ID on the JMS connections, you should do this through the clientID property of the ActiveMQXAConnectionFactory.)

          As an example of defining a JMS destination purely in Spring, the following fragment sets up an ActiveMQTopic:

          Code:
          <bean id="destination" class="org.activemq.message.ActiveMQTopic" autowire="constructor">
          	<constructor-arg>
          		<value>myTopicName</value>
          	</constructor-arg>
          </bean>
          Finally, to use the convenient Spring JmsTemplate class, we set it up as follows:

          Code:
          <bean id="myJmsTemplate" class="nl.chess.it.jmseventbridge.jmssupport.DurableJmsTemplate">
          	<property name="connectionFactory" ref="connectionFactory" />
          </bean>
          And that's it! Messages send or received through the JmsTemplate will now automatically take part in Spring managed JTA transactions.

          Comment


          • #6
            Strange - anyways I have used my user and published this thread on the WIKI with some minor formatting adjustments. You can see the page here:
            http://opensource.atlassian.com/conf...A+++JOTM+usage

            Comment


            • #7
              Great, thanks. Would you be so kind to fix two errors in the text? First, change the value of brokerURL property to ${brokerURL} (the value that's there now is sort of specific to our application).

              Also, in the final context fragment, could you change the class to org.springframework.jms.core.JmsTemplate? The DurableJmsTemplate class which is there now is also specific to our application.

              Thanks again,

              Joris

              Comment


              • #8
                done. try to register the new user and in case it still doesn't work post your error on the meta forum or on the mailing list.

                Comment


                • #9
                  HELP!!! I'v Got an Error!HELP!!

                  My config file:
                  Code:
                  <?xml version="1.0" encoding="UTF-8"?>
                  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
                  <beans>
                  
                  	<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
                  	
                  	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
                     		<property name="userTransaction"><ref local="jotm"/></property>
                  	</bean>
                  	<bean id="connectionFactory" class="com.marathon.jms.PooledSpringXAConnectionFactory">
                  	   <property name="connectionFactory">
                  	      <bean class="org.activemq.ActiveMQXAConnectionFactory">
                  	         <property name="brokerURL" value="tcp&#58;//localhost&#58;61616" />
                  	      </bean>
                  	   </property>
                  	   <property name="transactionManager" ref="jotm"/>
                  	</bean>
                  	
                  	<bean id="myJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
                     		<property name="connectionFactory" ref="connectionFactory" />
                  	</bean>
                  	
                  	<bean id="destination" class="org.activemq.message.ActiveMQTopic" autowire="constructor"> 
                  	   <constructor-arg> 
                  	      <value>Hello1.Topic</value> 
                  	   	</constructor-arg> 
                  	</bean>
                  </beans>
                  My program is
                  Code:
                  package com.jl.jms;
                  
                  import javax.jms.JMSException;
                  import javax.jms.Message;
                  import javax.jms.Session;
                  import javax.jms.Topic;
                  
                  import org.springframework.context.ApplicationContext;
                  import org.springframework.context.support.FileSystemXmlApplicationContext;
                  import org.springframework.jms.core.JmsTemplate;
                  import org.springframework.jms.core.MessageCreator;
                  
                  public class SendMessagePojo4 &#123;
                  
                  	/**
                  	 * @param args
                  	 */
                  	public static void main&#40;String&#91;&#93; args&#41; &#123;
                  		// TODO Auto-generated method stub
                  		ApplicationContext cxt = new FileSystemXmlApplicationContext&#40;"jotmsend.xml"&#41;;
                  		Topic t = &#40;Topic&#41;cxt.getBean&#40;"destination"&#41;;
                  		JmsTemplate jms = &#40;JmsTemplate&#41; cxt.getBean&#40;"myJmsTemplate"&#41;;
                  		jms.send&#40;t,new MessageCreator&#40;&#41; &#123;				
                  			public Message createMessage&#40;Session s&#41; throws JMSException &#123;
                  				return s.createTextMessage&#40;"Hello"&#41;;
                  			&#125;		
                  		&#125;
                  				
                  		&#41;;
                  	&#125;
                  
                  &#125;
                  got error like this:
                  Code:
                  Exception in thread "main" org.springframework.jms.UncategorizedJmsException&#58; Uncategorized exception occured during JMS processing; nested exception is javax.jms.JMSException&#58; Session's XAResource has not been enlisted in a distributed transaction.; nested exception is javax.jms.JMSException&#58; Session's XAResource has not been enlisted in a distributed transaction.
                  javax.jms.JMSException&#58; Session's XAResource has not been enlisted in a distributed transaction.
                  	at org.activemq.ActiveMQXASession.doStartTransaction&#40;ActiveMQXASession.java&#58;110&#41;
                  	at org.activemq.ActiveMQSession.send&#40;ActiveMQSession.java&#58;1366&#41;
                  	at org.activemq.ActiveMQMessageProducer.send&#40;ActiveMQMessageProducer.java&#58;426&#41;
                  	at com.marathon.jms.PooledProducer.send&#40;PooledProducer.java&#58;81&#41;
                  	at com.marathon.jms.PooledProducer.send&#40;PooledProducer.java&#58;62&#41;
                  	at org.springframework.jms.core.JmsTemplate.doSend&#40;JmsTemplate.java&#58;658&#41;
                  	at org.springframework.jms.core.JmsTemplate.doSend&#40;JmsTemplate.java&#58;644&#41;
                  	at org.springframework.jms.core.JmsTemplate$2.doInJms&#40;JmsTemplate.java&#58;620&#41;
                  	at org.springframework.jms.core.JmsTemplate.execute&#40;JmsTemplate.java&#58;582&#41;
                  	at org.springframework.jms.core.JmsTemplate.execute&#40;JmsTemplate.java&#58;594&#41;
                  	at org.springframework.jms.core.JmsTemplate.send&#40;JmsTemplate.java&#58;618&#41;
                  	at com.jl.jms.SendMessagePojo4.main&#40;SendMessagePojo4.java&#58;23&#41;
                  What's wrong??

                  Comment


                  • #10
                    It looks like ActiveMQ's XA session is requiring a transaction, and you ran your example outside of a transaction. Try something like this (I haven't even tried to compile this, so you might need to tweak it):

                    Code:
                    ...
                       public static void main&#40;String&#91;&#93; args&#41; &#123;
                          // TODO Auto-generated method stub
                          ApplicationContext cxt = new FileSystemXmlApplicationContext&#40;"jotmsend.xml"&#41;;
                    
                          // The next two lines are new
                          PlatformTransactionManager ptm = &#40;PlatformTransactionManager&#41;cxt.getBean&#40;"transactionManager"&#41;;
                          TransactionTemplate tt = new TransactionTemplate&#40;ptm&#41;;
                    
                          Topic t = &#40;Topic&#41;cxt.getBean&#40;"destination"&#41;;
                          JmsTemplate jms = &#40;JmsTemplate&#41; cxt.getBean&#40;"myJmsTemplate"&#41;;
                    
                          // The tt.execute&#40;...&#41; &#123; &#125; is new
                          tt.execute&#40;new TransactionCallbackWithoutResult&#40;&#41; &#123;
                            protected void doInTransactionWithoutResult&#40;TransactionStatus status&#41; &#123;
                              jms.send&#40;t,new MessageCreator&#40;&#41; &#123;            
                                 public Message createMessage&#40;Session s&#41; throws JMSException &#123;
                                    return s.createTextMessage&#40;"Hello"&#41;;
                                 &#125;      
                              &#125;&#41;;
                            &#125;
                          &#125;&#41;;
                       &#125; 
                    ...
                    As a workaround - if you want to use JMS outside of a transaction, then you may have to set up an alternative connection factory in your configuration using the plain ActiveMQ PooledConnectionFactory (org.activemq.pool.PooledConnectionFactory, I believe) instead of the XA one. Use the plain one outside of transactions and the XA one inside of transactions. This may be a bug in the XA pooled connection factory, so I'll look into it..

                    - Andy

                    Comment


                    • #11
                      Thank you Andy.
                      I have tried your program.it is no problem.The problem is :
                      Is your code a XA Transaction?
                      I tried my codes just from:
                      http://opensource2.atlassian.com/con...A+++JOTM+usage

                      Is there error in my code?

                      Comment


                      • #12
                        Originally posted by zjnbshifox
                        Thank you Andy.
                        I have tried your program.it is no problem.The problem is :
                        Is your code a XA Transaction?
                        I tried my codes just from:
                        http://opensource2.atlassian.com/con...A+++JOTM+usage

                        Is there error in my code?
                        I'm not sure I understand your question... my code above uses Spring's TransactionTemplate to surround the JMS code in a transaction. In this example, the TransactionTemplate uses Spring's JtaTransactionManager, which in turn uses JOTM, which provides XA transaction services. So, my code drives the transaction using Spring abstractions, which then use JOTM to provide the actual underlying transaction. Since JOTM is XA, then I think the answer to your question is, "yes, my code is using an XA transaction". As I mention in the message above, it looks like you must be sure that you always call that JmsTemplate within a transaction.

                        Comment


                        • #13
                          If I want make some Database operation in the same XA transaction,I must give these codes in the tt.execute{......} . Is that right??

                          Comment


                          • #14
                            Originally posted by zjnbshifox
                            If I want make some Database operation in the same XA transaction,I must give these codes in the tt.execute{......} . Is that right??
                            That's right, you must put the database operation in the same "execute" (or use Spring's declarative transaction ability).

                            - Andy

                            Comment

                            Working...
                            X