Announcement Announcement Module
Collapse
No announcement yet.
JmsTemplate and Acknowledge Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JmsTemplate and Acknowledge

    Hey Spring Team first just want to say Spring really ROCKS!
    I am working on it with my first project and it really makes wiring up an application & testing easy...

    But now a question (potential enhancement) for JmsTemplate.
    Scenario:
    I have an application that uses JMS to manage a message as it is processed by various agents. An agent within the application needs to read a message off Q1, do some processing, write the message to Q2, then acknowledge the message from Q1.

    While I know that the resources for the JMS session are locked until the ack is sent (and the time here is dependent on the processing we do) this application needs to make sure that messages are not lost as they move from one Q to another. If the system were to have any issues/crash during processing we would lose the message since it was not on any of the Qs.

    JmsTemplate:
    I started looking at using Spring's JmsTemplate to handle our JMS needs. I noticed however that this class does not provide any public methods to acknowledge (or commit/rollback) a message even though the user of the class can call setSessionTransacted or setSessionAcknowledgeMode.

    When I looked at the source code for JmsTemplate I noticed that the variable used by setSessionAcknowledgeMode (sessionAcknowledgeMode) is not used to determine when or if to call the Message.acknowledge method. Instead the doReceive method of JmsTemplate calls the Message.acknowledge after a successful consumer.receive() call.

    In our scenario above the message would be acknowledged off Q1 before being processed or written to Q2 (even when setting CLIENT_ACKNOWLEDGE) and would leave the potential of losing the message in our given scenario.

    I think that this topic in the Spring forums started to address this problem, seems that sessionAcknowledgeMode was going to be checked and the call to Message.acknowledge() was going to be called when set to CLIENT_ACKNOWLEDGE to bypass a problem calling acknowledge when setting mode to AUTO_ACKNOWLEDGE in WebSphereMQ.
    http://forum.springframework.org/vie...ht=acknowledge

    However I think that if sessionAcknowledgeMode is set to CLIENT_ACKNOWLEDGE then the JmsTemplate should not call message.acknowledge at all but provide the capability of the user of JmsTemplate to call a public method that does this.

    What do you think?

    Thanks,
    Mike

  • #2
    I have a similar scenario and would like to find a solution in Spring. How can I make it work with current Spring JMS support? Or I have to go back to traditional JMS.

    Thanks,
    Missy

    Comment


    • #3
      consuming and publishing with Spring

      I'd recommend you use Jencks with Spring

      http://jencks.org/

      You can then setup Jencks to do the inbound message consumption for you...

      http://jencks.org/Message+Driven+POJOs

      which has the added benefit of performing connection, session & thread pooling, handling concurrent processing and exception handling and it can work with regular JMS transactions or with full XA if you need it.

      Then use the outbound pooling of Jencks...
      http://jencks.org/Outbound+JMS

      along with, if you wish, the JmsTemplate helper class. Then you can get inbound & outbound messaging all in a single JMS transaction done for you declaratively - or you can move to full XA if you need it (e.g. if you are using 2 different JMS providers or you want to include some JDBC operations in the XA).

      Comment


      • #4
        use JmsTransactionManager

        Hi,

        I had a similar problem, but figured out that jmsTemplate.receive() was opening and closing the session for me.

        once the session is closed, you can't message.acknowledge(), it's too late.

        you have 2 options:
        • write your own SessionCallback implementation (look in jmsTemplate for examples) and insert your code and your msg.acknowledge() before returning from your SessionCallback.doInJms() implementation
        • use JmsTransactionManager


          Code:
          <!-- for a queueConnectionFactory with jms 1.0 -->
          <bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager102">
          		<property name="connectionFactory">
          			<ref bean="jmsQueueConnectionFactory"/>
          		</property>
          		<property name="pubSubDomain">
          			<value>false</value>
          		</property>
          	</bean>

          Code:
          JmsTransactionManager transactionManager = (JmsTransactionManager ) appContext.getBean("jmsTransactionManager");
          
          TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
            Message msg = jmsTemplate.receive(queueName);
            if (msg != null) {
              // do stuff...
              msg.acknowledge(); //session is still open within the transaction
            }
          transactionManager.commit(status);

        Personnaly I prefer the transactionManager approach

        cheers,

        Patrick

        Comment


        • #5
          I have below scenario.

          where in if the receiver/consumer is not able to process a message even after number of re-trys, the message should be moved to DLQ, Which can be moved back to main queue once the issue with Receiver/Consumer is fixed.

          I followed transactionManager approach. But still I see if the receiver/consumer doesn't have authority to acknowledge the message.[Due to exceptional condition] the message is not re-deliver again [Currently the number of re-try's is 6].

          Using ibm websphere mq

          I tried looking into the Spring code.

          Code:
          JmsResourceHolder resourceHolder =(JmsResourceHolder) TransactionSynchronizationManager.getResource(getC onnectionFactory());
          if (resourceHolder != null && resourceHolder.hasTimeout()) {
          timeout = Math.min(timeout, resourceHolder.getTimeToLiveInMillis());
          }
          Message message = doReceive(consumer, timeout);// @ this stage the message is removed from the Queue.
          if (session.getTransacted()) {
          // Commit necessary - but avoid commit call within a JTA transaction.
          if (isSessionLocallyTransacted(session)) {
          // Transacted session created by this template -> commit.
          JmsUtils.commitIfNecessary(session);
          }
          }
          else if (isClientAcknowledge(session)) {
          // Manually acknowledge message, if any.
          if (message != null) {
          message.acknowledge();
          }
          }
          after doReceive(..) the message is removed from the queue, I expect the message to remain in the queue, until the Client/Receive acknowledges the message.

          I have configured sessionTransacted & CLIENT_ACKNOWLEDGE to true [ even tough its not considered]

          Code:
          <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
                  <property name="connectionFactory" ref="authorizedConnectionFactoryWeb" />
                  <property name="receiveTimeout" value="${queue.timeout}" />
                  <property name="sessionTransacted" value="true" />
                  <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />        
           </bean>
          Below is the code in onMessage method
          Code:
          final Message message = (Message) jmsTemplate.receive(queueName);
                  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
                  if (null != message) {
                      try {
                          LOG.info("Batch Process: Started Processing the Request");
                          LOG.info("Message Details:",message.toString());
                          LOG.info("JMS Template Details:",jmsTemplate.toString());
                          System.out.println("Testing");
                          handleMessage(message);
                          message.acknowledge();
                          transactionManager.commit(status);
                      } catch (Exception exception) {
                          LOG.error("Faliled to process request: ", exception);
                          transactionManager.rollback(status);
                      }            
                  }
          Thank you for the Help.

          Comment


          • #6
            Please do not revive old threads; especially 9 year old threads that are unrelated to the subject at hand.

            The message is (temporarily) removed from the queue by the broker but is only finally removed on the transaction commit; if the transaction is rolled back the broker puts the message back on the queue.

            Comment

            Working...
            X