Package nexj.core.rpc.jms

Source Code of nexj.core.rpc.jms.JMSServerMDB

// Copyright 2010-2011 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.rpc.jms;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.XAQueueConnection;
import javax.jms.XAQueueConnectionFactory;
import javax.jms.XASession;
import javax.transaction.Status;
import javax.transaction.TransactionManager;

import nexj.core.meta.Repository;
import nexj.core.meta.integration.channel.jms.MessageQueue;
import nexj.core.rpc.IntegrationMDB;
import nexj.core.rpc.ServerException;
import nexj.core.rpc.jms.JMSUtil.MessagePropertyUpdater;
import nexj.core.util.Logger;
import nexj.core.util.ObjUtil;

/**
* The JMS server Message Driven Bean
*/
public class JMSServerMDB extends IntegrationMDB implements JMSListener, MessageListener
{
   // constants

   /**
    * Serialization version.
    */
   private final static long serialVersionUID = 6229679996585443650L;

   // associations

   /**
    * The channel. A temporary variable that is set during message delivery and cleared at the end
    * of message delivery.
    */
   protected MessageQueue m_channel;

   /**
    * The MDB logger.
    */
   protected final static Logger s_logger = Logger.getLogger(JMSServerMDB.class);

   /**
    * Message rejection message updater.
    */
   protected final static MessagePropertyUpdater s_redirectionMessageUpdater = new MessagePropertyUpdater()
   {
      public void getProperties(Message message) throws JMSException
      {
      }

      public void setProperties(Message message) throws JMSException
      {
         message.setBooleanProperty(JMS.REDIRECT, false);
      }
   };

   // operations

   /**
    * @see nexj.core.rpc.ServerMDB#getLogger()
    */
   protected Logger getLogger()
   {
      return s_logger;
   }

   /**
    * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
    */
   public void onMessage(Message message)
   {
      boolean bReject = false;
      boolean bRedirect = false;

      try
      {
         bReject = message.propertyExists(JMS.MESSAGE_REJECTED) && message.getBooleanProperty(JMS.MESSAGE_REJECTED);
      }
      catch (JMSException e)
      {
         s_logger.error("Unable to retrieve property " + JMS.MESSAGE_REJECTED + " from message " + JMSUtil.getMessageId(message) + " in " + this, e);
      }

      try
      {
         bRedirect = message.propertyExists(JMS.REDIRECT) && message.getBooleanProperty(JMS.REDIRECT);
      }
      catch (JMSException e)
      {
         s_logger.error("Unable to retrieve property " + JMS.REDIRECT + " from message " + JMSUtil.getMessageId(message) + " in " + this, e);
      }

      try
      {
         m_channel = (MessageQueue)Repository.getMetadata().getChannel(m_sChannelName);

         if (bReject)
         {
            try
            {
               redeliver(message);
            }
            catch (Throwable t)
            {
               s_logger.error("Unable to reject message " + JMSUtil.getMessageId(message) + " in " + this, t);
            }
         }
         else if (bRedirect)
         {
            Connection connection = null;
            Session session = null;

            try
            {
               connection = createConnection(m_channel);
               session = getSession(connection);

               redirect(message, getDestination(m_channel), session, s_redirectionMessageUpdater);
            }
            catch (Throwable t)
            {
               s_logger.error("Unable to redirect message " + JMSUtil.getMessageId(message) + " in class " + this, t);
               bRedirect = false;
            }
            finally
            {
               close(connection, session);
            }
         }

         if (!bReject && !bRedirect)
         {
            try
            {
               MessageListener receiver = (MessageListener)m_channel.getReceiver().getInstance(null);

               if (!message.getJMSRedelivered() || redeliver(message))
               {
                  receiver.onMessage(message);
               }
            }
            catch (Throwable t)
            {
               if (!(t instanceof ServerException) && s_logger.isDebugEnabled())
               {
                  s_logger.debug("Error in " + this, t);
               }

               boolean bDone = false;

               try
               {
                  TransactionManager manager = getTransactionManager();

                  if (manager.getStatus() != Status.STATUS_NO_TRANSACTION)
                  {
                     bDone = true;

                     if (s_logger.isDebugEnabled())
                     {
                        s_logger.debug("Rolling back the transaction in " + this);
                     }

                     manager.setRollbackOnly();
                  }
               }
               catch (Throwable t2)
               {
                  if (s_logger.isDebugEnabled())
                  {
                     s_logger.debug("Unable to mark the transaction for rollback in " + this, t2);
                  }
               }

               if (!bDone)
               {
                  ObjUtil.rethrow(t);
               }
            }
         }
      }
      finally
      {
         m_channel = null;
      }
   }

