Package org.objectweb.joram.client.jms

Source Code of org.objectweb.joram.client.jms.MessageConsumer$Status

/*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging
* Copyright (C) 2001 - 2009 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): Frederic Maistre (INRIA)
* Contributor(s): ScalAgent Distributed Technologies
*/
package org.objectweb.joram.client.jms;

import javax.jms.IllegalStateException;
import javax.jms.InvalidDestinationException;
import javax.jms.InvalidSelectorException;
import javax.jms.JMSException;
import javax.jms.JMSSecurityException;

import org.objectweb.joram.shared.client.ConsumerCloseSubRequest;
import org.objectweb.joram.shared.client.ConsumerSubRequest;
import org.objectweb.joram.shared.client.ConsumerUnsubRequest;
import org.objectweb.joram.shared.selectors.ClientSelector;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

import fr.dyade.aaa.common.Debug;

/**
* Implements the <code>javax.jms.MessageConsumer</code> interface.
* <p>
* A client uses a MessageConsumer object to receive messages from a
* destination. A MessageConsumer object is created by calling the
* createConsumer method on a session object. A message consumer is
* normally dedicated to a unique destination.
* <br>
* A message consumer can be created with a message selector. A message
* selector allows the client to restrict the messages delivered to the
* message consumer to those that match the selector.
* <br>
* A client may either synchronously receive a message consumer's messages or
* have the consumer asynchronously deliver them as they arrive:<ul>
* <li>For synchronous receipt, a client can request the next message from
* the message consumer using one of its receive methods. There are several
* variations of receive that allow a client to poll or wait for the next
* message.</li>
* <li>For asynchronous delivery, a client must register a MessageListener
* object with a message consumer. As messages arrive at the message consumer,
* it delivers them by calling the MessageListener's onMessage method. It is a
* client programming error for a MessageListener to throw an exception.</li>
* </ul>
*
*/
public class MessageConsumer implements javax.jms.MessageConsumer {
  /**
   * Status of the message consumer.
   */
  private static class Status {
    /**
     * Status of the message consumer
     * when it is open. It is the initial state.
     */
    public static final int OPEN = 0;
   
    /**
     * Status of the message consumer when it is
     * closed.
     */
    public static final int CLOSE = 1;
   
    private static final String[] names = {
      "OPEN", "CLOSE"};
   
    public static String toString(int status) {
      return names[status];
    }
  }

  private static Logger logger = Debug.getLogger(MessageConsumer.class.getName());

  /** The selector for filtering messages. */
  String selector;

  /** <code>true</code> for a durable subscriber. */
  private boolean durableSubscriber;

  /** The destination the consumer gets its messages from. */
  protected Destination dest;

  /**
   * <code>true</code> if the subscriber does not wish to consume messages
   * produced by its connection.
   */
  protected boolean noLocal;

  /** The session the consumer belongs to. */
  protected Session sess;

  /**
   * The consumer server side target is either a queue or a subscription on
   * its proxy.
   */
  String targetName;

  /** <code>true</code> if the consumer is a queue consumer. */
  boolean queueMode;

  /**
   * Message listener context (null if no message listener).
   */
  private MessageConsumerListener mcl;

  /**
   * Status of the message consumer
   * OPEN, CLOSE
   */
  private int status;

  /**
   * Used to synchronize the method close()
   */
  private Closer closer;

