0

I have two applications Apple and Pear that uses the above class to listen on a configured JMS queue in WildFly ( 10.1.0 ). The Spring configuration is shown below.

<bean id="appleMessageListenerContainer"
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    depends-on="transactionManager">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destination" ref="outQueue" />
    <property name="destinationResolver" ref="jmsDestinationResolver" />
    <property name="messageListener" ref="AppleMessageListener" />
    <property name="messageSelector" value="ID='APPLE_ID'" />
    <property name="transactionManager" ref="transactionManager" />
</bean>

<bean id="pearMessageListenerContainer"
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    depends-on="transactionManager">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destination" ref="outQueue" />
    <property name="destinationResolver" ref="jmsDestinationResolver" />
    <property name="messageListener" ref="PearMessageListener" />
    <property name="messageSelector" value="ID='PEAR_ID'" />
    <property name="transactionManager" ref="transactionManager" />
</bean>

The expected process is as follows :- Apple application listener ( AppleMessageListener ) will read a message from a "outQueue" JMS queue. The message is updated and the AppleMessageListener will write the message out to the "outQueue" with the senderId set to "PEAR_ID", so that the PearMessageListener will read the message. The AppleMessageListener will wait for a response from Pear application on different "inQueue" or timeout

Extarct from the following link :-

https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q032250_.htm

If an application sends a message within a transaction, the message 
is not delivered to its destination until the transaction is committed. 
This means that an application cannot send a message and receive a reply 
to the message within the same transaction.

This is exactly my situation. However, I am not able to find a solution to this that I can understand.

I would very much appreciate for suggestions to over come this issue I am facing.

Thank you for your help.

Pete

Pete Long
  • 107
  • 2
  • 11
  • As I explained in [this answer](https://stackoverflow.com/questions/50544584/sping-jms-listener-blocking-another-listener-from-reading-jms-message/50544885#comment88102464_50544885) you should use a non-transactional JmsTemplate. – Gary Russell May 28 '18 at 19:44
  • presumably with this approach, any errors encountered, the message does not get rolled back to the queue. If so this is a requirement for our application. Will try to understand and look into non-transactional JmsTemplate option .. if you are aware of example of this approach, I would very appreciate this. thank you. – Pete Long May 29 '18 at 05:02
  • @PeteLong how did you resolve this issue? – Ronak Patel Sep 30 '20 at 13:53

2 Answers2

1

You could use Spring Integration's JMS outbound gateway which deals with this issue.

EDIT

Here's a Spring Boot application using Spring Integration; hopefully it's self-explanatory...

@SpringBootApplication
public class So50572316Application {

    public static void main(String[] args) {
        SpringApplication.run(So50572316Application.class, args);
    }

    @Bean
    public ApplicationRunner runner(JmsTemplate template) {
        return args -> {
            MessagePostProcessor mpp = m -> {
                m.setStringProperty("ID", "APPLE_ID");
                return m;
            };
            template.convertAndSend("outQueue", "foo", mpp);
            template.convertAndSend("outQueue", "fail", mpp);
        };
    }

    @Bean
    public IntegrationFlow appleFlow(ConnectionFactory connectionFactory) {
        return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(connectionFactory)
                .destination("outQueue")
                .configureListenerContainer(c -> c.messageSelector("ID='APPLE_ID'")))
            .handle("appleHandler", "handle")
            .handle(Jms.outboundGateway(new CachingConnectionFactory(connectionFactory))
                    .requestDestination("outQueue")
                    .headerMapper(headerMapper()))
            .handle("resultHandler", "handle")
            .get();
    }

    @Bean
    public AppleHandler appleHandler() {
        return new AppleHandler();
    }

    @Bean
    public ResultHandler resultHandler() {
        return new ResultHandler();
    }

    @Bean
    public IntegrationFlow pearFlow(ConnectionFactory connectionFactory) {
        return IntegrationFlows.from(Jms.inboundGateway(connectionFactory)
                .destination("outQueue")
                .configureListenerContainer(c -> c.messageSelector("ID='PEAR_ID'")))
            .handle("pearHandler", "handle")
            .get();
    }

    @Bean
    public PearHandler pearHandler() {
        return new PearHandler();
    }

    private JmsHeaderMapper headerMapper() {
        return new DefaultJmsHeaderMapper() {

            @Override
            public void fromHeaders(MessageHeaders headers, Message jmsMessage) {
                super.fromHeaders(headers, jmsMessage);
                try {
                    jmsMessage.setStringProperty("ID", "PEAR_ID");
                }
                catch (JMSException e) {
                    e.printStackTrace();
                }
            }

        };
    }

}

