Package hermes.impl

Source Code of hermes.impl.DefaultHermesDispatcherImpl

/*
* Copyright 2003,2004 Colin Crist
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package hermes.impl;

import hermes.HermesDispatcher;
import hermes.HermesException;
import hermes.HermesMessageListener;
import hermes.util.JMSUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.apache.log4j.Category;

/**
* @author colincrist@hermesjms.com
* @version $Id: DefaultHermesDispatcherImpl.java,v 1.1 2004/05/01 15:52:35
*          colincrist Exp $
*/

public class DefaultHermesDispatcherImpl implements HermesDispatcher, Runnable
{
   private static final Category cat = Category.getInstance(DefaultHermesDispatcherImpl.class);
   private static int numDispatchers = 0;
   private Map<DestinationKeyWrapper, MessageListener> destinations = new HashMap<DestinationKeyWrapper, MessageListener>();
   private Set<DestinationKeyWrapper> removedDestinations = new HashSet<DestinationKeyWrapper>();
   private List queue = new ArrayList();
   private DefaultHermesImpl hermes;
   private boolean keepRunning = true;
   private long sleepPeriod = 50;
   private Thread dispatchThread;
   private boolean synchronizeThreadStart = false;

   /**
    * Constructor for Dispatcher.
    */
   public DefaultHermesDispatcherImpl(DefaultHermesImpl hermes)
   {
      super();

      this.hermes = hermes;
   }

   /**
    * Constructor for Dispatcher.
    */
   public DefaultHermesDispatcherImpl(DefaultHermesImpl hermes, boolean synchronizeThreadStart)
   {
      super();

      this.hermes = hermes;
      this.synchronizeThreadStart = synchronizeThreadStart;
   }

   private final String getName(Destination d) throws JMSException
   {
      return hermes.getDestinationName(d);
   }

   /**
    * Add a desination and message listener for messages to be dispatched to.
    */
   public void addDestination(Destination d, MessageListener ml) throws JMSException
   {
      synchronized (destinations)
      {
         if (dispatchThread == null)
         {
            start();
         }

         destinations.put(new DestinationKeyWrapper(d), ml);
      }

      cat.debug("new destination: " + getName(d));
   }

   /**
    * Remove a destination from dispatching
    */
   public void removeDestination(final Destination d) throws JMSException
   {
      synchronized (destinations)
      {
         DestinationKeyWrapper key = new DestinationKeyWrapper(d) ;
         if (destinations.remove(key) == null)
         {
            //
            // If the thread is running give some extra error information

            if (dispatchThread != null)
            {
               throw new JMSException("destination " + getName(d) + " not being dispatched on " + dispatchThread.getName());
            }
            else
            {
               throw new JMSException("destination " + getName(d) + " not registered");
            }
         }
         else
         {
            removedDestinations.add(key);
         }
      }

      cat.debug("removed destination: " + JMSUtils.getDestinationName(d));
   }

   /**
    * Dispatch a runnable on this thread at the next opportunity, you can add
    * events to be dispatched even though the object is not running on a thread,
    * they will be queued.
    */
   public void invoke(Runnable runnable) throws JMSException
   {
      synchronized (queue)
      {
         queue.add(runnable);
         queue.notifyAll();
      }
   }

