5 Replies Latest reply on Jul 16, 2010 12:37 PM by Dean Hiller

    JMS inside war file fails from seam?

    Dean Hiller Expert

      I have code that works in another war file with a servlet.  Using the same EXACT code from seam war file to send a JMS message fails though with




      java.lang.IllegalStateException: This invocation should not be handled here!
           at org.jboss.jms.client.delegate.ClientClusteredConnectionFactoryDelegate.org$jboss$jms$client$delegate$ClientClusteredConnectionFactoryDelegate$createConnectionDelegate$aop(ClientClusteredConnectionFactoryDelegate.java:260)
           at org.jboss.jms.client.delegate.ClientClusteredConnectionFactoryDelegate.createConnectionDelegate(ClientClusteredConnectionFactoryDelegate.java)
           at org.jboss.jms.client.JBossConnectionFactory.createConnectionInternal(JBossConnectionFactory.java:205)
           at org.jboss.jms.client.JBossConnectionFactory.createQueueConnection(JBossConnectionFactory.java:101)
           at org.jboss.jms.client.JBossConnectionFactory.createQueueConnection(JBossConnectionFactory.java:95)
           at net.voicelog.tpv.messaging.TPVMessenger.createConnection(TPVMessenger.java:107)
           at net.voicelog.tpv.messaging.TPVMessenger.jmsSend(TPVMessenger.java:170)
      
      



      NOTE: I need to lookup a REMOTE queue from a REMOTE jndi.  All the other jndi is local for everything else.  I do this programmatically just like in the servlet(literally, I just call into the same class from seam that the servlet calls into from our other application).


      Any ideas why JMS is not working in seam here?  Is this even the write forum as JMS works fine outside of seam for some reason.  Any ideas?


      I do not use components.xml because I need local jndi for some stuff and then remote jndi for these queues and doing it programmitcally should work too, right?


      thanks,
      Dean


        • 1. Re: JMS inside war file fails from seam?
          Nikolay Elenkov Master

          Here's what it says in the code:


          This invocation should either be handled by the client-side interceptor chain or by the server-side endpoint.
          
          public CreateConnectionResult  [Search] createConnectionDelegate(String username, String password, int failedNodeID) throws JMSException
          {
             throw new IllegalStateException("This invocation should not be handled here!");
          }
          



          So most probably some configuration error, or you are looking up the wrong connection factory. Can you show the code? In any case, probably better to ask in the JBoss AS forums.


          Cf. http://grepcode.com/file/repository.jboss.com/maven2/jboss.messaging/jboss-messaging/1.4.3.GA/org/jboss/jms/client/delegate/ClientClusteredConnectionFactoryDelegate.java

          • 2. Re: JMS inside war file fails from seam?
            Dean Hiller Expert

            No need to show the code since the unit test just calls the same code the servlet does(but it's below..but I think I know a little more know too)....literally, all the queue setup and send message is in one class that is used by both seam bean and unit test.  I will show you the client code in the seam bean below(but it won't help much)


            I think the issue is with classloaders at this point.  I put the seam app in a jboss repository classloader(only since their OSGi stuff is not that stable/working in 5.1 :( ).  I think it has to do with that now as that is the only difference between our other clusters that are sending JMS and this one that doesn't.  I am pretty convinced it is not seam.  Of course, once I remove our war and jar(ejbs) from the repository, seam just goes to all hell because the jar can't find the classes from seam which are in the war file anymore. 


            Simply removing jar and war form repository, the jar class here now has trouble finding the seam classes inside the war file....


            Cause: Class net.voicelog.seam.helpers.VoicelogPhaseTracker is missing a runtime dependency: java.lang.NoClassDefFoundError: org/jboss/seam/jsf/SeamNavigationHandler



            See, we share our EJB layer with jars and wars, etc.  Ideally, once OSGi is here, we will have 2 wars(one on seam 3 and one on seam 2) and then both would share the EJB layer which I have done in osgi successfully before.


            Client...




            @Name("liveAgentEvents")
            @Scope(ScopeType.EVENT)
            public class LiveAgentEvents extends ActionBase {
                 
                 //BIG NOTE: We should really expose both of these as a http request into here so everyone
                 //including Cp's etc. procs can schedule a callback through java code here all in one
                 //place....
                 
                 private static final long serialVersionUID = 1877150211363697006L;
                public final static  String  EMAIL_NOTIFICATION_EVENT_TYPE         = "end";
                public final static  String  EMAIL_NOTIFICATION_APPLICATION_SOURCE = "LiveAgent";
            
                 @In(create=true)
                 private net.voicelog.agent.operator.TPVMessenger messaging;
                 
                 public void sendNotificationIfNeeded(Tpvtrans tpvTrans) {
                    try {             
                        NotificationMessage nm = new NotificationMessage();
                        Dnis dnisObj = entityManager.find(Dnis.class, tpvTrans.getDnis());
                        boolean callbackEnabled = false;
                        if (dnisObj == null) {
                             log.error(tpvTrans+": invalid dnis [" + tpvTrans.getDnis() + "]- using callbackenabled = false");
                        } else if(!dnisObj.getSubaccount().getSubaccountno().equals(tpvTrans.getSubaccount().getSubaccountno())) {
                             log.error(tpvTrans+" Dnis="+tpvTrans.getDnis()+" is on wrong subaccount for this tpvTrans.  Setting callbackEnabled=false");
                        } else {
                             callbackEnabled = dnisObj.isCallbackEnabled();
                        }
                        log.info(tpvTrans +": sending notification");            
                        
                        nm.setCallbackEnabled(callbackEnabled);
                        nm.setApplicationSource(EMAIL_NOTIFICATION_APPLICATION_SOURCE);
                        nm.setTpvid(tpvTrans.getTpvid());
                        nm.setDispositionCode(disposition);
                        nm.setEventType(EMAIL_NOTIFICATION_EVENT_TYPE);
                        nm.setReviewed(reviewed);
                        nm.setVerified(tpvTrans.isVerified());
            
                        String message = nm.printEmailNotificationMessage();
                        message = message.replaceAll("\n", " | ");
                        log.info(tpvTrans + ": message : [" + message + "]");
            
                        send2(nm);
            
                        log.info(tpvTrans + ":successfully sent jms message");
                    }
                    catch (RuntimeException re) {
                        log.error(tpvTrans + ": unable to send notification JMS message", re);
                    }
                 }
                 
                 private void send2(NotificationMessage nm) {
                    messaging.jmsSend(nm);
                 }
                 
            
            }






            WORKING unit test....


                 private static void newTest() {
                      Map<String, String> props = new HashMap<String, String>();
                      props.put(Environment.HBM2DDL_AUTO, "update");
                      EntityManagerFactory sf = Persistence.createEntityManagerFactory(
                                "xcoreNonJta", props);
            
                      EntityManager mgr = sf.createEntityManager();
                      
                      TPVMessenger messenger = new TPVMessenger(mgr);
                      messenger.initialize();
                      
                      NotificationMessage message = new NotificationMessage();
                      message.setCallbackEnabled(false);
                    message.setApplicationSource("liveagent");
                    message.setTpvid("123");
                    message.setDispositionCode(0);
                    message.setEventType("end");
                    message.setReviewed(true);
                    message.setVerified(true);
                    
                      messenger.jmsSend(message);
                      
                      System.out.println("hello there");
                 }
            




            JMS bean that works in unit test, fails in seam/jboss...




            @Name("messaging")
            @Startup
            @Scope(ScopeType.APPLICATION)
            public class TPVMessenger {
            
                 //Not a property as changing it would mean losing messages unless we wrote alot more
                 //code to look at the old directory and remove and send all messages in previous directory..
                 static final String persistenceDirName = "/opt/voicelog/tpv/messaging/persistence/";
                 
                 static final String QUEUE_NAME_KEY = "queueName";
                 static final String PROVIDER_URL_KEY = "java.naming.provider.url";
                 static final String CONNECTION_FACTORY_KEY = "connectionFactory";
                 static final String INITIAL_CONTEXT_FACTORY_KEY = "java.naming.factory.initial";
                 static final String INITIAL_CONTEXT_FACTORY_VALUE = "org.jnp.interfaces.NamingContextFactory";
                 
                 static Logger logger = Logger.getLogger(TPVMessenger.class);
                 static Logger bbLogger = Logger.getLogger("BBPAGER");
            
                 @In
                 private EntityManager entityManager;
            
                 private MessageProducer producer;
                 private QueueSender sender;
                 
                 private String previousQueue="";
                 private String previousConnectionFactory="";
                 private String previousProvider="";
                 private QueueSession session;
                 private QueueConnection connection;
                 private Queue queue;
                 
                 public TPVMessenger() {}
                 public TPVMessenger(EntityManager mgr) {
                      this.entityManager = mgr;
                 }
            
                 @Create
                 @Transactional
                 public void initialize() {
                      ATAConfiguration globalProps = ATAConfiguration.findAppByName(entityManager, ATAConfiguration.PORTAL_APP_NAME);
                      if(globalProps == null) {
                           logger.info("creating properties");
                           globalProps = new ATAConfiguration();
                           globalProps.setName(ATAConfiguration.PORTAL_APP_NAME);
                           globalProps.setEnabled(true);
                           
                           entityManager.persist(globalProps);
                           
                           ATAJob job = new ATAJob();
                           job.setName("PortalPropertiesSingleAppInstX");
                           globalProps.addAppInstance(job);
                           
                           entityManager.persist(job);
                      }
                      
                      ATAJob appInstance = globalProps.getSingleAppInstance();
                      Map<String, String> properties = appInstance.getProperties();
                      String queueName = properties.get(QUEUE_NAME_KEY);
                      if(queueName == null) {
                           logger.info("creating properties");
                           //stuff the defaults into the database
                           AppProperty prop = new AppProperty();
                           prop.setName(QUEUE_NAME_KEY);
                           prop.setValue("queue/tpv.emailNotification.entryQueue");
                           
                           AppProperty prop2 = new AppProperty();
                           prop2.setName(PROVIDER_URL_KEY);
                           prop2.setValue("jnp://coappcl3n1.bic-bill.com:1099");
                           
                           AppProperty prop3 = new AppProperty();
                           prop3.setName(CONNECTION_FACTORY_KEY);
                           prop3.setValue("ClusteredConnectionFactory");
                      
                           appInstance.addProperty(prop);
                           appInstance.addProperty(prop2);
                           appInstance.addProperty(prop3);
                      }
                      
                      properties = appInstance.getProperties();
                      
                      try {
                           testPropertyChanges(properties); 
                      } catch(Throwable e) {
                           logger.error("Messaging was not started due to error", e);
                      }
                 }
            
                 private synchronized void testPropertyChanges(Map<String, String> properties) {
                      //If the queue already exists and no properties have changed, keep the same connection, queue, etc...
                      if(queue != null
                                && previousQueue.equals(properties.get(QUEUE_NAME_KEY)) 
                                && previousConnectionFactory.equals(properties.get(CONNECTION_FACTORY_KEY))
                                && previousProvider.equals(properties.get(PROVIDER_URL_KEY)))
                           return;
                      
                      //otherwise, change to the new values
                      previousQueue = properties.get(QUEUE_NAME_KEY);
                      previousConnectionFactory =  properties.get(CONNECTION_FACTORY_KEY);
                      previousProvider = properties.get(PROVIDER_URL_KEY);
                      
                      if(queue != null) {
                           //teardown here...
                           try {
                                producer.close();
                                sender.close();
                                session.close();
                                connection.close();
                           } catch (JMSException e) {
                                throw new RuntimeException("asdf", e);
                           }
                      }
                      
                      createQueue();
                 }
            
                 private void createQueue() {
                      Properties p = new Properties();
                      p.setProperty(INITIAL_CONTEXT_FACTORY_KEY, INITIAL_CONTEXT_FACTORY_VALUE);
                      p.setProperty(PROVIDER_URL_KEY, previousProvider);
                      
                      InitialContext initialContext = null;
                      try {
                           logger.info("xxx naming="+previousProvider+" qu="+previousQueue);
                           
                           logger.info("key1111="+INITIAL_CONTEXT_FACTORY_KEY);
                           logger.info("key2222="+PROVIDER_URL_KEY);               
                           logger.info("initCtx="+p.getProperty(INITIAL_CONTEXT_FACTORY_KEY));
                           logger.info("providr="+p.getProperty(PROVIDER_URL_KEY));
                           logger.info("connFac="+previousConnectionFactory);
                           logger.info("queuexx="+previousQueue);
                           initialContext = new InitialContext(p);
                      
                           QueueConnectionFactory queueConnectionFactory =
                                (QueueConnectionFactory) initialContext.lookup(previousConnectionFactory);
            
                           //We need to test failure on one node A and then bring A back up and fail node B
                           //and make sure this connection is still working in that case.  It should be as
                           //a ClusteredConnectionFactory should return a ClusteredConnection implementation...
                           connection = queueConnectionFactory.createQueueConnection();
                           session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
                           
                           logger.info("prevQueue="+previousQueue);
                           queue = (Queue) initialContext.lookup(previousQueue);
                           
                           producer = session.createProducer(queue);
                           producer.setDeliveryMode(DeliveryMode.PERSISTENT);
                           
                           sender = session.createSender(queue);
                           
                           initialContext.close();
                           
                      } catch (JMSException e) {
                           throw new RuntimeException("exc", e);
                      } catch (NamingException e) {
                           throw new RuntimeException("exc2", e);
                      }
                 }
            
                 /**
                  * @param message - String representing the message to be sent.
                  * @throws RuntimeException - If any NamingExcpetions or JMSExceptions 
                  * occur, persist the message and throw a RuntimeException.
                  */
                 public void jmsSend (NotificationMessage message) throws RuntimeException
                 {     
                      ATAConfiguration globalProps = ATAConfiguration.findAppByName(entityManager, ATAConfiguration.PORTAL_APP_NAME);          
                      Map<String, String> properties = globalProps.getSingleAppInstance().getProperties();
                      
                      testPropertyChanges(properties); 
                      
                      final String jmsErrorMsg = "Could not send JMS message";
                      
                      File persistenceDir = new File (persistenceDirName);
                      if (!persistenceDir.exists())
                      {
                           logger.warn ("persistanceDir: " + persistenceDirName + 
                                     " does not exist. Please create this directory");
                      }
                      
                      try {
                           sendMessage(message);
                      
                           //I am guessing order doesn't matter as the persisted messages would be going out
                           //after the message just sent even though someone tried to send them earlier than
                           //the one that just was sent out....
                           recoverMessages();
                           //ALSO, this method swallows any exceptions and reports them so will not cause
                           //persistMessage below to be called which would cause a duplicate send if that occurred
                           
                      } catch (Exception e) {
                           logger.error(jmsErrorMsg, e);
                           bbLogger.error(jmsErrorMsg, e);
                           tearDown();
                           persistMessage (message);
                           throw new RuntimeException (jmsErrorMsg, e);
                      }
                 }
            
                 private void sendMessage(NotificationMessage message) throws JMSException {
                      ObjectMessage objMessage = session.createObjectMessage();//TextMessage( message );
                      objMessage.setObject(message);
                      
                      producer.send(objMessage);
                      //sender.send(objMessage);
                 }
            
                 private void tearDown() {
                      if(producer != null) {
                           try {
                                producer.close();
                           } catch (JMSException e) {
                                logger.error ("Could not close messageproducer");
                           }
                      }
                      if (null != sender)
                      {
                           try
                           {
                                sender.close();
                           } 
                           catch (JMSException e) 
                           {
                                logger.error ("Could not close queueSender");
                           }
                      }
                      if (null != session)
                      {
                           try 
                           {
                                session.close();
                           } 
                           catch (JMSException e) 
                           {
                                logger.error ("Could not close session");
                           }
                      }
                      if (null != connection)
                      {
                           try 
                           {
                                connection.close();
                           } 
                           catch (JMSException e) 
                           {
                                logger.error ("Could not close connection");
                           }
                      }
                      producer = null;
                      sender = null;
                      session = null;
                      connection = null;
                 }
            
                 /**
                  * @param message String representing message to be persisted
                  * @param filename String representing filename to write the message to
                  */
                 private void persistMessage(NotificationMessage message) 
                 {     
                      String path = null;
                     try 
                     {
                           File f = new File(persistenceDirName);
                           if(!f.exists()) {
                                logger.error("Directory does not exist="+f.getAbsolutePath());
                                return;
                           }
                           
                           File file = File.createTempFile("msg_", ".obj", f);
                          path = file.getAbsolutePath();
                         ObjectOutputStream writer = new ObjectOutputStream(new FileOutputStream(file));
                         writer.writeObject(message);
                         writer.close();
                     } 
                     catch (IOException e) 
                     {
                          logger.error("Could not write file " + path + ". Content: " + message + ". Error: ", e);
                     }
                 }
                 
                 /**
                  * @param dirName - Name of the directory that holds all persisted files.
                  */
                 private void recoverMessages ()     {
                      try {
                           File dir = new File (persistenceDirName);
                           if (!dir.exists())          {
                                logger.warn ("Recovery diretory: " + persistenceDirName + "does not exist");
                                return;
                           }
                 
                           String[] files = dir.list();
                           for (int i = 0; i < files.length; i++)     {
                                File file = new File (dir, files[i]);
                                logger.debug ("Recovering: " + files[i]);
                                readSendAndDeleteFile ( file );
                           }
                      } catch(Exception e) {
                           logger.error("Exception recovering", e);
                      }
                 }
                 
                 private void readSendAndDeleteFile ( File file ){
            
                     ObjectInputStream reader;
                      try{
                           reader = new ObjectInputStream(new FileInputStream(file));
                           Object obj = reader.readObject();
                           NotificationMessage message = (NotificationMessage)obj;
                           reader.close();
                           
                           sendMessage(message);
                           logger.debug (message.toString());
                           file.delete();
                      } catch ( FileNotFoundException e ){
                           logger.error("FileNotFoundException: ", e);
                      } catch ( IOException e ){
                           logger.error("IOException: ", e);
                      } catch (ClassNotFoundException e) {
                          logger.error("class not found", e);
                    } catch (JMSException e) {
                         logger.error("very odd seeing a message must have just been sent to be here", e);
                      }
                 }
                 
            //     public void jmsBrowse ( )
            //     {
            //          final String jmsErrorMsg = "Could not broswe JMS messages";
            //
            //          QueueBrowser queueBrowser = null;
            //          
            //          try {
            //               queueBrowser = session.createBrowser(queue);
            //               
            //               connection.start();
            //               
            //               TextMessage textMessage = null;
            //               
            //               Enumeration<?> enumeration = queueBrowser.getEnumeration();
            //               
            //               int i = 1;
            //               while (enumeration.hasMoreElements())
            //               {
            //                    textMessage = (TextMessage) enumeration.nextElement();
            //                    if (textMessage != null)
            //                    {
            //                         System.out.println("Message: " + i);
            //                         System.out.println("\""+ textMessage.getText() +"\"");
            //                    }
            //                    i++;
            //               }
            //
            //               
            //               connection.stop();
            //               queueBrowser.close();
            //               session.close();
            //               connection.close();
            //          } catch (JMSException e) {
            //               logger.error(jmsErrorMsg, e);
            //               bbLogger.error(jmsErrorMsg, e);
            //               throw new RuntimeException (jmsErrorMsg);
            //          } catch (NamingException e) {
            //               logger.error(jmsErrorMsg, e);
            //               bbLogger.error(jmsErrorMsg, e);
            //               throw new RuntimeException (jmsErrorMsg);
            //          }
            //          finally
            //          {
            //               if (null != connection)
            //               {
            //                    try 
            //                    {
            //                         connection.close();
            //                    } 
            //                    catch (JMSException e) 
            //                    {
            //                         logger.error ("Could not close connection");
            //                    }
            //               }
            //               if (null != session)
            //               {
            //                    try 
            //                    {
            //                         session.close();
            //                    } 
            //                    catch (JMSException e) 
            //                    {
            //                         logger.error ("Could not close session");
            //                    }
            //               }
            //               if (null != queueBrowser)
            //               {
            //                    try 
            //                    {
            //                         queueBrowser.close();
            //                    } 
            //                    catch (JMSException e) 
            //                    {
            //                         logger.error ("Could not close queueReceiver");
            //                    }
            //               }
            //          }
            //     }
            }
            
            





            • 3. Re: JMS inside war file fails from seam?
              Dean Hiller Expert

              very odd, finally got it working with ConnectionFactory.  ClusteredConnectionFactory is still not working.  It was definitely not a classloader thing(I combined all classes into one jar and still got failure with ClusteredConnectionFactory).


              I have tried using HA JNDI on jboss and JNDI.  I guess I can eliminate seam and will move to another forum for this question(I think it's not seam at least though it works in our other non-seam war files...so weird)

              • 4. Re: JMS inside war file fails from seam?
                Nikolay Elenkov Master

                Dean Hiller wrote on Jul 15, 2010 16:27:


                very odd, finally got it working with ConnectionFactory.  ClusteredConnectionFactory is still not working.  It was definitely not a classloader thing(I combined all classes into one jar and still got failure with ClusteredConnectionFactory).


                According to the docs, the recommended way to work with ClusteredConnectionFactory is to use <resource-ref> in web xml and than have something like this in jboss-web.xml:


                 <resource-ref>
                    <res-ref-name>jms/ConnectionFactory</res-ref-name>
                    <jndi-name>jnp://${jboss.bind.address}:1100/ClusteredConnectionFactory</jndi-name>
                  </resource-ref>
                



                That might be easier than using properties with InitialContext.



                I have tried using HA JNDI on jboss and JNDI.  I guess I can eliminate seam and will move to another forum for this question(I think it's not seam at least though it works in our other non-seam war files...so weird)


                Do post a link if you do.

                • 5. Re: JMS inside war file fails from seam?
                  Dean Hiller Expert

                  Well, I figured it out finally, and this seems just like a bug to me personally.


                  If I take my local jboss and change out ds.xml file from postgres to oracle, remove my postgres-persistence-service.xml and change to oracle-persistence-service.xml(SO my local uses the same db as the remote servers), it then starts working which is very odd considering two things


                  1. It is a remote queue(NOT a local one) and no local persistence was needed by my unit test case. My unit test case hit postgres just like jboss!!!!


                  2. No storage is used so if the receiving queue is down, I am screwed which is why above we persist on failures to file system(though for better durability should really persist before send and delete after send if it succeeded(and persist to db too)....too bad they don't do it like websphere does so I don't have to write that reliability code above :( ).


                  that was not a fun problem.