class AppleHandler {

    @ServiceActivator
    public String handle(String in) {
        System.out.println("Apple:" + in);
        return in.toUpperCase();
    }

}

class ResultHandler {

    @ServiceActivator
    public void handle(String result) {
        if ("FAILFAIL".equals(result)) {
            throw new RuntimeException("testRollback");
        }
        System.out.println("Result:" + result);
    }

}

class PearHandler {

    @ServiceActivator
    public String handle(String in) {
        System.out.println("Pear:" + in);
        return in + in;
    }

}

and

Apple:foo
Pear:FOO
Result:FOOFOO
Apple:fail
Pear:FAIL
2018-05-29 09:53:31.217  WARN 98472 --- [erContainer#0-1] o.s.j.l.DefaultMessageListenerContainer  : Execution of JMS message listener failed, and no ErrorHandler has been set.

org.springframework.messaging.MessageHandlingException: nested exception is java.lang.RuntimeException: testRollback
...
Apple:fail
Pear:FAIL
2018-05-29 09:53:32.224  WARN 98472 --- [erContainer#0-1] o.s.j.l.DefaultMessageListenerContainer  : Execution of JMS message listener failed, and no ErrorHandler has been set.

org.springframework.messaging.MessageHandlingException: nested exception is java.lang.RuntimeException: testRollback
...
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • if you are aware of example of this approach, I would very much appreciate this. thank you. Spent the whole weekend into this with no joy. Thank you for your help. – Pete Long May 29 '18 at 05:06
  • See the edit to my answer for how to do it with Spring Integration. – Gary Russell May 29 '18 at 13:56
  • Hi Gary, Apologises for the delayed reply. Thank you for the sample example, which to be honest, I would not have got close too. Hope you didn't have to go too much out of your way. Our requirements have changed, in so much as we now no longer need to read, write msg and wait for a response from jms queue - phew !!! . We are going back to the tried and trusted ( and tested ) approach of write msg and wait for a response. Unfortunately, I was not able to implement your suggested solution, but of course, have book marked this in case I need to refer back to it. – Pete Long Jun 01 '18 at 05:26
  • Whilst this issue has been a pain, it has been a very good learning chance for me and all thanks to you. So very much appreciate it. So thank you again. Pete – Pete Long Jun 01 '18 at 05:26
0

I resolved by using non-Transacted JMS sessions by creating JmsListenerContainerFactory manually.

        factory.setTransactionManager(null);
        factory.setSessionTransacted(false);
    @Bean
    public ConnectionFactory connectionFactory() throws Exception {
        MQConnectionFactory connectionFactory = new MQConnectionFactory();
        connectionFactory.setHostName(mqProperties.getHostName());
        connectionFactory.setPort(mqProperties.getPort());
        connectionFactory.setQueueManager(mqProperties.getQueueManager());
        connectionFactory.setChannel(mqProperties.getChannel());
        connectionFactory.setAppName(mqProperties.getAppId());
        connectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
        return connectionFactory;
    }

    @Bean
    public JmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, MQErrorHandler errorHandler) throws Exception {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setTransactionManager(null);
        factory.setSessionTransacted(false);
        factory.setErrorHandler(errorHandler);
        return factory;
    }
Ronak Patel
  • 3,819
  • 1
  • 16
  • 29