   /**
    * Thread mainline, alternate between pulling events off the dispatch queue
    * as well as from the thread local session from the Hermes impl.
    */
   public void run()
   {
      //
      // Cache the thread and notify anyone who's waiting to be informed that
      // the dispatcher is up and running

      dispatchThread = Thread.currentThread();

      synchronized (dispatchThread)
      {
         dispatchThread.notifyAll();
      }

      cat.debug("dispatcher starting");

      while (keepRunning)
      {
         int messagesRead = 0;

         //
         // Drain any internal events, if message dispatching took a long
         // time you may get several
         // timer triggered runnables of the same type bunched up together.

         synchronized (queue)
         {
            while (queue.size() > 0)
            {
               Runnable r = (Runnable) queue.remove(0);

               r.run();
            }
         }

         //
         // Drain messages, just do a single pass accross all the
         // destinations so that we don't
         // starve any Runnable's on the queue.

         synchronized (destinations)
         {
            if (removedDestinations.size() > 0)
            {
               for (DestinationKeyWrapper key : removedDestinations)
               {
                
                  destinations.remove(key);

                  try
                  {
                     hermes.closeConsumer(key.getDestination());
                  }
                  catch (JMSException e)
                  {
                     cat.error("closing async consumer: " + e.getMessage(), e);
                  }
               }

               removedDestinations.clear();
            }

            if (destinations.size() == 0 && hermes.isOpen())
            {
               try
               {
                  cat.debug("nothing to dispatch, closing Hermes " + hermes.getId());

                  hermes.close();
               }
               catch (JMSException e)
               {
                  cat.error(e.getMessage(), e);
               }
            }
            else
            {
               for (Map.Entry<DestinationKeyWrapper, MessageListener> entry : destinations.entrySet())
               {
                  MessageListener ml = null;

                  try
                  {
                     ml = entry.getValue();

                     final Destination d = (Destination) entry.getKey().getDestination() ;
                     final Message m = hermes.receiveNoWait(d);

                     if (m != null)
                     {
                        if (ml != null)
                        {
                           ml.onMessage(m);
                        }
                        else
                        {
                           cat.fatal("no message listener available for destination " + hermes.getDestinationName(d) + " message discarded");
                        }

                        messagesRead++;
                     }
                  }
                  catch (JMSException ex)
                  {
                     cat.error(ex.getMessage(), ex);
                     removedDestinations.add(entry.getKey()) ;

                     if (ml instanceof HermesMessageListener)
                     {
                        ((HermesMessageListener) ml).onException(ex);
                     }
                    
                     break ;
                  }
               }
            }
         }

         //
         // Only sleep if nothing happened last time round otherwise you'll
         // soak the CPU.

         if (messagesRead == 0)
         {
            try
            {
               Thread.sleep(sleepPeriod);
            }
            catch (InterruptedException e)
            {
               // NOP
            }
         }
      }

      dispatchThread = null;
      cat.debug("dispatcher stopping");
   }

   /**
    * Returns the sleepPeriod which is the time for the thread to sleep if no
    * messages where dispatched on the last poll of all the destinations
    * registered.
    *
    * @return long
    */
   public long getSleepPeriod()
   {
      return sleepPeriod;
   }

   /**
    * Sets the sleepPeriod which is the time for the thread to sleep if no
    * messages where dispatched on the last poll of all the destinations
    * registered.
    *
    * @param sleepPeriod
    *           The sleepPeriod to set
    */
   public void setSleepPeriod(long sleepPeriod)
   {
      this.sleepPeriod = sleepPeriod;
   }

   /**
    * @see hermes.HermesDispatcher#invokeAndWait(Runnable)
    */
   public void invokeAndWait(final Runnable runnable) throws JMSException
   {
      if (dispatchThread == null)
      {
         throw new HermesException("dispatcher thread not running so cannot invokeAndWait");
      }

      Runnable myRunnable = new Runnable()
      {
         public void run()
         {
            runnable.run();

            synchronized (this)
            {
               notifyAll();
            }
         }
      };

      synchronized (myRunnable)
      {
         invoke(myRunnable);

         try
         {
            myRunnable.wait();
         }
         catch (Exception ex)
         {
            cat.error(ex.getMessage(), ex);
         }
      }
   }

   /**
    * Helper to start the dispatcher on a thread. If synchronizeThreadStart is
    * set then this will not return until this Runnable is active on the thread -
    * this ensures that any subsequent dispatchAndWait() will not throw a
    * DispatcherNotRunningException
    */
   public Thread start() throws JMSException
   {
      synchronized (DefaultHermesDispatcherImpl.class)
      {
         return start("dispatcher-" + numDispatchers++);
      }
   }

   /**
    * Helper to start the dispatcher on a thread. If synchronizeThreadStart is
    * set then this will not return until this Runnable is active on the thread -
    * this ensures that any subsequent dispatchAndWait() will not throw a
    * DispatcherNotRunningException
    *
    * @param threadName
    *           The name to assign the thread.
    */
   public Thread start(String threadName) throws JMSException
   {
      if (dispatchThread != null)
      {
         throw new HermesException("Dispatcher thread not running");
      }

      Thread thread = new Thread(this, threadName);

      synchronized (thread)
      {
         thread.start();

         if (synchronizeThreadStart)
         {
            try
            {
               thread.wait();
            }
            catch (InterruptedException ex)
            {
               cat.error(ex.getMessage(), ex);
            }
         }
      }

      return thread;
   }

   /**
    * @see hermes.HermesDispatcher#close()
    */
   public void close() throws JMSException
   {
      keepRunning = false;

      hermes.removeDispatcher(this);
   }

   /**
    * @see hermes.HermesDispatcher#setMessageListener(Destination,
    *      MessageListener)
    */
   public void setMessageListener(Destination from, MessageListener ml) throws JMSException
   {
      if (ml != null)
      {
         addDestination(from, ml);
      }
      else
      {
         removeDestination(from);
      }
   }

}
TOP

Related Classes of hermes.impl.DefaultHermesDispatcherImpl

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.