   /**
    * Redelivers the message.
    * @param message The message to process.
    * @return True if the message should still be delivered to the MessageListener.
    */
   public boolean redeliver(Message message) throws Exception
   {
      boolean bDone = false;
      Connection connection = null;
      Session session = null;
      Connection errorConnection = null;
      Session errorSession = null;

      try
      {
         int nMaxErrorCount = JMSUtil.getMaxErrorCount(m_channel, message);
         int nErrorCount = JMSUtil.getErrorCount(m_channel, message, nMaxErrorCount, true) + 1; // the error count of the next re-submission

         if (nErrorCount > nMaxErrorCount)
         {
            if (m_channel.getErrorQueue() != null)
            {
               if (s_logger.isDebugEnabled())
               {
                  s_logger.debug("Redirecting message " + JMSUtil.getMessageId(message) + " to the error queue of " + this);
                  s_logger.dump(message);
               }

               errorConnection = createConnection(m_channel.getErrorQueue());
               errorSession = createSession(errorConnection, false, Session.AUTO_ACKNOWLEDGE);

               if (errorSession instanceof XASession)
               {
                  TransactionManager tm = getTransactionManager();

                  if (tm.getStatus() == Status.STATUS_ACTIVE)
                  {
                     tm.getTransaction().enlistResource(((XASession)errorSession).getXAResource());
                  }
               }

               redirect(message, getDestination(m_channel.getErrorQueue()), errorSession,
                  new MessagePropertyUpdater()
                  {
                     protected String m_sOldMessageId;
                     protected String m_sOldDestination;

                     public void getProperties(Message message) throws JMSException
                     {
                        m_sOldMessageId = message.getJMSMessageID();

                        Destination destination = message.getJMSDestination();

                        m_sOldDestination = (destination instanceof Queue) ?
                           "queue:" + ((Queue)destination).getQueueName() :
                           "topic:" + ((Topic)destination).getTopicName();
                     }

                     public void setProperties(Message message) throws JMSException
                     {
                        message.setStringProperty(JMS.OLD_MESSAGE_ID, m_sOldMessageId);
                        message.setStringProperty(JMS.OLD_DESTINATION, m_sOldDestination);
                     }
                  });

               bDone = true;
               return false;
            }
         }
         else if (message.propertyExists(JMS.BACKOFF_DELAY))
         {
            if (m_channel.isBroadcast())
            {
               s_logger.error("Message back-off not supported for topics (message " +
                  JMSUtil.getMessageId(message) + " in " + this + ")");
               s_logger.dump(message);
            }
            else
            {
               if (s_logger.isDebugEnabled())
               {
                  s_logger.debug("Delaying message " + JMSUtil.getMessageId(message) + " for " + this);
                  s_logger.dump(message);
               }

               connection = createConnection(m_channel);
               session = getSession(connection);

               redirect(message, getDestination(m_channel), session,
                  new MessagePropertyUpdater()
                  {
                     protected long m_lBackoffDelay;
                     protected long m_lMaxBackoffDelay;
                     protected int m_nErrorCount;

                     public void getProperties(Message message) throws JMSException
                     {
                        m_lBackoffDelay = message.getLongProperty(JMS.BACKOFF_DELAY);

                        if (message.propertyExists(JMS.MAX_BACKOFF_DELAY))
                        {
                           m_lMaxBackoffDelay = message.getLongProperty(JMS.MAX_BACKOFF_DELAY);
                        }

                        if (message.propertyExists(JMS.ERROR_COUNT))
                        {
                           m_nErrorCount = message.getIntProperty(JMS.ERROR_COUNT);
                        }
                     }

                     public void setProperties(Message message) throws JMSException
                     {
                        message.setLongProperty(JMS.BACKOFF_DELAY, limitBackoffDelay(m_lBackoffDelay << 1));
                        message.setIntProperty(JMS.ERROR_COUNT, m_nErrorCount + 1);

                        if (message.propertyExists(JMS.JBOSS_REDELIVERY_COUNT) || message.getClass().getName().startsWith("org.jboss."))
                        {
                           message.setLongProperty(JMS.JBOSS_SCHEDULED_DELIVERY, System.currentTimeMillis() + limitBackoffDelay(m_lBackoffDelay));
                        }
                        else if (message.propertyExists(JMS.ACTIVEMQ_BROKER_IN_TIME))
                        {
                           message.setLongProperty(JMS.ACTIVEMQ_SCHEDULED_DELAY, limitBackoffDelay(m_lBackoffDelay));
                        }
                        else
                        {
                           message.setLongProperty(JMS.DELIVERY_TIME, System.currentTimeMillis() + limitBackoffDelay(m_lBackoffDelay));
                        }
                     }

                     private long limitBackoffDelay(long lBackoffDelay)
                     {
                        if (m_lMaxBackoffDelay <= 0 || lBackoffDelay <= m_lMaxBackoffDelay)
                        {
                           return lBackoffDelay;
                        }

                        return m_lMaxBackoffDelay;
                     }
                  });

               bDone = true;

               return false;
            }
         }

         bDone = true;
      }
      finally
      {
         if (!bDone)
         {
            if (((JMSSender)m_channel.getSender().getInstance(null)).getConnectionFactoryObject() instanceof XAQueueConnectionFactory)
            {
               TransactionManager tm = getTransactionManager();

               switch (tm.getStatus())
               {
                  case Status.STATUS_ACTIVE:
                  case Status.STATUS_MARKED_ROLLBACK:
                     tm.rollback();
                     break;
               }
            }
         }

         close(connection, session);
         close(errorConnection, errorSession);
      }

      return true;
   }

