3 Replies Latest reply on Jul 22, 2004 5:30 PM by cgraham

    Problem Casting Reference to javax.jms.Queue

      Hello ALL!

      I am using the JMS example that is given as part of the JBOSS MQ Qiki and I have run into a problem that I don't understand. Basically I am able to obtain the JMS Queue, but when I try to cast it to a javax.jms.Queue it fails with a class cast exception.

      package com.noi.jbossmq.app.message;
      
      import java.util.Properties;
      
      import javax.jms.Message;
      import javax.jms.Queue;
      import javax.jms.QueueConnection;
      import javax.jms.QueueConnectionFactory;
      import javax.jms.QueueReceiver;
      import javax.jms.QueueSender;
      import javax.jms.QueueSession;
      import javax.jms.Session;
      import javax.jms.TextMessage;
      import javax.naming.Context;
      import javax.naming.InitialContext;
      import com.noi.jbossmq.app.util.*;
      
      /**
       *
       * @author clay
       */
      public class JBossMqClient {
      
       /** Creates a new instance of JBossMqClient */
       public JBossMqClient() {
       }
      
       public static void main(String[] args) throws Exception
       {
       SysLogger.info("Creating jndi context - alternatively use a jndi.properties");
       Properties properties = new Properties();
       properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
       properties.put(Context.URL_PKG_PREFIXES, "org.jnp.interfaces");
       properties.put(Context.PROVIDER_URL, "jnp://192.168.1.7:1099");
       InitialContext ctx = new InitialContext(properties);
      
       SysLogger.info("Looking up queue");
       Object o = ctx.lookup("queue/DLQ");
       if(null != o)
       {
       SysLogger.info("not null");
       Class c = o.getClass();
       javax.naming.Reference ref = (javax.naming.Reference)o;
       SysLogger.info(ref.getClassName());
       }
       else
       SysLogger.info("null");
      
       Queue queue = (Queue)o;
      
       SysLogger.info("Looking up connection factory");
       QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup("UIL2ConnectionFactory");
      
       SysLogger.info("Creating connection");
       QueueConnection qc = qcf.createQueueConnection();
       try
       {
       SysLogger.info("Creating session");
       QueueSession qs = qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
      
       SysLogger.info("Creating sender");
       QueueSender sender = qs.createSender(queue);
      
       SysLogger.info("Creating message");
       TextMessage message = qs.createTextMessage("hello");
      
       SysLogger.info("Sending message");
       sender.send(message);
      
       SysLogger.info("Creating receiver");
       QueueReceiver receiver = qs.createReceiver(queue);
      
       SysLogger.info("Try to receive message, it will not work");
       Message received = receiver.receiveNoWait();
       if (received != null)
       throw new RuntimeException("Maybe you forget to clear the queue before running this test?");
      
       SysLogger.info("You have to start the connection before receiving messages");
       qc.start();
      
       SysLogger.info("This receive will work");
       received = receiver.receiveNoWait();
      
       SysLogger.info("Got message: " + received);
       }
       finally
       {
       qc.close();
       }
       }
      
      }
      
      


      But when I attempt to cast it to a javax.jms.Queue, it fails miserably.

      Creating jndi context - alternatively use a jndi.properties
      Looking up queue
      not null
      org.jboss.mq.SpyQueue
      Exception in thread "main" java.lang.ClassCastException
       at com.noi.jbossmq.app.message.JBossMqClient.main(Unknown Source)
      


      This is the no-brainer, newbie example, and I have spent hours looking at forums trying to understand why a Refrence comes back from JNDI instead of something I can cast as a Queue.

      Any help is appreciated.

      Clay


        • 1. Re: Problem Casting Reference to javax.jms.Queue

          Ok, here is the solution.

          First of all I don't really like the example on the WIKI, it leaves out too many details that a newbie like me really needs to configure and test JMS on JBOSS. So I am going to be verbose and offer the full example. Hopefully I can add this solution to the WIKI as well.

          1. Configure your project so that you have all the client JARS you will need, and make sure they are all in the classpath (lib). This will allow you to test your JMS producer and consumer remotely.

          This is the project structure I use to test:

          .:
          .
          ..
          build-jbossmq.xml
          jbossmq.war
          lib
          .nbattrs
          src
          tree.txt
          web
          
          
          ./lib:
          .
          ..
          concurrent.jar
          j2ee.jar
          jboss-common.jar
          jbossmq.jar
          jnpserver.jar
          
          ./src:
          .
          ..
          com
          
          ./src/com:
          .
          ..
          noi
          
          ./src/com/noi:
          .
          ..
          jbossmq
          
          ./src/com/noi/jbossmq:
          .
          ..
          app
          
          ./src/com/noi/jbossmq/app:
          .
          ..
          message
          util
          
          ./src/com/noi/jbossmq/app/message:
          .
          ..
          SimpleConsumer.java
          SimpleProducer.java
          
          ./src/com/noi/jbossmq/app/util:
          .
          ..
          SysLogger.java
          


          2. Create the producer.
          /*
           * SimpleProducer.java
           *
           * Created on July 22, 2004, 11:30 AM
           */
          
          package com.noi.jbossmq.app.message;
          
          import javax.jms.*;
          import javax.naming.*;
          import java.util.Properties;
          import com.noi.jbossmq.app.util.*;
          
          public class SimpleProducer {
          
           /**
           * Main method.
           *
           * @param args the destination used by the example,
           * its type, and, optionally, the number of
           * messages to send
           */
           public static void main(String[] args) {
          
           String messages[] = {"MESSAGE A" ,
           "MESSAGE B" ,
           "MESSAGE C" ,
           "MESSAGE D"};
          
           QueueConnectionFactory queueConnectionFactory = null;
           Queue testQueue = null;
          
           try {
           SysLogger.info("Creating jndi context - alternatively use a jndi.properties");
           Properties properties = new Properties();
           properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
           properties.put(Context.PROVIDER_URL, "jnp://192.168.1.7:1099");
           InitialContext jndiContext = new InitialContext(properties);
           queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("ConnectionFactory");
           testQueue = (Queue) jndiContext.lookup("queue/testQueue");
           } catch (NamingException nameEx) {
           SysLogger.info("Naming Exception: " + nameEx.toString());
           }
          
           SysLogger.info("made queue");
           QueueConnection queueConnection = null;
          
           try {
          
           SysLogger.info("trying to send message");
           queueConnection = queueConnectionFactory.createQueueConnection();
           QueueSession queueSession = queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
           QueueSender queueSender = queueSession.createSender(testQueue);
           TextMessage textMessage = queueSession.createTextMessage();
           SysLogger.info("send each message");
           for (int msgCount = 0; msgCount < messages.length; msgCount++)
           {
           textMessage.setText(messages[msgCount]);
           queueSender.send(textMessage);
           SysLogger.info(" sending line " + msgCount + " : " + messages[msgCount]);
           }
          
           textMessage.setText("end of message");
           queueSender.send(textMessage);
           SysLogger.info(" sending last line " + " : " + textMessage.getText());
          
           queueConnection.close();
          
           SysLogger.info(" sender closed");
          
           } catch (javax.jms.JMSException jmsEx) {System.out.println("JMS Exception: " + jmsEx.toString());
           } finally { if (queueConnection != null)
           { try
           { queueConnection.close();
           } catch (javax.jms.JMSException jmse) {
           SysLogger.info("jms exception.");
           }
           }
           }
          
          
           }
          }
          


          3. Create the Consumer

          /*
           * SimpleConsumer.java
           *
           * Created on July 22, 2004, 12:30 PM
           */
          
          package com.noi.jbossmq.app.message;
          import javax.jms.*;
          import javax.naming.*;
          import java.util.Properties;
          import com.noi.jbossmq.app.util.*;
          /**
           *
           * @author clay
           */
          public class SimpleConsumer {
          
          public static void main(String[] args) {
          
           QueueConnectionFactory queueConnectionFactory = null;
           Queue testQueue = null;
          
           try {
           SysLogger.info("Creating jndi context - alternatively use a jndi.properties");
           Properties properties = new Properties();
           properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
           properties.put(Context.PROVIDER_URL, "jnp://192.168.1.7:1099");
           InitialContext jndiContext = new InitialContext(properties);
           queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("ConnectionFactory");
           testQueue = (Queue) jndiContext.lookup("queue/testQueue");
          
           } catch (NamingException nameEx) {
           SysLogger.info("Naming Exception: " + nameEx.toString());
           }
          
           QueueConnection queueConnection = null;
          
           try {
          
           queueConnection = queueConnectionFactory.createQueueConnection();
           QueueSession queueSession = queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
           QueueReceiver queueReceiver = queueSession.createReceiver(testQueue);
           queueConnection.start();
          
           TextMessage textMessage = null;
          
           while (true)
           {
           textMessage = (TextMessage) queueReceiver.receive(1);
           SysLogger.info(" receiving line " + " : " + textMessage.getText());
           if (textMessage.getText().equals("end of message")) {break;}
           }
          
           queueConnection.close();
          
           SysLogger.info(" receiver closed");
          
           } catch (javax.jms.JMSException jmsEx) {SysLogger.info("JMS Exception: " + jmsEx.toString());
           } finally { if (queueConnection != null)
           { try
           { queueConnection.close();
           } catch (javax.jms.JMSException jmse) {}
           }
           }
           }
          }
          
          


          4. Make a logger, this one just goes to system.out

          /*
           * SysLogger.java
           *
           * Created on July 21, 2004, 3:50 PM
           */
          
          package com.noi.jbossmq.app.util;
          
          /**
           *
           * @author clay
           */
          public class SysLogger {
          
           public static void info(String message)
           {
           System.out.println(message);
           }
          
           public static void error(String message, Throwable t)
           {
           System.err.println(message);
           t.printStackTrace();
           }
          }
          


          5. create your ant build, if this is going to become a web application (mine is) you will also need to create a base web directory with a WEB-INF.

          <?xml version="1.0" encoding="UTF-8"?>
          <project basedir="." default="all" name="jbossmq">
           <target name="init">
           <property name="name" value="jbossmq"/>
           <property name="build" value="build" />
           <property name="build.lib" value="lib" />
           <property name="warfile" value="${name}.war" />
           <property name="web.orig.dir" value="web" />
           <property name="web.lib" value="${web.orig.dir}/WEB-INF/lib" />
           <property name="build.classes" value="${build}/WEB-INF/classes" />
           <property name="src.dir" value="src" />
           <!-- Configure the custom Ant tasks for the Manager application -->
           <echo message="JAVA_HOME=${java.home}"/>
           <path id="project.class.path">
           <fileset dir="${java.home}/lib">
           <include name="*.jar"/>
           </fileset>
           <fileset dir="${build.lib}">
           <include name="*.jar"/>
           </fileset>
           <fileset dir="${web.lib}">
           <include name="*.jar"/>
           </fileset>
           </path>
           <tstamp/>
           </target>
          
           <target name="prepare" depends="init"
           description="Create build directories.">
           <mkdir dir="${build}" />
           <mkdir dir="${build}/WEB-INF" />
           <mkdir dir="${build.classes}" />
           <mkdir dir="${build}/WEB-INF/lib" />
           <mkdir dir="${build}/html" />
           <mkdir dir="${build}/images" />
           </target>
          
           <target name="compile" depends="init, prepare"
           description="Compile app Java files and copy HTML and JSP pages" >
           <javac srcdir="${src.dir}" destdir="${build.classes}" deprecation="on">
           <include name="**/*.java" />
           <classpath refid="project.class.path"/>
           </javac>
           </target>
          
           <target name="web-war" depends="init, prepare, compile"
           description="Compile app Java files and copy HTML and JSP pages" >
           <copy todir="${build}/WEB-INF">
           <fileset dir="web/WEB-INF" >
           <include name="jboss-web.xml" />
           <include name="*.tld" />
           </fileset>
           </copy>
           <copy todir="${build}/WEB-INF/lib">
           <fileset dir="web/WEB-INF/lib" >
           <include name="*.jar" />
           </fileset>
           </copy>
           <copy todir="${build}/WEB-INF/classes">
           <fileset dir="${src.dir}" >
           <include name="**/*.properties" />
           </fileset>
           </copy>
           <copy todir="${build}">
           <fileset dir="web">
           <include name="*.html" />
           <include name="*.jsp" />
           </fileset>
           </copy>
           <copy todir="${build}/html">
           <fileset dir="web/html">
           <include name="*.htm" />
           <include name="*.html" />
           <include name="*.txt" />
           <include name="*.css" />
           </fileset>
           </copy>
           <copy todir="${build}/images">
           <fileset dir="web/images">
           <include name="*.jpg" />
           <include name="*.gif" />
           </fileset>
           </copy>
           <war warfile="${name}.war" webxml="${web.orig.dir}/WEB-INF/web.xml">
           <fileset dir="${build}" >
           <include name="WEB-INF/classes/**/*.class" />
           <include name="WEB-INF/classes/**/*.properties" />
           <include name="WEB-INF/lib/*.jar" />
           <include name="WEB-INF/*.xml" />
           <include name="WEB-INF/*.tld" />
           <include name="META-INF/**" />
           <include name="html/**" />
           <include name="images/**" />
           <include name="*.jsp" />
           <include name="*.html" />
           <include name="*.css" />
           </fileset>
           </war>
           </target>
          
           <target name="clean" depends="init">
           <delete dir="${build}"/>
           </target>
          
          
          </project>
          
          



          What I generate is a WAR, that I then extract to a deploy dir. If you dont want to do that then make a JAR instead of a WAR and run it from the command line.

          This is working for me with the following output:

          Producer
          Creating jndi context - alternatively use a jndi.properties
          made queue
          trying to send message
          send each message
           sending line 0 : MESSAGE A
           sending line 1 : MESSAGE B
           sending line 2 : MESSAGE C
           sending line 3 : MESSAGE D
           sending last line : end of message
           sender closed
          
          Consumer
          Creating jndi context - alternatively use a jndi.properties
           receiving line : MESSAGE A
           receiving line : MESSAGE B
           receiving line : MESSAGE C
           receiving line : MESSAGE D
           receiving line : end of message
           receiver closed
          
          



          Good luck, I hope this helps.

          Clay


          • 2. Re: Problem Casting Reference to javax.jms.Queue

            You should download and read the getting started guide.

            Your post will mislead people.

            1) The client jars are in ${JBOSS.HOME}/client, you are using copies of the server
            jars. The WIKI even says compile it with jbossall-client.jar from that directory.

            2) If you are not going to use jbossall-client.jar, you should use jboss-j2ee.jar not j2ee.jar.
            If you read the license carefully, you are not allowed to distribute j2ee.jar.
            If you have an older version, you are not even allowed to use it in a production application
            http://java.sun.com/j2ee/sdk_1.3/faq.html#licensing

            3) Inside the jboss server, creating a remote jndi context and
            lookup("ConnectionFactory") are both inappropriate.
            In a true j2ee web application you should resource-refs.
            It is upto the person doing the deployment whether they want to use a remote
            connection factory.

            If you do think the posts on the WIKI are incomplete, you can "Edit this page".

            • 3. Re: Problem Casting Reference to javax.jms.Queue

              Adrian,

              Thank you for your input, it may help someone refine my example to be 100% j2EE compliant. A good thing. Maybe you have time? If not you I would love it if another seasoned person like yourself could provide a complete example.

              It was my intent to be more verbose, and at least help someone like myself who may not know as much as you how to get going using a "cookbook" approach.


              You should download and read the getting started guide.

              Your post will mislead people.


              Great! do you have a URL for that? I would love to read it. I promise I looked around for help for a really long time, with no avail on finding a clear approach. I hope the document you refer to will provide a clearer explaniation and example.


              Inside the jboss server, creating a remote jndi context and
              lookup("ConnectionFactory") are both inappropriate.


              Understood. The example is intended for a remote client, but I am interested in an approach that would be suitable for deployment. I would appreciate any verbose examples that fix all the problems this newbie's cookbook has created.


              If you do think the posts on the WIKI are incomplete, you can "Edit this page".


              I added this cookbook as an attachement to the wiki. Is that good enough?

              Respectfully,

              Clay