Package org.jboss.soa.esb.listeners.message

Source Code of org.jboss.soa.esb.listeners.message.MessageAwareListener$TransactionalRunner

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.soa.esb.listeners.message;

import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.couriers.PickUpOnlyCourier;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.addressing.EPR;
import org.jboss.soa.esb.addressing.MalformedEPRException;
import org.jboss.soa.esb.common.TransactionStrategy;
import org.jboss.soa.esb.common.TransactionStrategyException;
import org.jboss.soa.esb.couriers.CourierException;
import org.jboss.soa.esb.couriers.CourierFactory;
import org.jboss.soa.esb.couriers.CourierTimeoutException;
import org.jboss.soa.esb.couriers.CourierUtil;
import org.jboss.soa.esb.couriers.FaultMessageException;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.soa.esb.listeners.ListenerUtil;
import org.jboss.soa.esb.listeners.RegistryUtil;
import org.jboss.soa.esb.listeners.lifecycle.AbstractThreadedManagedLifecycle;
import org.jboss.soa.esb.listeners.lifecycle.ManagedLifecycleException;
import org.jboss.soa.esb.listeners.lifecycle.ManagedLifecycleThreadState;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.services.registry.RegistryException;
import org.jboss.soa.esb.util.Util;

/**
* Esb Message aware transport independent listener. <p/> Relies on the
* CourierFactory to obtain an appropriate Courier for the EPR this listener
* will be listening on <br/>Keeps a thread pool to instantiate
* ActionProcessingPipelines whenever a Message is received
*
* @author <a
*         href="mailto:schifest@heuristica.com.ar">schifest@heuristica.com.ar</a>
* @since Version 4.0
*/

public class MessageAwareListener  extends AbstractThreadedManagedLifecycle
{
        /**
         * The minimum error delay.
         */
        private static final long MIN_ERROR_DELAY = 1000 ;
        /**
         * The maximum error delay.
         */
        private static final long MAX_ERROR_DELAY = (MIN_ERROR_DELAY << 5) ;

        /**
         * The action pipeline.
         */
        private ActionProcessingPipeline pipeline ;

        /**
         * The error delay.
         */
        private long errorDelay ;

        private TransactionStrategy transactionStrategy;
        private boolean transactional = false;
        private boolean rollbackOnPipelineFaults = true;

        /**
   * public constructor
   *
   * @param config
   *            ConfigTree - Containing 'static' configuration for this
   *            instance
   * @throws ConfigurationException
   */
  public MessageAwareListener(final ConfigTree config)
      throws ConfigurationException
  {
            super(config);
            _config = config ;
            checkMyParms() ;
  }

  /**
   * Check for mandatory and optional attributes in parameter tree
   *
   * @throws ConfigurationException -
   *             if mandatory atts are not right or actionClass not in
   *             classpath
   */
  protected void checkMyParms () throws ConfigurationException
  {
                _eprCategoryName = _config.getAttribute(ListenerTagNames.SERVICE_CATEGORY_NAME_TAG);
                _eprName = _config.getAttribute(ListenerTagNames.SERVICE_NAME_TAG);

                final String maxThreadVal = _config.getAttribute(ListenerTagNames.MAX_THREADS_TAG) ;

                /*
                 * TODO augment this so that per service filters can be
                 * registered in the configuration (and removed by the ESB when
                 * the service is undeployed).
                 *
                 * Do the same for gateways.
                 */
               
                if (!Util.isNullString(maxThreadVal))
                {
                    try
                    {
                        _maxThreads = Integer.parseInt(maxThreadVal) ;
                    }
                    catch (NumberFormatException nfe)
                    {
                        _maxThreads = _defaultMaxThreads ;
                        _logger.warn("Invalid " + ListenerTagNames.MAX_THREADS_TAG + " attribute, defaulting to <" + _maxThreads + ">") ;
                    }
                }

    if (Util.isNullString(_eprCategoryName))
      throw new ConfigurationException(
          "Missing or invalid " + ListenerTagNames.SERVICE_CATEGORY_NAME_TAG);
    if (Util.isNullString(_eprName))
      throw new ConfigurationException(
          "Missing or invalid " + ListenerTagNames.SERVICE_NAME_TAG);

    ConfigTree eprElement = _config.getFirstChild(ListenerTagNames.EPR_TAG);
    if (null == eprElement)
      throw new ConfigurationException(
          "Missing or invalid " + ListenerTagNames.EPR_TAG + " element");
    _epr = ListenerUtil.assembleEpr(eprElement);

                String latency = _config.getAttribute(ListenerTagNames.POLL_LATENCY_SECS_TAG);
                long lSeconds = 10;
                if (null != latency)
                {
                    try
                    {
                        lSeconds = Integer.parseInt(latency);
                    }
                    catch (NumberFormatException e)
                    {
                        _logger.warn("Invalid number format <" + latency + "> using default value (" + lSeconds + ")");
                    }
                }
                _latencySecs = lSeconds ;
               
                transactional = _config.getBooleanAttribute(ListenerTagNames.TRANSACTED_TAG, false) ;
                transactionStrategy = TransactionStrategy.getTransactionStrategy(transactional) ;
               
                rollbackOnPipelineFaults = _config.getBooleanAttribute(ListenerTagNames.ROLLBACK_ON_PIPELINE_FAULTS, true);
  }