  /**
   * Constructs a consumer.
   *
   * @param sess  The session the consumer belongs to.
   * @param dest  The destination the consumer gets messages from.
   * @param selector  Selector for filtering messages.
   * @param subName  The durableSubscriber subscription's name, if any.
   * @param noLocal  <code>true</code> for a subscriber not wishing to consume
   *          messages produced by its connection.
   *
   * @exception InvalidDestinationException if an invalid destination is specified.
   * @exception InvalidSelectorException  If the selector syntax is invalid.
   * @exception IllegalStateException  If the connection is broken, or if the
   *                                   subscription is durable and already
   *                                   activated.
   * @exception JMSException           Generic exception.
   */
  MessageConsumer(Session sess,
                  Destination dest,
                  String selector,
                  String subName,
                  boolean noLocal) throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "MessageConsumer.<init>(" + sess + ',' + dest + ',' + selector + ','
          + subName + ',' + noLocal + ')');
   
    if (dest == null)
      throw new InvalidDestinationException("Invalid null destination.");
    dest.check();
   
    if (dest instanceof TemporaryQueue) {
      Connection tempQCnx = ((TemporaryQueue) dest).getCnx();

      if (tempQCnx == null || ! tempQCnx.equals(sess.getConnection()))
        throw new JMSSecurityException("Forbidden consumer on this temporary destination.");
    }
    else if (dest instanceof TemporaryTopic) {
      Connection tempTCnx = ((TemporaryTopic) dest).getCnx();
   
      if (tempTCnx == null || ! tempTCnx.equals(sess.getConnection()))
        throw new JMSSecurityException("Forbidden consumer on this temporary destination.");
    }

    try {
      ClientSelector.checks(selector);
    } catch (org.objectweb.joram.shared.excepts.SelectorException sE) {
      throw new InvalidSelectorException("Invalid selector syntax: " + sE);
    }

    // If the destination is a topic, the consumer is a subscriber:
    if (dest instanceof javax.jms.Topic) {
      if (subName == null) {
        subName = sess.getConnection().nextSubName();
        durableSubscriber = false;
      } else {
        durableSubscriber = true;
      }
      sess.syncRequest(
        new ConsumerSubRequest(dest.getName(),
                               subName,
                               selector,
                               noLocal,
                               durableSubscriber,
                               sess.isAsyncSub()));
      targetName = subName;
      this.noLocal = noLocal;
      queueMode = false;
    } else {
      targetName = dest.getName();
      queueMode = true;
    }

    this.sess = sess;
    this.dest = dest;
    this.selector = selector;
   
    closer = new Closer();

    setStatus(Status.OPEN);
  }

  /**
   * Constructs a consumer.
   *
   * @param sess  The session the consumer belongs to.
   * @param dest  The destination the consumer gets messages from.
   * @param selector  Selector for filtering messages.
   *
   * @exception InvalidDestinationException if an invalid destination is specified.
   * @exception InvalidSelectorException  If the selector syntax is invalid.
   * @exception IllegalStateException  If the connection is broken, or if the
   *                                   subscription is durable and already
   *                                   activated.
   * @exception JMSException           Generic exception.
   */
  MessageConsumer(Session sess, Destination dest, String selector) throws JMSException {
    this(sess, dest, selector, null, false);
  }

  private synchronized void setStatus(int status) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "MessageConsumer.setStatus(" + Status.toString(status) + ')');
    this.status = status;
  }

  public final String getTargetName() {
    return targetName;
  }

  public final boolean getQueueMode() {
    return queueMode;
  }

  protected synchronized void checkClosed() throws IllegalStateException {
    if (status == Status.CLOSE)
      throw new IllegalStateException("Forbidden call on a closed consumer.");
  }

  /** Returns a string view of this consumer. */
  public String toString() {
    return "Consumer:" + sess.getId();
  }
 
  /**
   * Sets the message consumer's MessageListener.
   * API method.
   * <p>
   * This method must not be called if the connection the consumer belongs to
   * is started, because the session would then be accessed by the thread
   * calling this method and by the thread controlling asynchronous deliveries.
   * This situation is clearly forbidden by the single threaded nature of
   * sessions. Moreover, unsetting a message listener without stopping the
   * connection may lead to the situation where asynchronous deliveries would
   * arrive on the connection, the session or the consumer without being
   * able to reach their target listener!
   *
   * @exception IllegalStateException  If the consumer is closed, or if the
   *              connection is broken.
   * @exception JMSException  If the request fails for any other reason.
   */
  public synchronized void setMessageListener(javax.jms.MessageListener messageListener) throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "MessageConsumer.setMessageListener(" + messageListener + ')');
    checkClosed();
    if (mcl != null) {
      if (messageListener == null) {
        sess.removeMessageListener(mcl, true);
        mcl = null;
      } else throw new IllegalStateException(
      "Message listener not null");
    } else {
      if (messageListener != null) {
        mcl = sess.addMessageListener(new SingleSessionConsumer(queueMode,
                                                                durableSubscriber,
                                                                selector,
                                                                targetName,
                                                                sess,
                                                                messageListener));
      }
      // else idempotent
    }
  }

  /**
   * Gets the message consumer's MessageListener.
   * API method.
   *
   * @exception IllegalStateException  If the consumer is closed.
   */
  public synchronized javax.jms.MessageListener getMessageListener() throws JMSException {
    checkClosed();
    if (mcl == null)
      return null;
    return mcl.getMessageListener();
  }

  /**
   * Gets this message consumer's message selector expression.
   * API method.
   *
   * @exception IllegalStateException  If the consumer is closed.
   */
  public final String getMessageSelector() throws JMSException {
    checkClosed();
    return selector;
  }

  /**
   * Receives the next message that arrives before the specified timeout.
   * API method.
   *
   * @exception IllegalStateException  If the consumer is closed, or if the
   *              connection is broken.
   * @exception JMSSecurityException  If the requester is not a READER on the
   *              destination.
   * @exception JMSException  If the request fails for any other reason.
   */
  public javax.jms.Message receive(long timeOut) throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "MessageConsumer.receive(" + timeOut + ')');
    checkClosed();
    return sess.receive(timeOut, timeOut, this, targetName, selector, queueMode);
  }

  /**
   * Receives the next message produced for this message consumer.
   * API method.
   *
   * @exception IllegalStateException  If the consumer is closed, or if the
   *              connection is broken.
   * @exception JMSSecurityException  If the requester is not a READER on the
   *              destination.
   * @exception JMSException  If the request fails for any other reason.
   */
  public javax.jms.Message receive() throws JMSException {
    return receive(0);
  }

  /**
   * Receives the next message if one is immediately available.
   * API method.
   *
   * @exception IllegalStateException  If the consumer is closed, or if the
   *              connection is broken.
   * @exception JMSSecurityException  If the requester is not a READER on the
   *              destination.
   * @exception JMSException  If the request fails for any other reason.
   */
  public javax.jms.Message receiveNoWait() throws JMSException {
    checkClosed();
    if (sess.getConnection().isStopped()) return null;

    return sess.receive(-1, 0, this, targetName, selector, queueMode);
  }

  /**
   * API method.
   *
   * @exception JMSException
   */
  public void close() throws JMSException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "MessageConsumer.close()");
    closer.close();
  }

  /**
   * This class synchronizes the close.
   * Close can't be synchronized with 'this'
   * because the MessageConsumer must be accessed
   * concurrently during its closure. So
   * we need a second lock.
   */
  class Closer {
    synchronized void close() throws JMSException {
      doClose();
    }
  }

  void doClose() throws JMSException {
    synchronized (this) {
      if (status == Status.CLOSE)
        return;
      // The status must be changed before
      // the call to Session.closeConsumer
      // in order to enable Session.preReceive
      // to check if the consumer has been closed.
      setStatus(Status.CLOSE);
    }
   
    if (!queueMode) {
      // For a topic, remove the subscription.
      if (durableSubscriber) {
        try {
          sess.syncRequest(new ConsumerCloseSubRequest(targetName));
        } catch (JMSException exc) {
          if (logger.isLoggable(BasicLevel.DEBUG))
            logger.log(BasicLevel.DEBUG, "", exc);
        }
      } else {
        try {
          sess.syncRequest(new ConsumerUnsubRequest(targetName));
        } catch (JMSException exc) {
          if (logger.isLoggable(BasicLevel.DEBUG))
            logger.log(BasicLevel.DEBUG, "", exc);
        }
      }
    }
   
    sess.closeConsumer(this);
   
    if (mcl != null) {
      // Stop the listener.
      sess.removeMessageListener(mcl, false);
    }
  }

  void activateMessageInput() throws JMSException {
    if (mcl != null)
      mcl.activateMessageInput();
  }

  void passivateMessageInput() throws JMSException {
    if (mcl != null)
      mcl.passivateMessageInput();
  }
}
TOP

Related Classes of org.objectweb.joram.client.jms.MessageConsumer$Status

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.