Package org.objectweb.joram.client.jms

Source Code of org.objectweb.joram.client.jms.MessageConsumerListener$ReceiveStatus

/*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging
* Copyright (C) 2001 - 2008 ScalAgent Distributed Technologies
* Copyright (C) 1996 - 2000 Dyade
*
* This library 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 any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
*/
package org.objectweb.joram.client.jms;

import java.util.Vector;

import javax.jms.MessageListener;
import javax.jms.JMSException;

import org.objectweb.joram.shared.client.AbstractJmsReply;
import org.objectweb.joram.shared.client.ConsumerMessages;
import org.objectweb.joram.shared.client.ConsumerSetListRequest;
import org.objectweb.joram.shared.client.ConsumerUnsetListRequest;
import org.objectweb.joram.shared.client.ConsumerAckRequest;
import org.objectweb.joram.shared.client.ActivateConsumerRequest;
import org.objectweb.joram.client.jms.connection.ReplyListener;
import org.objectweb.joram.client.jms.connection.AbortedRequestException;
import org.objectweb.joram.client.jms.connection.RequestMultiplexer;

import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.common.StoppedQueueException;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
* This class listens to replies asynchronously returned by the user proxy
* for a message consumer.
*/
abstract class MessageConsumerListener implements ReplyListener {
 
  public static Logger logger = Debug.getLogger(MessageConsumerListener.class.getName());

  /**
   * Status of the message consumer listener.
   */
  protected static class Status {
    public static final int INIT = 0;
    public static final int RUN = 1;
    public static final int ON_MSG = 2;
    public static final int CLOSE = 3;

    private static final String[] names = {
      "INIT", "RUN", "ON_MSG", "CLOSE"};

    public static String toString(int status) {
      return names[status];
    }
  }
 
  private static class ReceiveStatus {
    public static final int INIT = 0;
   
    public static final int WAIT_FOR_REPLY = 1;

    public static final int CONSUMING_REPLY = 2;

    private static final String[] names = {
        "INIT", "WAIT_FOR_REPLY", "CONSUMING_REPLY" };

    public static String toString(int status) {
      return names[status];
    }
  }

  private boolean queueMode;
 
  private boolean durable;
 
  private String selector;
 
  private String targetName;

  /**
   * The identifier of the subscription request.
   */
  private volatile int requestId;

  private int status;

  private Vector messagesToAck;
 
  /**
   * The number of messages which are in queue (Session.qin)
   * waiting for being consumed.
   */
  private volatile int messageCount;
 
  /**
   * The receive status of this message listener:
   *  - WAIT_FOR_REPLY if a reply is expected from the destination
   *  - CONSUMING_REPLY if a reply is being consumed and no new request has
   *    been sent
   */
  private volatile int receiveStatus;
 
  /**
   * Indicates whether the topic message input has been passivated or not.
   */
  private boolean topicMsgInputPassivated;
 
  private int queueMessageReadMax;
 
  private RequestMultiplexer rm;
 
  private int topicActivationThreshold;
 
  private int topicPassivationThreshold;
 
  private int topicAckBufferMax;
 
  private MessageListener listener;
 
