users@glassfish.java.net

jms bug in glassfish? (for synchronous consumption)

From: <glassfish_at_javadesktop.org>
Date: Wed, 23 Jan 2008 15:25:08 PST

[u]Scenario:[/u]
I have a test case where 2 jms clients connect to Glassfish. One client is a standalone (out of container) and the other is an EJB (a web service implemented as a stateless session bean). What I'd like to accomplish with this test case is to perform a [b]synchronous[/b] request/response (send/receive) from the web service to the standalone jms client. This currently fails. Note however, [b]asynchronous[/b] consumption works fine.

[u]Code:[/u]
I've removed the Facade and placed the boilerplate code directly into the classes for simpler viewing over the web. Note that, performance and exception handling has been removed to provide a bare bones example of what I'm trying to accomplish.
/////////////////////////////////////////////
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;

public class TestClient {

    public static void main(String[] args) throws Exception {
        
         Properties props = System.getProperties();
        props.setProperty("java.naming.provider.url", "iiop://localhost:3700");
        props.setProperty("java.naming.factory.initial", "com.sun.appserv.naming.S1ASCtxFactory");
        props.setProperty("com.sun.appserv.iiop.endpoints", "localhost:3700");
        props.setProperty("org.omg.CORBA.ORBSingletonClass", "com.sun.corba.ee.impl.orb.ORBSingleton");

        Context context = new InitialContext(props);
        ConnectionFactory factory = (ConnectionFactory)context.lookup("jms/ump/ConnectionFactory");
        Destination destination = (Destination)context.lookup("jms/ump/Topic");

        Connection connection = null;
        Session session = null;
        MessageConsumer consumer = null;
        MessageProducer producer = null;
        try {
            connection = factory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            consumer = session.createConsumer(destination, "", true); // no msg selector so we receive everything
            producer = session.createProducer(null); // set producer destination to null, set for every send

            connection.start();
            System.out.println("Test client is ready...");
            
            while (true) { // loop forever, block and wait for msg, then respond
                Message receivedMsg = consumer.receive();

                System.out.println("received msg");
                Destination replyDest = receivedMsg.getJMSReplyTo();

                TextMessage replyMsg = session.createTextMessage("my reply");
                replyMsg.setJMSCorrelationID(receivedMsg.getJMSCorrelationID());

                // let msg die after 10000 = 10 seconds
                producer.send(replyDest, replyMsg, DeliveryMode.NON_PERSISTENT, Message.DEFAULT_PRIORITY, 10000);
                System.out.println("sent reply");
            }
            
        }
        finally {
            try {
                producer.close();
                consumer.close();
                session.close();
                connection.close();
            }
            catch (JMSException jmse) {}
        }
    }


}
//////////////////////////////////////////////////////////////
package testjms;

import java.util.*;
import javax.jws.WebService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.jms.*;

@Stateless
@WebService (
    endpointInterface = "testjms.TestJMS",
    serviceName = "TestJMSService",
    portName = "TestJMSPort"
)
public class TestJMSImpl {

    @Resource (mappedName = "jms/ump/ConnectionFactory")
    private ConnectionFactory connFactory;

    @Resource (mappedName = "jms/ump/Topic")
    private Destination destination;

    Connection connection = null;
    Session session = null;
    MessageConsumer consumer = null;
    MessageProducer producer = null;

    @PostConstruct
    public void init() {

        try {
            connection = connFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            consumer = session.createConsumer(destination, "", true); // no msg selector so we receive everything
            producer = session.createProducer(null); // set producer destination to null, set for every send

            connection.start();
            System.out.println("EJB/JMS client is ready...");
        }
        catch (JMSException jmse) {
            System.err.println("Error: " + jmse);
        }
    }

    @PreDestroy
    public void destroy() {
        System.out.println("clean up");
        try {
            producer.close();
            consumer.close();
            session.close();
            connection.close();
        }
        catch (JMSException jmse) {}
    }
    
    public void testSend() {

        try {

            Message sendMsg = session.createTextMessage("testSend()");
            sendMsg.setJMSReplyTo(destination);

            producer.send(destination, sendMsg, DeliveryMode.NON_PERSISTENT, Message.DEFAULT_PRIORITY, 10000);
            System.out.println("sent message");
        }
        catch (JMSException jmse) {
            System.err.println("error: " + jmse);
        }
    }

