Package net.timewalker.ffmq3.remote.session

Source Code of net.timewalker.ffmq3.remote.session.RemoteMessageConsumer

/*
* This file is part of FFMQ.
*
* FFMQ 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 of the License, or
* (at your option) any later version.
*
* FFMQ 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 FFMQ; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
package net.timewalker.ffmq3.remote.session;

import java.util.LinkedList;

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

import net.timewalker.ffmq3.FFMQCoreSettings;
import net.timewalker.ffmq3.client.ClientEnvironment;
import net.timewalker.ffmq3.common.message.AbstractMessage;
import net.timewalker.ffmq3.common.session.AbstractMessageConsumer;
import net.timewalker.ffmq3.storage.message.MessageSerializationLevel;
import net.timewalker.ffmq3.transport.PacketTransportEndpoint;
import net.timewalker.ffmq3.transport.packet.query.CloseConsumerQuery;
import net.timewalker.ffmq3.transport.packet.query.CreateConsumerQuery;
import net.timewalker.ffmq3.transport.packet.query.PrefetchQuery;
import net.timewalker.ffmq3.utils.ErrorTools;
import net.timewalker.ffmq3.utils.Settings;
import net.timewalker.ffmq3.utils.async.AsyncTask;
import net.timewalker.ffmq3.utils.concurrent.Semaphore;
import net.timewalker.ffmq3.utils.id.IntegerID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* RemoteMessageConsumer
*/
public class RemoteMessageConsumer extends AbstractMessageConsumer implements AsyncTask
{
    private static final Log log = LogFactory.getLog(RemoteMessageConsumer.class);
   
    // Parent remote connection
    protected PacketTransportEndpoint transportEndpoint;
   
    // Runtime
    private boolean traceEnabled;
    private LinkedList prefetchQueue = new LinkedList();
    private Semaphore prefetchSemaphore = new Semaphore();
    protected int prefetchCapacity;
   
    // Settings
    private boolean logListenersFailures;
   
    /**
     * Constructor
     */
    public RemoteMessageConsumer(IntegerID consumerId,
                             RemoteSession session,
                                 Destination destination,
                                 String messageSelector,
                                 boolean noLocal) throws JMSException
    {
        super(session,destination,messageSelector,noLocal,consumerId);
        this.transportEndpoint = session.getTransportEndpoint();
        this.traceEnabled = log.isTraceEnabled();
        this.logListenersFailures = getSettings().getBooleanProperty(FFMQCoreSettings.DELIVERY_LOG_LISTENERS_FAILURES, false);
        log.debug("New remote consumer ID is "+consumerId);
    }
       
    /**
     * Initialize the remote endpoint for this consumer
     */
    protected void remoteInit() throws JMSException
    {
        CreateConsumerQuery query = new CreateConsumerQuery();
        query.setConsumerId(id);
        query.setSessionId(session.getId());
        query.setDestination(destination);
        query.setMessageSelector(messageSelector);
        query.setNoLocal(noLocal);
        transportEndpoint.blockingRequest(query);       
    }

    /*
     * (non-Javadoc)
     * @see net.timewalker.ffmq3.common.session.AbstractMessageConsumer#shouldLogListenersFailures()
     */
    protected final boolean shouldLogListenersFailures()
    {
        return logListenersFailures;
    }
   
    protected final Settings getSettings()
    {
        return ClientEnvironment.getSettings();
    }
   
    /* (non-Javadoc)
     * @see javax.jms.MessageConsumer#setMessageListener(javax.jms.MessageListener)
     */
    public final void setMessageListener(MessageListener messageListener) throws JMSException
    {
        super.setMessageListener(messageListener);
       
        // If the connection was already started, wake up the new listener in case there were messages
        // waiting in the destination
        if (messageListener != null && session.getConnection().isStarted())
          wakeUpMessageListener();
    }
   
    /* (non-Javadoc)
     * @see net.timewalker.ffmq3.common.session.AbstractMessageConsumer#onConsumerClose()
     */
    protected final void onConsumerClose()
    {
      super.onConsumerClose();

      // Unlock waiting threads
      prefetchSemaphore.release();
     
      try
      {
          CloseConsumerQuery query = new CloseConsumerQuery();
        query.setSessionId(session.getId());
        query.setConsumerId(id);
       
        // Append message IDs to rollback
        synchronized (prefetchQueue)
            {
            while (!prefetchQueue.isEmpty())
            {
                Message msg = (Message)prefetchQueue.removeFirst();
                query.addUndeliveredMessageID(msg.getJMSMessageID());
            }
            }
       
        transportEndpoint.blockingRequest(query);
      }
        catch (JMSException e)
        {
            ErrorTools.log(e, log);
        }
    }
   