        /**
         * Handle the initialisation of the managed instance.
         *
         * @throws ManagedLifecycleException for errors while initialisation.
         */
        protected void doInitialise()
            throws ManagedLifecycleException
        {
            final ActionProcessingPipeline pipeline ;
            try
            {
                pipeline = new ActionProcessingPipeline(_config) ;
                pipeline.setTransactional(transactional);
                pipeline.initialise() ;
            }
            catch (final ConfigurationException ce)
            {
                throw new ManagedLifecycleException("Error configuring action processing pipeline", ce) ;
            }

            try
            {
                RegistryUtil.register(_config, _epr);
            }
            catch (final RegistryException re)
            {
                throw new ManagedLifecycleException("Unexpected error during registration for epr " + _epr, re);
            }

            this.pipeline = pipeline ;
            final PickUpOnlyCourier pickUpCourier ;
            try
            {
                pickUpCourier = getCourier() ;
                cleanCourier(pickUpCourier) ;
            }
            catch (final MalformedEPRException mepre)
            {
                RegistryUtil.unregister(_eprCategoryName, _eprName, _epr) ;
                throw new ManagedLifecycleException("Malformed EPR: " + _epr) ;
            }
            catch (final CourierException ce)
            {
                RegistryUtil.unregister(_eprCategoryName, _eprName, _epr) ;
                throw new ManagedLifecycleException("No appropriate courier can be obtained for " + _epr, ce);
            }
        }

        /**
         * Handle the start of the managed instance.
         *
         * @throws ManagedLifecycleException for errors while starting.
         */
        protected void doStart()
            throws ManagedLifecycleException
        {
            checkExecutorTermination() ;

            _execService = Executors.newFixedThreadPool(_maxThreads) ;

            super.doStart() ;
        }

        /**
         * Execute on the thread.
         */
        protected void doRun()
        {
            if (_logger.isDebugEnabled())
            {
                _logger.debug("doRun() method of " + this.getClass().getSimpleName()
                            + " started on thread " + Thread.currentThread().getName());
            }

            while (isRunning())
            {
                // Only pickup a message when a thread is available
                if (waitForThread(_pauseLapseInMillis))
                {
                    waitForEventAndProcess(100) ;
                }
            }

            if (_logger.isDebugEnabled())
            {
                _logger.debug("run() method of " + this.getClass().getSimpleName()
                            + " finished on thread " + Thread.currentThread().getName());
            }
        }
       
        /**
         * Handle the stop of the managed instance.
         *
         * @throws ManagedLifecycleException for errors while stopping.
         */
        protected void doStop()
            throws ManagedLifecycleException
        {
            super.doStop();
            _execService.shutdown() ;
        }