    public void testSendReceive() {

        try {
            Message sendMsg = session.createTextMessage("testSendReceive()");
            sendMsg.setJMSReplyTo(destination);
            sendMsg.setJMSCorrelationID(String.valueOf(System.currentTimeMillis()));

            producer.send(destination, sendMsg, DeliveryMode.NON_PERSISTENT, Message.DEFAULT_PRIORITY, 10000);
            System.out.println("sent message, correlationID = " + sendMsg.getJMSCorrelationID());

            Message reply = consumer.receive(5000); // block until timeout (5 seconds)
            if (reply != null) {
                System.out.println("got reply, correlationID = " + reply.getJMSCorrelationID());
            }
            else {
                System.out.println("timed out, reply is null");
            }

        }
        catch (JMSException jmse) {
            System.err.println("error: " + jmse);
        }
    }

    public void testRequestor() {

        Requestor requestor = null;
        try {
            requestor = new Requestor(session, destination);

            Message sendMsg = session.createTextMessage("testRequestor()");
            sendMsg.setJMSCorrelationID(String.valueOf(System.currentTimeMillis()));

            int timeToLive = 10000;
            int timeout = 5000;

            System.out.println("requesting now, correlationID = " + sendMsg.getJMSCorrelationID());
            Message reply =
                requestor.request(sendMsg, DeliveryMode.NON_PERSISTENT, Message.DEFAULT_PRIORITY, timeToLive, timeout);

            if (reply != null) {
                System.out.println("got reply, correlationID = " + reply.getJMSCorrelationID());
            }
            else {
                System.out.println("timed out, reply is null");
            }
        }
        catch (JMSException jmse) {
            System.err.println("Error:" + jmse);
        }
        finally {
            try {
                requestor.close();
            }
            catch (JMSException jmse) {}
        }
    }


}

[u]Environment:[/u]
OS = Red Hat Advanced Server, kernel 2.6.9-22.0.2.ELsmp
Java version = 1.6.0_04
Glassfish version = V2 Update 1 (build b09d-fcs), configuration is non-clustered and out of the box (unmodified).

[u]Instructions for testing:[/u]
Assumptions: glassfish is running and the appropriate administered objects have already been created. Attached are the files (and build) to perform the tests.
1) Building and running the standalone TestClient.java: Assuming you have the appropriate jars (under glassfish/lib) you can compile this file with:
 javac -cp .:appserv-admin.jar:appserv-deployment-client.jar:appserv-ext.jar:appserv-rt.jar:imqbroker.jar:imq.jar:imqjmsra.jar:javaee.jar:jms.jar TestClient.java

running it in a shell:
 java -cp .:appserv-admin.jar:appserv-deployment-client.jar:appserv-ext.jar:appserv-rt.jar:imqbroker.jar:imq.jar:imqjmsra.jar:javaee.jar:jms.jar TestClient

2) You can build the EJB using 'ant clean all'. Under the deploy dir you'll find the TestJMS.ear file. Login as administrator for glassfish and upload the EJB with:
Applications -> Enterprise Applications -> Deploy -> Browse and find the TestJMS.ear file. -> click OK. Go to Web Services -> TestJMSImpl -> Test - and the web service tester page will appear. Open another shell and view the server.log file with 'tail -f server.log' (for me it's under $GLASSFISH_HOME/domains/domain1/logs/server.log).

Now you should have 2 shells opened so you can see the output. And you should also have the tester page opened to trigger the tests.

1) testSend() - this method only sends a message to the standalone jms client. It does not test the synchronous receive. It only serves as a sanity check to verify the clients can perform some messaging. Click on this and you'll see the output of the standalone client receive the message and send back a reply (the reply just eventually dies). This works as expected.

2) testSendReceive() - this method tests a simple synchronous send/receive but fails. Expected behavior is such that the web service sends a message and receives one back from the standalone client. What actually happens (speculating here) is that the receive blocks before the send finishes. Then after the receive's timeout occurs, the block is released and the send occurs after the receive. The standalone client receives the message but it's too late, the web service has already returned with null as the reply.

3) testRequestor() - this method performs the same test as testSendReceive() and the results are the same as well (failure). The difference here is that it uses the my own implementation of java's TopicRequestor - meant for simple request/reply messaging. My implementation only exists so you can specify a timeout param...otherwise in this testcase, it would block forever and you'll have to undeploy the ear to unblock it.


1) Is this a bug in glassfish or my code?

2) Maybe there is no bug in glassfish or my code, rather a configuration issue? Note, however, I've tried all 3 different JMS service types (local, embedded, and remote) and none seem to make a difference for this test case.

I've attached all files for your viewing and testing. Your replies are appreciated, thank you. Sorry for the extremely verbose posting...

-pn42
[Message sent by forum member 'pnguyen42' (pnguyen42)]

http://forums.java.net/jive/thread.jspa?messageID=255486