   /**
    * Redirects a message to a given destination.
    * @param message The message to redirect.
    * @param destination The new destination.
    * @param session The session to use for sending the message.
    * @param updater The message property updater strategy.
    */
   protected void redirect(Message message, Destination destination, Session session, MessagePropertyUpdater updater) throws Exception
   {
      MessageProducer producer = null;

      try
      {
         long lExpiration = message.getJMSExpiration();
         long lTTL = (lExpiration == 0) ? Message.DEFAULT_TIME_TO_LIVE : lExpiration - System.currentTimeMillis();

         if (lExpiration != 0 && lTTL <= 0)
         {
            if (s_logger.isDebugEnabled())
            {
               s_logger.debug("Message " + JMSUtil.getMessageId(message) + " expired in " + this);
               s_logger.dump(message);
            }
         }
         else
         {
            int nDeliveryMode = message.getJMSDeliveryMode();
            int nPriority = message.getJMSPriority();

            JMSUtil.setMessageProperties(message, updater);
            producer = session.createProducer(destination);
            producer.send(message, nDeliveryMode, nPriority, lTTL);
         }
      }
      finally
      {
         if (producer != null)
         {
            try
            {
               producer.close();
            }
            catch (Throwable t)
            {
               if (s_logger.isWarnEnabled())
               {
                  s_logger.warn("Unable to close the producer in " + this, t);
               }
            }
         }
      }
   }

   /**
    * Retrieves the destination from the MessageQueue sender.
    * @param mq The message queue from which to retrieve the destination.
    * @return The Destination.
    */
   protected Destination getDestination(MessageQueue mq)
   {
      return ((JMSSender)mq.getSender().getInstance(null)).getDestinationObject();
   }

   /**
    * Creates a JMS connection for a sender for the specified MessageQueue.
    * @param mq The message queue for which to create the connection.
    * @return The JMS connection.
    */
   protected Connection createConnection(MessageQueue mq) throws JMSException
   {
      boolean bDone = false;
      Connection connection = null;
      JMSSender sender = (JMSSender)mq.getSender().getInstance(null);

      try
      {
         ConnectionFactory cf = sender.getConnectionFactoryObject();

         if (mq.getUser() != null)
         {
            connection = cf.createConnection(mq.getUser(), mq.getPassword());
         }
         else
         {
            connection = cf.createConnection();
         }

         if (mq.getClientId() != null)
         {
            connection.setClientID(mq.getClientId());
         }

         connection.start();

         bDone = true;

         return connection;
      }
      catch (JMSException e)
      {
         throw e;
      }
      catch (Throwable t)
      {
         JMSException x = new JMSException("Unable to create connection");

         x.initCause(t);

         throw x;
      }
      finally
      {
         if (!bDone)
         {
            close(connection, null);
         }
      }
   }

   /**
    * Creates a JMS session for m_channel.
   * @param connection The connection from which to create the session.
    * @return The JMS session
    */
   protected Session getSession(Connection connection) throws JMSException
   {
      return createSession(connection, m_channel.isTransactional(), m_channel.getAckMode());
   }

   /**
    * Creates a JMS session for given queue connection.
    * @param connection The JMS connection
    * @param bTransactional True if session should be transactional.
    * @param nAckMode The acknowledgement mode of the session.
    * @return The JMS session.
    */
   protected Session createSession(Connection connection, boolean bTransactional, int nAckMode) throws JMSException
   {
      boolean bDone = false;
      Session session = null;

      try
      {
         if (connection instanceof XAQueueConnection)
         {
            session = ((XAQueueConnection)connection).createXAQueueSession();
         }
         else
         {
            session = connection.createSession(bTransactional, nAckMode);
         }

         bDone = true;

         return session;
      }
      catch (JMSException e)
      {
         throw e;
      }
      catch (Throwable t)
      {
         JMSException x = new JMSException("Unable to create the queue session");

         x.initCause(t);

         throw x;
      }
      finally
      {
         if (!bDone)
         {
            close(connection, session);
         }
      }
   }

   /**
    * Closes JMS objects.
    * @param connection The JMS connection
    * @param session The JMS session
    */
   protected void close(Connection connection, Session session)
   {
      if (session != null)
      {
         try
         {
            session.close();
         }
         catch (Throwable t)
         {
            if (s_logger.isWarnEnabled())
            {
               s_logger.warn("Unable to close queue session of " + this, t);
            }
         }
      }

      if (connection != null)
      {
         try
         {
            connection.close();
         }
         catch (Throwable t)
         {
            if (s_logger.isWarnEnabled())
            {
               s_logger.warn("Unable to close queue connection of " + this, t);
            }
         }
      }
   }

   /**
    * @return The transaction manager
    */
   protected TransactionManager getTransactionManager()
   {
      return (TransactionManager)m_channel.getType().getMetadata()
         .getComponent("System.TransactionManager").getInstance(null);
   }
}
TOP

Related Classes of nexj.core.rpc.jms.JMSServerMDB

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.