Search code examples
springjmswebsphereeclipselink

Spring, Websphere, JMS and Eclipselink Issue


I have an application that runs inside of Websphere, and I am having an issue with persisting JPA entities.

Previously, the application was setup with RESOURCE_LOCAL persistence units, with the Spring JpaTransactionManager, and transactions that were committed explicitly in code.

  TransactionStatus transactionStatus = transactionManager.getTransaction( new DefaultTransactionDefinition() );
  try {
     entityManager.persist( someJpaEntity );
  }
  catch( Exception exception ) {
     transactionManager.rollback( transactionStatus );
     throw exception;
  }
  try {
     transactionManager.commit( transactionStatus );
  }
  catch( TransactionException exception ) {
     exception
  }

I am working on an enhancement to the application that will allow calls through a Message Driven Pojo linked to a Websphere Queue. I was able to setup a configuration through spring that will allow my application to receive messages through a JMS queue. The spring config looks like:

<jee:jndi-lookup id="jmsConnectionFactory" jndi-name="QueueConnectionFactory"/>
<jee:jndi-lookup id="jmsQueue" jndi-name="DIQueue" />

<!-- A dynamic resolver -->
<bean id="jmsDestResolver" class="org.springframework.jms.support.destination.DynamicDestinationResolver"/> 

<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
   <property name="connectionFactory">
      <ref bean="jmsConnectionFactory"/>
   </property>
   <property name="destinationResolver">
      <ref bean="jmsDestResolver"/>
   </property>
</bean>

<bean id="messageListener" class="my.app.FileMessageListener" />

<bean id="exListener" class="my.app.JmsExceptionListener" />

<bean id="msgListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
  <property name="connectionFactory" ref="jmsConnectionFactory" />
  <property name="destination" ref="jmsQueue" />
  <property name="messageListener" ref="messageListener" />
  <property name="transactionManager" ref="transactionManager" />
  <property name="taskExecutor" ref="myTaskExecutor" />
  <property name="exceptionListener" ref="exListener" />
</bean>

<bean id="myTaskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
  <property name="workManagerName" value="wm/default" />
</bean> 

Not sure if there is an issue with my spring setup, but I do receive messages through my Active MQ broker, so that part I seem to be good with.

Now, the issue is, that when I get a message in through JMS, I would call the above code to insert the JPA entity. When the code would run, I would get the message "unable to commit a one phase resource in a two phase transaction", or something similar. What I came to understand is that the Spring JpaTransactionManager does not work with XA or JTA transactions.

So, I worked on moving to the Spring JtaTransactionManager.

I changed everything I Could think of over to use JTA, here is where I declare my transaction manager:

<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>

Updated my persistence XML:

<persistence-unit name="AppUnit" transaction-type="JTA">        
    <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
    <jta-data-source>java:APPDS</jta-data-source> 

And still, nothing works. My code runs without exception, but nothing gets persisted to the database. The message gets pulled off of the JMS Queue, but no data.

Any suggestions?


Solution

  • I finally got this working, and figured I would post the answer.

    There are actually 2 pieces to the puzzle.

    First, in Websphere, you need to go to your app server -> TransactionService, and check / enable the "Accept Heuristic Hazard" checkbox. That definitely helped. I am running WAS 7.

    The second thing, is you MUST set the property eclipselink.target-server on your persistence unit or your EntityManagerFactory.

    That second item definitely did the trick. I tested with the property and without it. Without it, nothing persists. With it, everything works fine.

    Here is my EntityManagerFactory, I am using a property placeholder to set the value of the eclipselink.target-server property:

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" /> 
        <property name="persistenceUnitName" value="MyUnit" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
                <property name="databasePlatform">          
                   <value>${app.databasePlatform}</value>
                </property>
                <property name="showSql">
                   <value>${app.showSql}</value>
                </property>             
            </bean>
        </property>
    
    <!-- THIS DID THE TRICK -->
        <property name="jpaPropertyMap">
            <map>
                <entry key="eclipselink.target-server" value="${app.targetServer}"/> 
             </map>
        </property>       
    </bean>