【熟能生巧】JMS吃消息小组件

在开发或测试中,我们有时候会遇到Queue/Topic被多余的消息堵塞的问题,这时候就需要把队列里的消息清理掉,然后新的消息才能进来。

有些JMS Provider有GUI可以查看,修改队列中的消息。有些则没有提供这样的功能。这就需要我们自己手动写程序做这件事了。

原理很简单,就是open a connection,然后正常查看Queue/Topic里面的消息,这些就被“吃”掉了。

核心部分是messageListenerContainer.start(),然后onMessage()就会收到信息。

Code

PART 1

import javax.jms.Message;
import javax.jms.MessageListener;

public class JmsHelperMessageListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        if (message != null) {
            JmsHelper.printInfo("Consumed message: " + message.toString());
        } else {
            JmsHelper.printInfo("Message is null");
        }
        return;
    }
}

PART 2

import javax.jms.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import org.springframework.jms.core.BrowserCallback;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.listener.DefaultMessageListenerContainer;

import java.util.Date;
import java.util.Enumeration;
import java.util.Scanner;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class JmsHelper {
    @Autowired
    private JmsTemplate jmsTemplate;
    @Autowired
    private Queue datasetQueueCUS;
    @Autowired
    private Topic datasetTopicCUS;

    int CONSUME_MSG_TIME_IN_SECOND = 5;

    public static void printInfo(String m) {
        System.out.println(m);
    }

    private void simpleSendToQueue(Queue queue, String msg) {
        this.jmsTemplate.send(queue, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                MapMessage mapMessage = session.createMapMessage();
                mapMessage.setJMSCorrelationID( UUID.randomUUID().toString() ) ;
                mapMessage.setStringProperty( "DATASET_ID",  msg); ;
                mapMessage.setStringProperty( "NAME",  msg) ;
                mapMessage.setStringProperty( "VALUE_DATE",  "2019") ;
                mapMessage.setStringProperty( "STATUS",  "T") ;
                return mapMessage;
            }
        });
        printInfo("Sent one msg, DESTINATION: " + queue + ", NAME: " + msg + ".");
    }

    private void simpleSendToTopic(Topic topic, String msg) {
        this.jmsTemplate.send(topic, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                MapMessage mapMessage = session.createMapMessage();
                mapMessage.setJMSCorrelationID( UUID.randomUUID().toString() ) ;
                mapMessage.setIntProperty( "DATASET_ID",  376480); ;
                mapMessage.setStringProperty( "STATUS",  "P") ;

                return mapMessage;
            }
        });
        printInfo("Sent one msg, DESTINATION: " + topic + ", NAME: " + msg + ".");
    }

    private void simpleBrowseQueue(Queue queue) {
        this.jmsTemplate.browse(queue, new BrowserCallback<Object>() {
            @Override
            public Object doInJms(Session session, QueueBrowser queueBrowser) throws JMSException {
                printInfo(queueBrowser.getQueue().toString());
                Enumeration e = queueBrowser.getEnumeration();
                int count = 0;
                if (e.hasMoreElements()) {
                    printInfo(formatMsgWithDes((Message) e.nextElement()));
                    count++;
                }
                while (e.hasMoreElements()) {
                    printInfo(formatMsgWithoutDes((Message) e.nextElement()));
                    count++;
                }
                printInfo("INFO - message count: " + count);
                queueBrowser.close();
                session.close();
                return null;
            }
        });
    }

    private void consumeAllMessagesInQueue(ApplicationContext context, Queue queue) {
        DefaultMessageListenerContainer messageListenerContainer = (DefaultMessageListenerContainer) context.getBean("jmsContainer");
        messageListenerContainer.setDestination(queue);
        messageListenerContainer.initialize();
        if (!messageListenerContainer.isRunning())
            messageListenerContainer.start();
        try {
            printInfo("start consuming "+ messageListenerContainer.getDestination() +" for " + CONSUME_MSG_TIME_IN_SECOND + " seconds...");
            TimeUnit.SECONDS.sleep(CONSUME_MSG_TIME_IN_SECOND);
        } catch(Exception e) {
            printInfo(e.getMessage());
        }
        printInfo("end consuming...");
        messageListenerContainer.stop();
    }

    private void consumeAllMessagesInTopic(ApplicationContext context, Topic topic) {
        DefaultMessageListenerContainer messageListenerContainer = (DefaultMessageListenerContainer) context.getBean("jmsContainer2");
        messageListenerContainer.setDestination(topic);
        messageListenerContainer.initialize();
        if (!messageListenerContainer.isRunning())
            messageListenerContainer.start();
        try {
            printInfo("start consuming "+ messageListenerContainer.getDestination());
        } catch(Exception e) {
            printInfo(e.getMessage());
        }
    }

    private String formatMsgWithDes(Message message) {
        StringBuilder outputMessage = new StringBuilder("");
        try {
            String JMSDestination = message.getJMSDestination().toString();
            outputMessage.append("JMSDestination: " + JMSDestination + "\n");
            outputMessage.append(formatMsgWithoutDes(message));
        } catch (JMSException e) {
            e.printStackTrace();
        }
        return outputMessage.toString();
    }

    private String formatMsgWithoutDes(Message message) {
        String outputMessage="";
        try {
            String JMSMessageID = message.getJMSMessageID();
            String JMSTimestamp = new Date(message.getJMSTimestamp()).toString();
            String JMSPriority = Integer.toString(message.getJMSPriority());
            String DATASET_ID = message.getStringProperty("DATASET_ID");
            String VALUE_DATE = message.getStringProperty("VALUE_DATE");
            String NAME = message.getStringProperty("NAME");
            String STATUS = message.getStringProperty("STATUS");
            String MKT = message.getStringProperty("MKT");
            outputMessage = "DATASET_ID: " + DATASET_ID + ", " +
                    "VALUE_DATE: " + VALUE_DATE + ", " +
                    "NAME: " + NAME + ", " +
                    "STATUS: " + STATUS + ", " +
                    "JMSMessageID: " + JMSMessageID + ", " +
                    "JMSTimestamp: " + JMSTimestamp + ", " +
                    "MKT: " + MKT + ", ";
        } catch (JMSException e) {
            e.printStackTrace();
        }
        return outputMessage;
    }

    private static void printInstruction() {
        printInfo("********************"
                + "\n" + "Enter the instruction number: ");
    }

    private static void printInstructionDetails() {
        printInfo("\n" + "0 - EXIT"
                + "\n" + "--------------------"
                + "\n" + "1 - [CUS][Queue] check all msg"
                + "\n" + "2 - [CUS][Queue] try consume msg (if msg is not received, try send one msg OR retry the action)"
                + "\n" + "3 - [CUS][Queue] send one test msg"
                + "\n" + "4 - [CUS][Topic] try check/consume all msg (if msg is not received, try send one msg OR retry the action)"
                + "\n" + "5 - [CUS][Topic] send one test msg"
        );

    }

    public static void main(String[] args) {
        boolean isContinue = true;
        printInfo("INFO - start app...");
        printInstructionDetails();
        while (isContinue) {
            ApplicationContext context = new ClassPathXmlApplicationContext("helper.jms.xml");
            JmsHelper client = (JmsHelper) context.getBean("client");
            Queue cusQueue = client.getDatasetQueueCUS();
            Topic cusTopic = client.getDatasetTopicCUS();

            Scanner scanner = new Scanner(System.in);
            int inputNumber;
            printInstruction();
            inputNumber = scanner.nextInt();

            switch (inputNumber) {
                case 0: isContinue = false; break;
                //
                case 1: client.simpleBrowseQueue(cusQueue); break;
                case 2: client.consumeAllMessagesInQueue(context, cusQueue); break;
                case 3: client.simpleSendToQueue(cusQueue, "test_123_cus"); break;
                case 4: client.consumeAllMessagesInTopic(context, cusTopic); break;
                case 5: client.simpleSendToTopic(cusTopic, "test_123_cus"); break;
                default: printInfo("INFO - input error..."); break;
            }
        }
        printInfo("INFO - end app...");
        System.exit(1);
    }

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public Queue getDatasetQueueCUS() {
        return datasetQueueCUS;
    }

    public void setDatasetQueueCUS(Queue datasetQueueCUS) {
        this.datasetQueueCUS = datasetQueueCUS;
    }

    public Topic getDatasetTopicCUS() {
        return datasetTopicCUS;
    }

    public void setDatasetTopicCUS(Topic datasetTopicCUS) {
        this.datasetTopicCUS = datasetTopicCUS;
    }
}

