Search code examples
queuejmswebspheremessage-queue

My code receives the message from the queue multiple time, I want to receive it only one time. How can I do it?


I am new, so my question is relatively easy, I guess.

I am using Websphere Application Server platform and default JMS provider to send and receive message from queue. This is how my app looks like:

enter image description here

Saytime is my main servlet which reroute my code to a .jsp file. The "Produce" button sends the app following code and generate the message written in box:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String queueName = "jms/helloqueue";
    Context jndiContext = null;
    QueueConnectionFactory queueConnectionFcatory = null;
    QueueConnection queueConnection = null;
    QueueSession queueSession = null;
    QueueSender queueSender = null;
    Queue queue = null;
    TextMessage textMessage = null;
    response.setContentType("text/html");
    request.setCharacterEncoding("UTF-8"); // To information the that you may use Unicode characters
    response.setCharacterEncoding("UTF-8");
    String txt = request.getParameter("text");
   
    try {
        Properties initialProperties = new Properties();
        initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
        initialProperties.put(InitialContext.PROVIDER_URL, "iiop://localhost:2810");
        jndiContext = new InitialContext(initialProperties);
    } catch (NamingException e) {
        e.printStackTrace();
        System.exit(1);
    }
    try {
        queueConnectionFcatory = (QueueConnectionFactory) jndiContext.lookup("jms/helloqcf");
        queue = (Queue) jndiContext.lookup(queueName);
    } catch (NamingException e) {
        e.printStackTrace();
        System.exit(1);
    }
    try {
        queueConnection = queueConnectionFcatory.createQueueConnection();
        queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
        queueSender = queueSession.createSender(queue);
        textMessage = queueSession.createTextMessage();
        
        textMessage.setText(txt);
        queueSender.send(textMessage);
        
    } catch (JMSException e) {
        System.out.println("JMS Exception occured: "+ e.getMessage());
    } finally {
        if(queueConnection != null){
            try{
                Thread.sleep(6000);
                queueConnection.close();
            } catch(Exception e){}
        }
    }
    RequestDispatcher rd = request.getRequestDispatcher("saytime");
    rd.forward(request,response);
}

The "Receive" button sends my app to following servlet code and receives the message from queue:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String queueName = "jms/helloqueue";
    Context jndiContext = null;
    QueueConnectionFactory queueConnectionfactory = null;
    QueueConnection queueConnection = null;
    QueueSession queueSession = null;
    QueueReceiver queueReceiver = null;
    Queue queue = null;
    String text = null;

    try {
        Properties initialProperties = new Properties();
        initialProperties.put(InitialContext.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
        initialProperties.put(InitialContext.PROVIDER_URL,"iiop://localhost:2810");
        jndiContext = new InitialContext(initialProperties);
    } catch (NamingException e) {
        System.out.println("JNDI exception occured: " + e.getMessage());
        System.exit(1);
    }

    try {
        queueConnectionfactory = (QueueConnectionFactory) jndiContext.lookup("jms/helloqcf");
        queue = (Queue) jndiContext.lookup(queueName);
    } catch (NamingException e) {
        System.exit(1);
    }
    try {
        queueConnection = queueConnectionfactory.createQueueConnection();
        queueSession = queueConnection.createQueueSession(true,Session.AUTO_ACKNOWLEDGE);
        queueReceiver = queueSession.createReceiver(queue);
        //queueReceiver.setMessageListener(listener);
        queueConnection.start();
        text = queueReceiver.receive().toString();
        
    } catch(JMSException e) {
        System.out.println("Exception occured: "+ e.getMessage());
    } finally {
        if (queueConnection != null) {
            try {
                queueConnection.close();
            } catch (JMSException e) {
            }
        }
    }

    if(text != null) {
        request.setAttribute("message", text.toString());
    }
    RequestDispatcher rd = request.getRequestDispatcher("saytime");
    rd.forward(request,response);
}

After that I print the message with this little code in my .jsp file:

<%
String getValues = (String) request.getAttribute("message");
%>
<%
if (getValues != null) {
    out.println("<p>" + getValues + "</p>");
} else {
    out.println("<p> There is no message </p>");
}
%>

The problem is this: I am able to take my produced message, but the button continues to receive the message till the count on JMSXDeliveryCount hit 5. Mostly JMSXDeliveryCount start with 1 and total I can receive the message 5 times. I want to receive it only once and then message to disappear.

Additionally, I want to know how I can print only my message. I print with additional details like you see in the picture. If it's possible, I don't want that.

I tried to limit redelivery number, but I am unable to come up with right code I guess. Also, I tried to use different acknowledgement mode but, it did not work either.

I got really confused with it, some help would be perfect. Thanks.


Solution

  • The problem is you're creating the consumer's session as transacted. See this line:

    queueSession = queueConnection.createQueueSession(true,Session.AUTO_ACKNOWLEDGE);
    

    The acknowledgement mode will be ignored since the session is transacted (i.e. you're passing true in the first parameter). This is noted in the documentation which states:

    If transacted is set to true then the session will use a local transaction which may subsequently be committed or rolled back by calling the session's commit or rollback methods. The argument acknowledgeMode is ignored.

    Therefore, you should either acknowledge the message and commit the session manually, e.g.:

    Message message = queueReceiver.receive();
    text = message.toString();
    message.acknowledge();
    queueSession.commit();
    

    Or you should use a non-transacted session and allow the message to be auto-acknowledged according to the acknowledgement mode, e.g.:

    queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    

    Typically transacted sessions are only used when multiple operations (i.e. send and receive) need to be combined together atomically. Since you're only consuming a single message here I would recommend you just use a non-transacted session.

    Also, you will eventually want to cache the javax.jms.Connection or perhaps use a connection pool rather than creating a connection, session, & producer/consumer for every message. This is an anti-pattern and should be avoided whenever possible.