Sorry, JMS 2.0 standalone examples are not that simple
23 October 2013 6 comments
Reading time:
6 minutes
Word count:
1183
Well I going to have to leave this one for somebody who is more expert than me, because I have more things to do with my time now. I had a question from a reader about the source code for Java EE 7 Developer Handbook. Does JMS 2.0 standalone work as a client? What I understood from this, was that a developer should be able to run GlassFish 4 and get access to JMS queues and topics from a standalone container. I used to do this for investment banks using J2EE 1.4 with WebSphere and WebLogic application servers and so I said sure that code I worked with several months is bound to work, the reader’s question is more likely my own oversight. I must have been dreaming about these results.
I am sorry say, I spent several days going through this experience of getting GlassFish Embedded 4.0, then Managed Server to work together in order to demonstrate a straightforward JMS 2.0 standalone example. I also a use Arquillian test to work and then also tried a non-Arquillian version and now I am very perturbed. It would appear nobody knows on the Net how to exactly configure a standalone JMS 2.0 as a Java SE application.
This is as far as I get:
package je7hb.jms.essentials; import static org.junit.Assert.*; import javax.annotation.Resource; import javax.ejb.*; import javax.jms.*; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.*; import org.junit.runner.RunWith; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Properties; @RunWith(Arquillian.class) public class AsynchronousJMSMessageArquillianTest { @Deployment public static WebArchive createDeployment() { WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "asyncjms.war") .addClasses(PayloadCheck.class) .addAsWebInfResource( new File("src/test/resources-glassfish-managed/glassfish-resources.xml"), "glassfish-resources.xml") .addAsWebInfResource( EmptyAsset.INSTANCE, "beans.xml"); System.out.println(webArchive.toString(true)); return webArchive; } private List<String> messages = new ArrayList<>(); private CompletionListener completionListener = new CompletionListener() { @Override public void onCompletion(Message msg) { TextMessage textMsg = (TextMessage)msg; try { System.out.printf("%s.onCompletion(%s) Thread: %s\n", getClass().getSimpleName(), textMsg.getText(), Thread.currentThread()); } catch (JMSException e) { e.printStackTrace(System.err); } } @Override public void onException(Message msg, Exception ex) { ex.printStackTrace(System.err); } }; @Test @RunAsClient public void shouldFire() throws JMSException, InterruptedException, NamingException { Properties properties = new Properties(); // properties.put("com.sun.appserv.iiop.endpoints", "localhost:7676"); // properties.put("org.omg.CORBA.ORBInitialHost", "localhost"); // properties.put("org.omg.CORBA.ORBInitialPort", "3700"); // properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory"); // properties.put(Context.PROVIDER_URL, "mq://localhost:7676/"); // properties.put("java.naming.factory.url.pkgs", "com.sun.enterprise.naming"); // properties.put("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl"); properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory"); properties.put(Context.URL_PKG_PREFIXES, "com.sun.enterprise.naming"); properties.put(Context.STATE_FACTORIES, "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl"); // properties.put("org.omg.CORBA.ORBInitialHost", "localhost"); // properties.put("org.omg.CORBA.ORBInitialPort", "3700"); properties.put(Context.PROVIDER_URL, "mq://localhost:7676"); // vm://localhost: // properties.put(Context.PROVIDER_URL, "iiop://localhost:7676"); // vm://localhost: InitialContext jndiContext = new InitialContext(properties); System.out.printf("\t jndiContext=%s\n", jndiContext); ConnectionFactory connectionFactory = (ConnectionFactory)jndiContext.lookup("jms/demoConnectionFactory"); Queue queue = (Queue)jndiContext.lookup("jms/demoQueue"); assertNotNull(connectionFactory); assertNotNull(queue); Connection connection = connectionFactory.createConnection(); JMSContext context = connectionFactory.createContext( Session.AUTO_ACKNOWLEDGE ); JMSProducer producer = context.createProducer(); messages.clear(); producer.setAsync(completionListener); producer.send(queue, "hello"); producer.send(queue, "world"); producer.send(queue, "asynchronously"); Thread.sleep(2000); // Delay before shutdown System.out.println("Done"); } }
The evidence is in the code and you can review commented out lines, because every which way and some has been tried.
The reason is that certain calls such as setAsync
are forbidden in a Java EE and web container. Therefore if you want an asynchronous JMS reader or writer in EE you can forget about it.
The glassfish resource configuration file is:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "https://glassfish.org/dtds/glassfish-resources_1_5.dtd"> <resources> <admin-object-resource enabled="true" jndi-name="jms/demoQueue" object-type="user" res-adapter="jmsra" res-type="javax.jms.Queue"> <description/> <property name="Name" value="PhysicalQueue"/> </admin-object-resource> <admin-object-resource enabled="true" jndi-name="jms/demoTopic" object-type="user" res-adapter="jmsra" res-type="javax.jms.Topic"> <description/> <property name="Name" value="PhysicalTopic"/> </admin-object-resource> <connector-connection-pool name="jms/demoDestinationFactoryPool" connection-definition-name="javax.jms.QueueConnectionFactory" resource-adapter-name="jmsra" /> <connector-resource enabled="true" jndi-name="jms/demoConnectionFactory" pool-name="jms/demoDestinationFactoryPool" object-type="system-all" /> </resources>
In fact, it is such an embarrassing state of affairs, I would recommend not look at GlassFish for a standalone JMS client, but maybe to wait for WildFly or Tom EE. Clearly, using JMS 2.0 outside of EE container is very unamusing, and I think this has be bloody easier for new developers, otherwise they want touch Java EE with a barge pole. If the technical expert cannot develop a simple example of outside-of-container JMS use then what is the point? The official JMS 2.0 page is also unhelpful.
Part of the problem is that there is no standard client side JMS API for Java SE, JAX RS 2.0 now has a client-side API. Another is that some of the GlassFish Maven artifacts have huge dependencies, which cause the download of the entire application server’s modules (I pointing a finger at gf-client:4.0-SNAPSHOT
). Finally, this hardshell problem is not really documented and sadly, Java EE 7 tutorial sample code is only accessible as part of a bundle and not independently for quick access.
So my plan, is to leave this JMS async
project as part of the book’s code distribution. I am sorry that the code does not work at all. This code should work as a single integration test in the reference implementation GlassFish and Arquillian. I have no more time to devote it, but I will exclude building it (because the test fails) in the new Gradle dynamic task sub-project enhancements, which I am currently writing now.
😐 +PP+