    public final boolean addToPrefetchQueue( AbstractMessage prefetchedMessage , int prefetchCapacity )
    {
        synchronized (externalAccessLock)
        {
            if (closed)
                return false; // Consumer is closed, refuse prefetched messages
           
          synchronized (prefetchQueue)
        {
          if (traceEnabled)
                log.trace("#"+id+" [PREFETCHED] from "+destination+" - "+prefetchedMessage);
         
            prefetchQueue.add(prefetchedMessage);
            this.prefetchCapacity = prefetchCapacity;
        }
        }
        prefetchSemaphore.release();
       
    try
    {
      if (messageListener != null)
        ClientEnvironment.getAsyncTaskManager().execute(this);
    }
    catch (JMSException e)
    {
      ErrorTools.log(e, log);
    }
   
    return true;
    }
   
    private AbstractMessage getFromPrefetchQueue( long timeout )
    {
      // Wait for a message to be available
      if (!prefetchSemaphore.tryAcquire(timeout))
      return null;
       
      // Get the message from the queue
      boolean shouldPrefetchMore = false;
      AbstractMessage message;
      synchronized (externalAccessLock)
        {
        if (closed)
          return null; // [JMS SPEC]
       
        synchronized (prefetchQueue)
          {
          // Consistency check
            if (prefetchQueue.isEmpty())
              throw new IllegalStateException("Prefetch queue is empty");
           
            message = (AbstractMessage)prefetchQueue.removeFirst();
           
            // If we have consumed all the prefetched messages, ask for more !
            if (prefetchCapacity == 0 && prefetchQueue.isEmpty())
              shouldPrefetchMore = true;
          }
        }
   
      // Ask for more messages if necessary (asynchronous)
      if (shouldPrefetchMore)
      {
        try
        {
          prefetchFromDestination();
        }
        catch (JMSException e)
        {
          log.error("Cannot prefetch more messages from remote server",e);
        }
      }
     
      ((RemoteSession)session).addDeliveredMessageID(message.getJMSMessageID());
     
       if (traceEnabled)
            log.trace("#"+id+" [GET PREFETCHED] in "+destination+" - "+message);

        // Make sure the message is fully deserialized and marked as read-only
       message.ensureDeserializationLevel(MessageSerializationLevel.FULL);
    message.markAsReadOnly();
   
        return message;
    }
   
    /**
     * Prefetch messages from destination
     * @param timeout
     * @throws JMSException
     */
    private void prefetchFromDestination() throws JMSException
    {
        // Lazy test, do not synchronize here but on response (see addToPrefetchQueue())
      if (closed)
            return;

      if (traceEnabled)
        log.trace("#"+id+" Prefetching more from destination "+destination);
     
      // Ask for more
        PrefetchQuery query = new PrefetchQuery();
        query.setSessionId(session.getId());
        query.setConsumerId(id);
        transportEndpoint.nonBlockingRequest(query);
    }
   
    /* (non-Javadoc)
     * @see net.timewalker.ffmq3.common.session.AbstractMessageConsumer#receiveFromDestination(long, boolean)
     */
    protected final AbstractMessage receiveFromDestination(long timeout, boolean duplicateRequired) throws JMSException
    {
        if (closed)
            return null; // [JMS SPEC]

        // Get something from preftech queue
        return getFromPrefetchQueue(timeout);
    }
   
    /*
     * (non-Javadoc)
     * @see net.timewalker.ffmq3.common.MessageAvailabilityListener#messageAvailable()
     */
    public final void messageAvailable()
    {
      // Check that the consumer is not closed
    if (closed)
      return;
       
        // Check that the connection is started
        if (!session.getConnection().isStarted())
            return;
         
        if (messageListener != null)
        {
          wakeUpMessageListener();
        }
        else
          throw new IllegalStateException("Unexpected message availability notification");
    }
   
    /* (non-Javadoc)
     * @see net.timewalker.ffmq3.utils.async.AsyncTask#execute()
     */
    public final void execute()
    {
      messageAvailable();
    }
   
    /* (non-Javadoc)
     * @see net.timewalker.ffmq3.utils.async.AsyncTask#isMergeable()
     */
    public final boolean isMergeable()
    {
      return true;
    }
}
TOP

Related Classes of net.timewalker.ffmq3.remote.session.RemoteMessageConsumer

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.