  MessageConsumerListener(boolean queueMode,
                          boolean durable,
                          String selector,
                          String targetName,
                          MessageListener listener,
                          int queueMessageReadMax,
                          int topicActivationThreshold,
                          int topicPassivationThreshold,
                          int topicAckBufferMax,
                          RequestMultiplexer reqMultiplexer) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG,
                 "MessageConsumerListener(" + queueMode +
                 ',' + durable + ',' + selector + ',' + targetName +
                 ',' + listener + ',' + queueMessageReadMax +
                 ',' + topicActivationThreshold +
                 ',' + topicPassivationThreshold +
                 ',' + topicAckBufferMax + ',' + reqMultiplexer + ')');
    this.queueMode = queueMode;
    this.durable = durable;
    this.selector = selector;
    this.targetName = targetName;
    this.listener = listener;
    this.queueMessageReadMax = queueMessageReadMax;
    this.topicActivationThreshold = topicActivationThreshold;
    this.topicPassivationThreshold = topicPassivationThreshold;
    this.topicAckBufferMax = topicAckBufferMax;
    rm = reqMultiplexer;
    messagesToAck = new Vector(0);
    requestId = -1;
    messageCount = 0;
    topicMsgInputPassivated = false;
    setStatus(Status.INIT);
    setReceiveStatus(ReceiveStatus.INIT);
  }
 
  protected final int getStatus() {
    return status;
  }

  protected void setStatus(int status) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG,
                 "MessageConsumerListener.setStatus(" + Status.toString(status) + ')');
    this.status = status;
  }
 
  private void setReceiveStatus(int s) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG,
                 "MessageConsumerListener.setReceiveStatus(" + ReceiveStatus.toString(s) + ')');
    receiveStatus = s;
  }
 
  /**
   * Decrease the message count.
   * Synchronized with the method replyReceived() that increments the
   * messageCount += cm.getMessageCount();
   *
   * @return the decreased value
   */
  private int decreaseMessageCount(int ackMode) throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG,
                 "MessageConsumerListener.decreaseMessageCount()");
   
    synchronized (this) {
      messageCount--;
    }
   
    if (queueMode) {
      boolean subscribe = false;
      String[] toAck = null;
      synchronized (this) {
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, " -> messageCount = " + messageCount);
        // Consume in advance (default is one message in advance)
        if (messageCount < queueMessageReadMax
            && receiveStatus == ReceiveStatus.CONSUMING_REPLY) {
          subscribe = true;
          if (ackMode == javax.jms.Session.DUPS_OK_ACKNOWLEDGE) {
            synchronized (messagesToAck) {
              if (messagesToAck.size() > 0) {
                toAck = new String[messagesToAck.size()];
                messagesToAck.copyInto(toAck);
                messagesToAck.clear();
              }
            }
          }
        }
      }
      if (subscribe) {
        // out of the synchronized block
        subscribe(toAck);
      }
    } else {
      synchronized (this) {
        if (topicMsgInputPassivated) {
          if (messageCount < topicActivationThreshold) {
            activateMessageInput();
            topicMsgInputPassivated = false;
          }
        } else {
          if (messageCount > topicPassivationThreshold) {
            passivateMessageInput();
            topicMsgInputPassivated = true;
          }
        }
      }
    }
   
    if (ackMode == javax.jms.Session.DUPS_OK_ACKNOWLEDGE
        && messageCount == 0) {
      // Need to acknowledge the received messages
      // if we are in lazy mode (DUPS_OK)
      acknowledge(0);
    }
   
    return messageCount;
  }
  /**
   * Called by Session.
   */
  synchronized void start() throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(
        BasicLevel.DEBUG, "MessageConsumerListener.start()");
    if (status == Status.INIT) {
      subscribe(null);
      setStatus(Status.RUN);
    } else {
      // Should not happen
      throw new IllegalStateException("Status error");
    }
  }

  private void subscribe(String[] toAck) throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(
        BasicLevel.DEBUG, "MessageConsumerListener.subscribe()");
   
    ConsumerSetListRequest req =
      new ConsumerSetListRequest(
        targetName,
        selector,
        queueMode,
        toAck,
        queueMessageReadMax);
   
    // Change the receive status before sending
    // the request. subscribe() is not synchronized
    // so the reply can be received before the end
    // of this method.
    setReceiveStatus(ReceiveStatus.WAIT_FOR_REPLY);
    rm.sendRequest(req, this);
    requestId = req.getRequestId();
  }

  /**
   * Called by Session.
   */
  public void close() throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(
        BasicLevel.DEBUG, "MessageConsumerListener.close()");

    synchronized (this) {
      while (status == Status.ON_MSG) {
        try {
          // Wait for the message listener to return from
          // onMessage()
          wait();
        } catch (InterruptedException exc) {}
      }
     
      if (status == Status.INIT ||
          status == Status.CLOSE) return;
     
      rm.abortRequest(requestId);

      // If session ack mode is DUPS_OK
      acknowledge(0);

      setStatus(Status.CLOSE);
    }
   
    if (queueMode) {
      // Out of the synchronized block because it could
      // lead to a dead lock with
      // the connection driver thread calling replyReceived.
      ConsumerUnsetListRequest unsetLR = new ConsumerUnsetListRequest(
          queueMode);
      unsetLR.setTarget(targetName);
      unsetLR.setCancelledRequestId(requestId);
      rm.sendRequest(unsetLR);
    }
    // else useless for a topic
    // because the subscription
    // is deleted (see MessageConsumer.close())
  }

  private void acknowledge(int threshold) {
    try {
      synchronized (messagesToAck) {
        if (messagesToAck.size() > threshold) {
          ConsumerAckRequest ack = new ConsumerAckRequest(
              targetName,
              queueMode);
          for (int i = 0; i < messagesToAck.size(); i++) {
            String msgId = (String) messagesToAck.elementAt(i);
            ack.addId(msgId);
          }
          rm.sendRequest(ack);
          messagesToAck.clear();
        }
      }
    } catch (JMSException exc) {
      if (logger.isLoggable(BasicLevel.ERROR))
        logger.log(
          BasicLevel.ERROR, "", exc);
    }
  }
 
  /**
   * Called by RequestMultiplexer.
   */
  public synchronized boolean replyReceived(AbstractJmsReply reply)
    throws AbortedRequestException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(
        BasicLevel.DEBUG, "MessageConsumerListener.replyReceived(" +
        reply + ')');
   
    if (status == Status.CLOSE)
      throw new AbortedRequestException();

    if (queueMode) {
      // 1- Change the status before pushing the  messages into the session queue.
      setReceiveStatus(ReceiveStatus.CONSUMING_REPLY);
    }
   
    try {
      ConsumerMessages cm = (ConsumerMessages)reply;
      // 2- increment messageCount (synchronized)
      messageCount += cm.getMessageCount();
     
      pushMessages(cm);
    } catch (StoppedQueueException exc) {
      throw new AbortedRequestException();
    } catch (JMSException exc) {
      throw new AbortedRequestException();
    }
   
    if (queueMode) {
      return true;
    }
    return false;
  }
 
  /**
   * Pushes the received messages.
   * Currently two behaviors:
   * 1- SingleSessionConsumer pushes the message
   * in a single session (standard JMS)
   * 2- MultiSessionConsumer pushes the message
   * in several session (from a session pool)
   *
   * @param cm
   */
  public abstract void pushMessages(ConsumerMessages cm) throws JMSException;
 
  public void replyAborted(int requestId) {
    // Nothing to do.
  }

  public synchronized boolean isClosed() {
    return (status == Status.CLOSE);
  }
 
  public final MessageListener getMessageListener() {
    return listener;
  }
 
  public final boolean getQueueMode() {
    return queueMode;
  }
 
  public final String getTargetName() {
    return targetName;
  }

 
  protected void activateListener(
      Message msg, MessageListener listener, int ackMode)
    throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG,
                 "MessageConsumerListener.onMessage(" +  msg + ')');
   
    // Consume one message
    decreaseMessageCount(ackMode);

    try {
      listener.onMessage(msg);

      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG,
                   " -> consumer.onMessage(" + msg + ") returned");
    } catch (RuntimeException re) {
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, "", re);
      JMSException exc = new JMSException(re.toString());
      exc.setLinkedException(re);
      throw exc;
    }
  }
 
  public abstract void onMessage(
      Message msg, MessageListener listener, int ackMode)
    throws JMSException;
 
  /**
   * Called by Session (standard JMS, mono-threaded
   */
  public void onMessage(Message msg, int ackMode) throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "MessageConsumerListener.onMessage(" + msg + ')');
    if (listener != null) {
        synchronized (this) {
          if (status == Status.RUN) {
            setStatus(Status.ON_MSG);
          } else {
            // Notify threads trying to close the listener.
          notifyAll();
            throw new javax.jms.IllegalStateException("Message listener closed");
          }
        }

      try {
        activateListener(msg, listener, ackMode);
      } finally {
        synchronized (this) {
          if (status == Status.ON_MSG)
            setStatus(Status.RUN);

          // Notify threads trying to close the listener.
          notifyAll();
        }
      }
    } else {
      throw new JMSException("Null listener");
    }
  }

  void ack(String msgId, int ackMode)
      throws JMSException {
    if (ackMode == javax.jms.Session.DUPS_OK_ACKNOWLEDGE) {
      // All the operations on messagesToAck are synchronized
      // on the vector (see subscribe() and acknowledge()).
      messagesToAck.addElement(msgId);
      if (! queueMode) {
        acknowledge(topicAckBufferMax);
      }
    } else {
      ConsumerAckRequest ack = new ConsumerAckRequest(targetName, queueMode);
      ack.addId(msgId);
      rm.sendRequest(ack);
    }
  }

  void activateMessageInput() throws JMSException {
    rm.sendRequest(
      new ActivateConsumerRequest(targetName, true));
  }

  void passivateMessageInput() throws JMSException {
    rm.sendRequest(
      new ActivateConsumerRequest(targetName, false));
  }
}
TOP

Related Classes of org.objectweb.joram.client.jms.MessageConsumerListener$ReceiveStatus

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.