XML Configuration

说明:这里的Connection Factory用了TibjmsConnectionFactory,Queue和Topic分别用了TibjmsQueueTibjmsTopic。可以根据自身需求改用其它的,比如RabbitMQ。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns="http://www.springframework.org/schema/beans" 
	   xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/util
     http://www.springframework.org/schema/util/spring-util-2.5.xsd">

    <context:annotation-config />

    <context:property-placeholder location="classpath:helper.jms.properties" />

    <bean id="client" class="com.nomura.unity.udw.util.JmsHelper" scope="prototype"></bean>

    <!-- Common Components -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
    </bean>

    <bean id="jmsConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
        <constructor-arg>
            <bean class="com.tibco.tibjms.TibjmsConnectionFactory">
                <property name="userPassword" value="${jms.userPassword}"/>
                <property name="userName" value="${jms.userName}"/>
                <property name="serverUrl" value="${jms.serverUrl},${jms.serverUrl}"/>
                <property name="reconnAttemptCount" value="30"/>
                <property name="reconnAttemptDelay" value="1000" />
            </bean>
        </constructor-arg>
        <property name="reconnectOnException" value="true"/>
    </bean>

    <!-- Message Listener Container 2, used for Topic, set defalut destination to dummy -->
    <bean id="jmsContainer2" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsConnectionFactory"/>
        <property name="messageListener" ref="messageListener" />
        <property name="destination" ref="datasetTopicDUMMY"/>
        <property name="sessionTransacted" 		 value="true" /><!-- whether JMS Sessions are transacted-->
        <property name="pubSubDomain" 			 value="true"/><!--use Publish/Subscribe domain (Topics)-->
        <property name="subscriptionDurable" 	 value="true" /><!--make the subscription durable-->
        <property name="durableSubscriptionName" value="${static.durableSubscriptionName}" />
        <property name="autoStartup" 			 value="false" />
    </bean>

    <!-- Message Listener Container 1, used for Queue, set defalut destination to dummy -->
    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsConnectionFactory"/>
        <property name="messageListener" ref="messageListener" />
        <property name="destination" ref="datasetQueueDUMMY"/>
        <property name="sessionTransacted" 		value="false" />
        <property name="pubSubDomain" 			value="false"/>
        <property name="autoStartup" 			value="false" />
    </bean>
    <!-- Message Driven POJO -->
    <bean id="messageListener" class="com.nomura.unity.udw.util.JmsHelperMessageListener" />

    <!-- Destinations in different env -->
    <!-- CUS -->
    <bean id="datasetQueueCUS" class="com.tibco.tibjms.TibjmsQueue">
        <constructor-arg value="${cus.jms.dataset.queue}" />
    </bean>
    <bean id='datasetTopicCUS' class="com.tibco.tibjms.TibjmsTopic">
        <constructor-arg value="${cus.jms.dataset.topic}" />
    </bean>
    <!-- DUMMY -->
    <bean id="datasetQueueDUMMY" class="com.tibco.tibjms.TibjmsQueue">
        <constructor-arg value="dummy1" />
    </bean>
    <bean id='datasetTopicDUMMY' class="com.tibco.tibjms.TibjmsTopic">
        <constructor-arg value="dummy2" />
    </bean>
</beans>

Property File

jms.userName=user1
jms.userPassword=123456
jms.serverUrl=tcp://some_address

# JMS Destination for CUS
cus.jms.dataset.queue=my-queue-1
cus.jms.dataset.topic=my-topic-1

# Topic durableSubscriptionName
static.durableSubscriptionName=my-subscription-1

Dependency

1. springframework
2. jms provider (tibjms)
3. javax.jms lib

Result

下面是运行之后的一些输入和输出:

jms_1

posted @ 2020-06-08 16:22  MaxStack  阅读(19)  评论(0编辑  收藏  举报