We're very pleased that you want to get in touch with us. Please fill in the form below:



or   Close this form  
Some content

Peter Pilgrim :: Java Champion :: Digital Developer Architect

I design Java EE and Scala software solutions for the blue-chip clients and private sector

Hey all! Thanks for visiting. I provide fringe benefits to interested readers: checkout consultancy, training or mentorship Please make enquiries by email or call +44 (0)7397 067 658.

Due to the Off-Payroll Working plan for the UK government, I am enforcing stricter measures on contracts. All potential public sector GOV.UK contracts engagements must be approved by QDOS and/or SJD Accounting. Please enquire for further information.

Sorry, JMS 2.0 standalone examples are not that simple

23 October 2013 6 comments

6 minutes

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" "http://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+

6 Comments

  1. If you want to write a standalone JMS client for GlassFish (whether JMS 1.1 or 2.0), the following section of the GlassFish Application Developer Guide describes how to do it: http://docs.oracle.com/cd/E18930_01/html/821-2418/gkusn.html#gkuqa . If you have any questions about this please ask in the GlassFish user forum at https://www.java.net/forums/glassfish/glassfish and we’ll try to help.

    Your complaint that this requires rather a lot of client-side jars is a valid one, but that’s a general GlassFish issue rather than something specific to JMS 2.0.

    I don’t understand what lies behind your remark about there being “no standard client side JMS API for Java SE”, since there most definitely is. Please get in touch via the GlassFish forum, the JMS spec forum, or directly and we can discuss this.

    If you’re not interested in application servers at all and want to try JMS in a purely Java SE environment then you could try Open Message Queue 5.0 (which support JMS 2.0) from https://mq.java.net/ . This has a much lighter-weight client than GlassFish. In fact you can use the Open Message Queue client to read and write to queues and topics in Glassfish, though you can’t use it to lookup GlassFish JNDI objects. If you want to know more please ask on the Glassfish forum..

    Comment by Nigel Deakin — 24 October 2013 @ 1:08 pm

  2. Hi Nigel

    Thanks for replying. Yes I am frustrated with the GlassFish set up as you can imagine with the client jar. It is not the fault of the JMS 2.0 specification. It is unfair to brush JMS 2.0 with this. There are client specific API in there, I think I meant to write about how developers can get access to ease-of-use features on Java EE 7 on the server side inside a client application. Part of this is the ability to use CDI and annotations on the client side in a Java SE application. Of course, this falls out of the EE specification. There is no CDI standalone specification and there are questions on how say @JMSContext can be applied in a Java SE settings.

    Other folk have also pointed to the Application Client Container to run GlassFish JMS examples. I suppose the question is what does ACC give us in 2013 in Java EE setting.

    Thanks for the pointers to Open Message Queue 5.0.

    Anyway I have to crack on with contracting … I guess this also part of the general feeling, this sort of stuff should not be that hard, it should not take all day to figure out. We are all time challenged…. All the best, Nigel and I will post a follow up blog entry on this soon

    Comment by Peter Pilgrim — 25 October 2013 @ 8:12 am

  3. The @Inject annotation (and dependency injection in general) is not part of Java SE (or the Java EE application client container).

    The only environment where @Inject is guaranteed (by the relevant spec) to be available is the Java EE web or EJB container. That’s why the JMS 2.0 specification is only able to specify “@Inject JMSContext” for Java EE web or EJB applications.

    Comment by Nigel Deakin — 25 October 2013 @ 10:02 am

  4. Maybe we can fix in Java EE 8 one day when CDI has a standalone specification that work in Java SE. To observers, JBoss Weld does have Weld-SE implementation. Perhaps, even the Java EE application client container requires an update (EE 8) so that the developer can make use of annotations and much more of the goodness that went into Java EE 7.

    Comment by Peter Pilgrim — 28 October 2013 @ 4:51 pm

  5. JMS is a strange API. Some parts work only in SE, others only in EE and some in both. In JMS 2 the JavaDoc methods finally explicitly states where you can use a method (at least the ones I checked). Some methods clearly state that they behave differently in SE and EE.

    But there’s more. With JMS 1.1 (haven’t checked with 2) the only non-blocking listening option was MDBs. In SE you could only do blocking listening (eating threads). But there’s more. An EE app server ships with a default JMS implementation. If you want to use a different messaging product you:
    – need a RAR (only JMS 2 finally requires JMS vendors to supply a RAR, however the RAR does not need to be portable, eg. is allowed to call vendor proprietary API)
    – if you want to associate a MDB with a non-default JMS implementation you have to use a vendor specific deployment descriptor

    All these things make JMS unnecessarily hard to use, especially compared to things like JDBC.

    Comment by Philippe Marschall — 28 October 2013 @ 6:38 pm

  6. Sounds like you are after JMS Service Provider Interface for assembly of application server composed of modules, where you can plug in a different JMS implementation as long it meets the requirement of the SPI. Maybe you should take this up with JMS expert group for version 2.1 or 3.0.

    Comment by Peter Pilgrim — 31 October 2013 @ 4:00 pm

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Contents of this blog entry are under copyright © 2017 by Peter Pilgrim and associates. For enquiries after republishing, please contact us for permission. All requests for syndicated content will be ignored /dev/null, consider yourself warned!

I help to design, create and build JVM components and services that are behind popular e-commerce websites.

My Blurb

Please get in touch , directly, to establish hire availability, contract & consulting opportunities.

Speaking at Your Conference

Contact by invitation

What Peter Does

Contact