XA: The conclusion of the example

Onward!

As we said last time— In our quest to produce a working example of XA transactions, our next step is to use the DAO class to write a record to disk; internally it uses Hibernate to do this.

The Convenience of the MessageAdapter

It bugged me that my domain code was in a class named MessageListener that extended javax.jms.MessageListener, and that the method name was onMessage, taking a javax.jms.Message as a parameter.  It was too tied into the JMS stuff.  But by making this little change in my Spring bean file:

<bean id="messageProcessor" class="com.ontsys.dmn.MessageProcessor" />

<jms:listener-container transaction-manager="transactionManager" connection-factory="queueConnectionFactory" acknowledge="transacted">
<jms:listener destination="myTest.Queue" ref="messageProcessor" <strong>method="processIt"</strong> />
</jms:listener-container>

…I was able to change my class from this:

public class MessageListener extends javax.jms.MessageListener {
  public void onMessage(Message message) {
  ...

to this:

public class MessageProcessor {
  public void processIt(String text) {
  ...

(For production-grade use we’d have to think through the exception-swallowing semantics, but for this example it’s fine!)

NullPointerException

Now I have this:

    public void processIt(String text) {
        SimplePerson person = instantiateAndFillInPerson(text);
        SimplePersonDAO dao = new SimplePersonDAO();
        dao.create(person);
        ...
    }
    private SimplePerson instantiateAndFillInPerson(String firstName) {
        SimplePerson person = new SimplePerson();
        person.setDateOfBirth(new Date(0));
        person.setSsn("123-00-1234");
        person.setFirstName(firstName);
        person.setLastName("Sudbury");
        return person;
    }

But when I call dao.create(person), I get a NullPointerException on the line where the create method tries to use its sessionFactory.  What’s wrong??

Forgot to inject

Hah! Looking over the code, I notice that in my processIt method I’m creating my own instance of a SimplePersonDAO:

SimplePersonDAO dao = new SimplePersonDAO();

Of course the sessionFactory property won’t be filled in if I do it like this.  I need to inject the SimplePersonDAO instance into my MessageProcessor.

It doesn’t error, but does it work?

Once I do this, the NullPointerExceptions go away.  So let’s see if anything’s being stored to the database

I can go to the Database Home Page:

Once there, I log in as the Oracle user I set up previously, and go to Object Browser -> SIMPLEPERSON table, Data tab.

It works, it really seems to work!

I sent three messages to the queue and ended up with the following data in the table:

My first message was “hi yall”.  My code filled in the other fields of the DAO with data and wrote it to disk as ID #1 (each time my .war project loads it clears out the SIMPLEPERSON table by virtue of this setting in the Hibernate SessionFactory bean: <prop key="hibernate.hbm2ddl.auto">create</prop>).

My second message was “whazzup”. It wrote successfully as ID #2.

The third message was “boom”.  Now, I have the processIt method set up to throw a RuntimeException every other time it receives a message containing the string “boom”.  Before that exception was thrown though, I had the DAO write the person to the database.  You can see that the record did not end up persisting to disk — the transacton rollback not only affected the JMS message receive, but also the disk write, (all as it should be!)

Since the JMS message receive rolled back due to the runtime exception, the listener immediately received the same “boom” message again, but this time did not throw an exception.  Notice that the record correctly persisted to disk as ID #4.

Wahoo!

Update 8/1/2008: But also see the post-conclusion addendum…

Advertisements

, ,

  1. #1 by Spring User on February 11, 2009 - 6:18 am

    Hi,

    I am not able to make this work. I have exactly the same scenario which you had mentioned (receive message using DMLC and insert into DB). If the DB insert fails then the message should rollback ie remain back in the queue. I use Weblogic + MQ Series 6 and Spring.

    Here is my piece of code for DMLC

    public void onMessage(Message message)
    {
    try {
    String msgId = null;
    if (log.isDebugEnabled())
    {
    msgId = message.getJMSMessageID();
    log.debug("onMessage invoked : " + message);

    }
    sendToTest();
    throw new DataAccessLayerException("Simulating Rollback by throwing Exception !!");
    }
    catch(DataAccessLayerException e)
    {

    e.printStackTrace();
    throw new RuntimeException("Simulating Rollback by throwing JMSException !!");

    }
    catch(JMSException e)
    {
    throw new RuntimeException("Simulating Rollback by throwing JMSException ");
    }
    catch(Exception e)
    {

    throw new RuntimeException("Simulating Rollback by throwing JMSException );
    }
    }

    As you had mentioned, i do an insert in the DB layer (which i call from my listener onMessage method) and just throw an exception in the last line of my onMessage method to check whether the transaction is rolled back.

    Unfortunately, in my case, while the DB insert is rolled back, the message disappears from the queue. I have used XADatasource and XA connectionFactory and use WeblogicJTATransactionManager.
    Here is a piece from spring config


    What mistake am i committing?

    Thanks

  2. #2 by Spring User on February 11, 2009 - 6:22 am

    Sorry !!! i missed the Spring config part.

  3. #3 by Spring User on February 11, 2009 - 7:29 am

    Hi,
    Not sure why my spring config is not uploading to this site.

  4. #4 by danielmeyer on February 11, 2009 - 11:14 am

    Spring User (comment #3) – Raw XML won’t show up right by default, but I bet if you surround it with
    *sourcecode language=’xml’**
    */sourcecode** then it will show up right.

    Note: replace ‘*’ with ‘[‘ and ‘**’ with ‘]’

  5. #5 by Spring User on February 12, 2009 - 2:19 am

    Hi,

    Thanks. Here is my Spring config xml

    I have a XADatasource and XAConnectionfactory defined in my weblogic console.

    Can you tell me where i am going wrong and why XA is not working in this case?

    Thanks

  6. #6 by Spring User on February 12, 2009 - 4:09 am

    Hi,
    Not sure why its still not getting uploaded. Anyway,
    my spring config is very simple.
    These are the entries in my spring config.
    1) tx:jta-transaction-manager entry which will give me WeblogicJTATransactionManager instance
    2) a DMLC having
    property=”connectionFactory” ref=”jmsXAConnectionFactory”
    property=”transactionManager” ref=”transactionManager”
    property name=”messageListener” ref=”jmsListener”
    property=”sessionTransacted” value=”false”

    where jmsListener is my MessageListener class

    3) Transaction AOP entry
    aop:config
    aop:pointcut id=”serviceMethods” expression=”execution(* *..*Listener.*(..))”
    aop:advisor pointcut-ref=”serviceMethods” advice-ref=”txReqAdvice”
    aop:config

    tx:advice id=”txReqAdvice”
    tx:attributes
    tx:method name=”save*” propagation=”REQUIRED”
    tx:method name=”onMessage”propagation=”REQUIRED”
    tx:attributes
    tx:advice

    As i had mentioned earlier, I have a XADatasource and XAConnectionfactory defined in my weblogic console and i have already uploaded the piece of code in my earlier post.

    Can you point me on where i am making a mistake?

    FYI..i have just removed the” from the spring config entries

    Thanks

  7. #7 by danielmeyer on February 13, 2009 - 10:13 am

    Spring User,
    A couple of suggestions:
    1. You might try setting sessionTransacted=true in your DMLC. That was necessary for me with ActiveMQ, but I don’t know if will work with your setup.

    2. Use your debugger and set a breakpoint on your message listener’s onMessage() method, and when it hits that breakpoint look at the call stack. Make sure you see AOP transaction proxy stuff. If not, maybe your AOP advice isn’t working.

    (Your message listener is defined as a Spring bean, right? Otherwise the Spring AOP advice won’t apply to it)

  8. #8 by Spring User on February 16, 2009 - 3:39 am

    Hi DanielMeyer,

    Thanks for the reply.

    Its working now. I have not changed the settings. However, this seems to be working only with Weblogic Queues (XA enabled) and not with Websphere MQ even though i defined XA queue connection factories using JMSAdmin.bat provided by Websphere MQ.

    FYI…My message listener is a spring bean only which i have defined in my spring config.

    bean id=”jmsListener” class=”com.sample.JMSRequestListener”
    property name=”jndiTemplate” ref=”jndiTemplate”
    bean

    Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s