        /**
         * We have JMS transactional delivery/work semantics: before pulling a unit of work
         * we start a transaction. If the pipeline completes successfully then we will
         * commit that transaction and the OUW will be deleted. If we have to roll back
         * the transaction then the UOW will be placed back on the input "queue" (assumes that
         * the courier is transactional).
         *
         * @param maxWaitMillis
         */
  public void waitForEventAndProcess (long maxWaitMillis)
  {
    Message message = null ;
    boolean problem = false;
   
        PickUpOnlyCourier pickUpCourier = null ;
    try
    {
      transactionStrategy.begin();
     
      pickUpCourier = getCourier() ;
     
      message = (maxWaitMillis > 0) ? pickUpCourier
          .pickup(maxWaitMillis) : null;
                        errorDelay = 0 ;
    }
    catch (TransactionStrategyException ex)
    {
      _logger.error("Could not begin transaction!");
     
      problem = true;
     
      return;
    }
        catch (MalformedEPRException e)
        {
            problem = true;
            return;
        }
    catch (CourierTimeoutException e)
    {
      problem = true;
      return;
    }
    catch (FaultMessageException fme)
    {
      message = fme.getReturnedMessage() ;
    }
    catch (CourierException e)
    {
            _logger.debug("Courier Exception", e);
            if (errorDelay == 0)
            {
                errorDelay = MIN_ERROR_DELAY ;
            }
            else if (errorDelay < MAX_ERROR_DELAY)
            {
                errorDelay <<= 1 ;
            }
            _logger.warn("Error processing courier, backing off for " + errorDelay + " milliseconds") ;
            boolean waitForRunningStateChange = waitForRunningStateChange(ManagedLifecycleThreadState.STOPPING, errorDelay) ;
            _logger.info("State reached : " + waitForRunningStateChange);
      problem = true;
      return;
    }
    finally
    {
      if (problem || (message == null))
      {
          cleanCourier(pickUpCourier) ;
     
        rollbackTransaction();
      }
    }

    if (null != message)
    {
      final Message pipelineMessage = message ;
      final TransactionalRunner txRunner ;
      try
      {
        final Object txHandle = transactionStrategy.suspend();
        txRunner = new TransactionalRunner(pickUpCourier, pipelineMessage, txHandle);
      }
      catch (TransactionStrategyException ex)
      {
        _logger.warn("Caught transaction related exception: ", ex);
        cleanCourier(pickUpCourier);
        rollbackTransaction();
        return ;
      }
     
      updateThreadCount(+1);
      try
      {
        _execService.execute(txRunner);
      }
      catch (final RejectedExecutionException ree)
      {
        txRunner.run() ;
      }
    }
  } // ________________________________

        /**
         * Handle the threaded destroy of the managed instance.
         *
         * @throws ManagedLifecycleException for errors while destroying.
         */
        protected void doThreadedDestroy()
            throws ManagedLifecycleException
        {
            try
            {
                checkExecutorTermination() ;
            }
            catch (final ManagedLifecycleException ex)
            {
                throw ex;
            }
            catch (final Throwable ex)
            {
                _logger.warn("Caught throwable during shutdown: "+ex);
            }

            pipeline.destroy() ;
            pipeline = null ;

            CourierUtil.cleanCourier(_pickUpCourier);
            RegistryUtil.unregister(_eprCategoryName, _eprName, _epr) ;
        }

        /**
         * Check that the existing executor has been closed down.
         * @throws ManagedLifecycleException If executor tasks are still active.
         */
        private void checkExecutorTermination()
            throws ManagedLifecycleException
        {
            if (_execService != null)
            {
                try
                {
                    try
                    {
                        if (!_execService.awaitTermination(getTerminationPeriod(), TimeUnit.MILLISECONDS))
                        {
                            throw new ManagedLifecycleException("Tasks remain active in executor") ;
                        }
                    }
                    catch (final InterruptedException ie)
                    {
                        throw new ManagedLifecycleException("Interrupted waiting for active tasks to terminate") ;
                    }
                }
                finally
                {
                    _execService = null ;
                }
            }
        }

        private boolean waitForThread(final long delay)
        {
            boolean result = true ;
            synchronized(_synchThreads)
            {
                if (_qRunningThreads >= _maxThreads)
                {
                    try
                    {
                        _synchThreads.wait(delay) ;
                    }
                    catch (final InterruptedException ie) {}
                    result = _qRunningThreads < _maxThreads ;
                }
            }
            return result ;
        }

