Will Lovett
Will Lovett

Reputation: 1251

Why does JMS deliver message twice?

I'm using JMS to perform some long-running import processes, but running into some duplicate message problems I don't quite understand.

The flow is as follows. Some backing bean code sends a message to a Message Bean. The message bean receives this message, polls a third party service, and then if there is new data to import, commits these rows into a database. The problem is that JMS is sending my message twice in instances where my import process is taking some time, approx 60 seconds or so. If there are no new rows to import and the process takes 30 seconds or so, the message is only sent once.

I thought it had something to do with the acknowledgement of the message receipt so the first thing I did (since the auto acknowledge wasn't working) was set the QueueSession to Session.CLIENT_ACKNOWLEDGE and put msg.acknowledge() in my onMessage method. Sadly, the messages were still being sent twice.

No error codes or Exceptions are thrown during any of this.

Here's the code.

In Backing Bean

public MyBackingBean(){
    try {
        InitialContext ctx = new InitialContext();
        qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
        qcon = qconFactory.createQueueConnection();
        qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

        queue = (javax.jms.Queue) ctx.lookup(IMPORT_QUEUE);
        qsender = qsession.createSender(queue);
        qsender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

        qcon.start();

    } catch (JMSException e) {
        System.out.println(e);
    } catch (NamingException e) {
        System.out.println(e);
    }

public void startImport() {
        try {
            // send a JMS message for the long running job
            MapMessage mapMessage = qsession.createMapMessage();
            mapMessage.setObjectProperty("ipAddress", "127.0.0.1");
            qsender.send(mapMessage);
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (Throwable te) {
            te.printStackTrace();
        }
}

In Import Message Bean

@Override
public void onMessage(Message msg) {
    try {
           // create an ADF Application Module
           // poll a third party for some data
           // copy these rows (if new) and then 
           // commit via the ADF Application Module
        }
    catch (Exception e){
       // no errors are being thrown
    }
}

For now, I'm disregarding the duplicate by checking to see if the message is a redelivery (msg.getJMSRedelivered()) and disregarding the duplicate, but I'm not too happy with the band-aid.

Does anyone have some pointers on this?

Upvotes: 1

Views: 4690

Answers (1)

RickF
RickF

Reputation: 36

Will, I can't be sure what's happening, but realize that, with AUTO_ACKNOWLEDGE, the acknowledgement doesn't actually happen until the thread returns from the onMessage() call. Therefore, if you are spending 60 seconds in your onMessage() call, I'm not surprised at the redelivery. With CLIENT_ACKNOWLEDGE, by calling msg.acknowledge(), you should be explicitly acknowledging the message. However, at what point are you doing that? After spending 60 seconds in onMessage()? Then you'd get the same behavior, I'd expect. You can call msg.acknowledge() as soon as you enter your onMessage() call, but realize that means the message won't be redelivered in the event that something later in your onMessage() method crashes. But it looks like you're not using persistent delivery anyway, so perhaps you don't care.

Here's a good reference;

http://www2.sys-con.com/itsg/virtualcd/Java/archives/0604/chappell/index.html#s1

Upvotes: 1

Related Questions