        private void updateThreadCount(Integer i)
        {
            synchronized (_synchThreads)
            {
                _qRunningThreads += i.intValue();
                if (_qRunningThreads < _maxThreads)
                {
                    _synchThreads.notifyAll() ;
                }
            }
        }
       
        private void rollbackTransaction ()
        {
          try
          {
            transactionStrategy.rollbackOnly();
            transactionStrategy.terminate();
          }
          catch (Throwable ex)
          {
            _logger.warn("Problem while attempting to rollback transaction!"); // timeout should catch it next!
          }
        }
       
        protected PickUpOnlyCourier getCourier()
            throws MalformedEPRException, CourierException
        {
            PickUpOnlyCourier pickUpCourier = _pickUpCourier;
            if (transactional || (pickUpCourier == null))
            {
                pickUpCourier = CourierFactory.getPickupCourier(_epr) ;
                try
                {
                    final Method setPollLatency = pickUpCourier.getClass().getMethod(
                        "setPollLatency", new Class[] { Long.class });
                    setPollLatency.invoke(pickUpCourier, new Long(1000 * _latencySecs));
                }
                catch (final NoSuchMethodException nsme)
                {
                        // OK, just leave it null
                }
                catch (final Throwable th)
                {
                    CourierUtil.cleanCourier(pickUpCourier);
                    throw new CourierException("Problems invoking setPollLatency(long)", th);
                }
               
                if (!transactional)
                {
                    _pickUpCourier = pickUpCourier ;
                }
            }

            return pickUpCourier;
        }

        private void cleanCourier(final PickUpOnlyCourier pickUpOnlyCourier)
        {
            if (transactional)
            {
                CourierUtil.cleanCourier(pickUpOnlyCourier) ;
            }
        }

        class TransactionalRunner implements Runnable
        {
          public TransactionalRunner (PickUpOnlyCourier courier, Message pipelineMessage, Object txHandle)
          {
            _courier = courier;
            _pipelineMessage = pipelineMessage;
            _txHandle = txHandle;
          }
         
          public void run()
          {
            boolean problem = false;
           
            try
            {
              if (_txHandle != null)
              {
                  transactionStrategy.resume(_txHandle);
              }
             
              /*
               * Current strategy is to commit as long as process returns true.
               * If fails, or any exceptions are caught, then we roll back.
               *
               * TODO re-examine the semantics around true/false from the pipeline.
               */
             
              // TODO consider adding a RollbackOnFalse option to allow override.
             
              problem = rollbackOnPipelineFaults && !pipeline.process(_pipelineMessage);

              if (!problem)
              {
                transactionStrategy.terminate();
              }
            }
            catch (TransactionStrategyException ex)
            {
              problem = true;
             
              _logger.warn("TransactionalRunner caught transaction exception: ", ex);
            }
            catch (RuntimeException ex)
            {
              problem = true;
             
              throw ex;
            }
            catch (Throwable ex)
            {
              problem = true;
             
              _logger.warn("TransactionalRunner caught throwable: ",ex);
            }
            finally
            {
                cleanCourier(_courier);
              if (problem)
              {
                rollbackTransaction();
              }
             
              updateThreadCount(-1);
            }
          }
         
          private PickUpOnlyCourier _courier;
          private Message _pipelineMessage;
          private Object _txHandle;
        }
       
        private ConfigTree _config;

        private String _eprCategoryName;

        private String _eprName;

        private EPR _epr;

        private int _maxThreads;

        private int _defaultMaxThreads = 1;

        private long _latencySecs;

        private long _pauseLapseInMillis = 50 ;

        private ExecutorService _execService;

        private byte[] _synchThreads = new byte[0];

        private int _qRunningThreads;

        private Logger _logger = Logger.getLogger(MessageAwareListener.class);

        private PickUpOnlyCourier _pickUpCourier;
}
TOP

Related Classes of org.jboss.soa.esb.listeners.message.MessageAwareListener$TransactionalRunner

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.