org.apache.activemq.ActiveMQMessageConsumer Maven / Gradle / Ivy
Show all versions of activemq-core Show documentation
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import javax.jms.IllegalStateException;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQMessage;
import org.apache.activemq.command.ActiveMQTempDestination;
import org.apache.activemq.command.ConsumerId;
import org.apache.activemq.command.ConsumerInfo;
import org.apache.activemq.command.MessageAck;
import org.apache.activemq.command.MessageDispatch;
import org.apache.activemq.command.MessagePull;
import org.apache.activemq.management.JMSConsumerStatsImpl;
import org.apache.activemq.management.StatsCapable;
import org.apache.activemq.management.StatsImpl;
import org.apache.activemq.selector.SelectorParser;
import org.apache.activemq.thread.Scheduler;
import org.apache.activemq.transaction.Synchronization;
import org.apache.activemq.util.Callback;
import org.apache.activemq.util.IntrospectionSupport;
import org.apache.activemq.util.JMSExceptionSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
import edu.emory.mathcs.backport.java.util.concurrent.Executors;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
/**
* A client uses a MessageConsumer
object to receive messages
* from a destination. A MessageConsumer
object is created by
* passing a Destination
object to a message-consumer creation
* method supplied by a session.
*
* MessageConsumer
is the parent interface for all message
* consumers.
*
* 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.
*
* A client may either synchronously receive a message consumer's messages or
* have the consumer asynchronously deliver them as they arrive.
*
* For synchronous receipt, a client can request the next message from a 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.
*
* For asynchronous delivery, a client can 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.
*
* @version $Revision: 1.22 $
* @see javax.jms.MessageConsumer
* @see javax.jms.QueueReceiver
* @see javax.jms.TopicSubscriber
* @see javax.jms.Session
*/
public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsCapable, ActiveMQDispatcher {
private static final Log log = LogFactory.getLog(ActiveMQMessageConsumer.class);
protected final ActiveMQSession session;
protected final ConsumerInfo info;
// These are the messages waiting to be delivered to the client
private final MessageDispatchChannel unconsumedMessages = new MessageDispatchChannel();
// The are the messages that were delivered to the consumer but that have
// not been acknowledged. It's kept in reverse order since we
// Always walk list in reverse order. Only used when session is client ack.
private final LinkedList deliveredMessages = new LinkedList();
private int deliveredCounter = 0;
private int additionalWindowSize = 0;
private int rollbackCounter = 0;
private long redeliveryDelay = 0;
private int ackCounter = 0;
private MessageListener messageListener;
private JMSConsumerStatsImpl stats;
private final String selector;
private boolean synchronizationRegistered = false;
private AtomicBoolean started = new AtomicBoolean(false);
private MessageAvailableListener availableListener;
private RedeliveryPolicy redeliveryPolicy;
private boolean optimizeAcknowledge;
private AtomicBoolean deliveryingAcknowledgements = new AtomicBoolean();
private ExecutorService executorService = null;
/**
* Create a MessageConsumer
*
* @param session
* @param dest
* @param name
* @param selector
* @param prefetch
* @param maximumPendingMessageCount TODO
* @param noLocal
* @param browser
* @param dispatchAsync
* @throws JMSException
*/
public ActiveMQMessageConsumer(ActiveMQSession session, ConsumerId consumerId, ActiveMQDestination dest,
String name, String selector, int prefetch, int maximumPendingMessageCount, boolean noLocal, boolean browser, boolean dispatchAsync)
throws JMSException {
if (dest == null) {
throw new InvalidDestinationException("Don't understand null destinations");
} else if (dest.getPhysicalName() == null) {
throw new InvalidDestinationException("The destination object was not given a physical name.");
} else if (dest.isTemporary()) {
String physicalName = dest.getPhysicalName();
if (physicalName == null) {
throw new IllegalArgumentException("Physical name of Destination should be valid: " + dest);
}
String connectionID = session.connection.getConnectionInfo().getConnectionId().getValue();
if (physicalName.indexOf(connectionID) < 0) {
throw new InvalidDestinationException("Cannot use a Temporary destination from another Connection");
}
if (session.connection.isDeleted(dest)) {
throw new InvalidDestinationException("Cannot use a Temporary destination that has been deleted");
}
}
this.session = session;
this.selector = selector;
this.redeliveryPolicy = session.connection.getRedeliveryPolicy();
this.info = new ConsumerInfo(consumerId);
this.info.setSubscriptionName(name);
this.info.setPrefetchSize(prefetch);
this.info.setCurrentPrefetchSize(prefetch);
this.info.setMaximumPendingMessageLimit(maximumPendingMessageCount);
this.info.setNoLocal(noLocal);
this.info.setDispatchAsync(dispatchAsync);
this.info.setRetroactive(this.session.connection.isUseRetroactiveConsumer());
// Allows the options on the destination to configure the consumerInfo
if (dest.getOptions() != null) {
HashMap options = new HashMap(dest.getOptions());
IntrospectionSupport.setProperties(this.info, options, "consumer.");
}
this.info.setDestination(dest);
this.info.setBrowser(browser);
if (selector != null && selector.trim().length() != 0) {
// Validate that the selector
new SelectorParser().parse(selector);
this.info.setSelector(selector);
} else {
this.info.setSelector(null);
}
this.stats = new JMSConsumerStatsImpl(session.getSessionStats(), dest);
this.optimizeAcknowledge=session.connection.isOptimizeAcknowledge()&&session.isAutoAcknowledge()
&&!info.isBrowser();
this.info.setOptimizedAcknowledge(this.optimizeAcknowledge);
try {
this.session.addConsumer(this);
this.session.syncSendPacket(info);
} catch (JMSException e) {
this.session.removeConsumer(this);
throw e;
}
if(session.connection.isStarted())
start();
}
public StatsImpl getStats() {
return stats;
}
public JMSConsumerStatsImpl getConsumerStats() {
return stats;
}
public RedeliveryPolicy getRedeliveryPolicy() {
return redeliveryPolicy;
}
/**
* Sets the redelivery policy used when messages are redelivered
*/
public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
this.redeliveryPolicy = redeliveryPolicy;
}
/**
* @return Returns the value.
*/
protected ConsumerId getConsumerId() {
return info.getConsumerId();
}
/**
* @return the consumer name - used for durable consumers
*/
protected String getConsumerName() {
return this.info.getSubscriptionName();
}
/**
* @return true if this consumer does not accept locally produced messages
*/
protected boolean isNoLocal() {
return info.isNoLocal();
}
/**
* Retrieve is a browser
*
* @return true if a browser
*/
protected boolean isBrowser() {
return info.isBrowser();
}
/**
* @return ActiveMQDestination
*/
protected ActiveMQDestination getDestination() {
return info.getDestination();
}
/**
* @return Returns the prefetchNumber.
*/
public int getPrefetchNumber() {
return info.getPrefetchSize();
}
/**
* @return true if this is a durable topic subscriber
*/
public boolean isDurableSubscriber() {
return info.getSubscriptionName()!=null && info.getDestination().isTopic();
}
/**
* Gets this message consumer's message selector expression.
*
* @return this message consumer's message selector, or null if no message
* selector exists for the message consumer (that is, if the message
* selector was not set or was set to null or the empty string)
* @throws JMSException
* if the JMS provider fails to receive the next message due to
* some internal error.
*/
public String getMessageSelector() throws JMSException {
checkClosed();
return selector;
}
/**
* Gets the message consumer's MessageListener
.
*
* @return the listener for the message consumer, or null if no listener is
* set
* @throws JMSException
* if the JMS provider fails to get the message listener due to
* some internal error.
* @see javax.jms.MessageConsumer#setMessageListener(javax.jms.MessageListener)
*/
public MessageListener getMessageListener() throws JMSException {
checkClosed();
return this.messageListener;
}
/**
* Sets the message consumer's MessageListener
.
*
* Setting the message listener to null is the equivalent of unsetting the
* message listener for the message consumer.
*
* The effect of calling MessageConsumer.setMessageListener
* while messages are being consumed by an existing listener or the consumer
* is being used to consume messages synchronously is undefined.
*
* @param listener
* the listener to which the messages are to be delivered
* @throws JMSException
* if the JMS provider fails to receive the next message due to
* some internal error.
* @see javax.jms.MessageConsumer#getMessageListener
*/
public void setMessageListener(MessageListener listener) throws JMSException {
checkClosed();
if (info.getPrefetchSize() == 0) {
throw new JMSException("Illegal prefetch size of zero. This setting is not supported for asynchronous consumers please set a value of at least 1");
}
this.messageListener = listener;
if (listener != null) {
boolean wasRunning = session.isRunning();
if (wasRunning)
session.stop();
session.redispatch(unconsumedMessages);
if (wasRunning)
session.start();
}
}
public MessageAvailableListener getAvailableListener() {
return availableListener;
}
/**
* Sets the listener used to notify synchronous consumers that there is a message
* available so that the {@link MessageConsumer#receiveNoWait()} can be called.
*/
public void setAvailableListener(MessageAvailableListener availableListener) {
this.availableListener = availableListener;
}
/**
* Used to get an enqueued message from the unconsumedMessages list. The
* amount of time this method blocks is based on the timeout value. - if
* timeout==-1 then it blocks until a message is received. - if timeout==0
* then it it tries to not block at all, it returns a message if it is
* available - if timeout>0 then it blocks up to timeout amount of time.
*
* Expired messages will consumed by this method.
*
* @throws JMSException
*
* @return null if we timeout or if the consumer is closed.
*/
private MessageDispatch dequeue(long timeout) throws JMSException {
try {
long deadline = 0;
if (timeout > 0) {
deadline = System.currentTimeMillis() + timeout;
}
while (true) {
MessageDispatch md = unconsumedMessages.dequeue(timeout);
if (md == null) {
if (timeout > 0 && !unconsumedMessages.isClosed()) {
timeout = Math.max(deadline - System.currentTimeMillis(), 0);
} else {
return null;
}
} else if ( md.getMessage()==null ) {
return null;
} else if (md.getMessage().isExpired()) {
if (log.isDebugEnabled()) {
log.debug("Received expired message: " + md);
}
beforeMessageIsConsumed(md);
afterMessageIsConsumed(md, true);
if (timeout > 0) {
timeout = Math.max(deadline - System.currentTimeMillis(), 0);
}
} else {
if (log.isDebugEnabled()) {
log.debug("Received message: " + md);
}
return md;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw JMSExceptionSupport.create(e);
}
}
/**
* Receives the next message produced for this message consumer.
*
* This call blocks indefinitely until a message is produced or until this
* message consumer is closed.
*
* If this receive
is done within a transaction, the consumer
* retains the message until the transaction commits.
*
* @return the next message produced for this message consumer, or null if
* this message consumer is concurrently closed
*/
public Message receive() throws JMSException {
checkClosed();
checkMessageListener();
sendPullCommand(0);
MessageDispatch md = dequeue(-1);
if (md == null)
return null;
beforeMessageIsConsumed(md);
afterMessageIsConsumed(md, false);
return createActiveMQMessage(md);
}
/**
* @param md
* @return
*/
private ActiveMQMessage createActiveMQMessage(final MessageDispatch md) {
ActiveMQMessage m = (ActiveMQMessage) md.getMessage().copy();
if (session.isClientAcknowledge()) {
m.setAcknowledgeCallback(new Callback() {
public void execute() throws Exception {
session.checkClosed();
session.acknowledge();
}
});
}
return m;
}
/**
* Receives the next message that arrives within the specified timeout
* interval.
*
* This call blocks until a message arrives, the timeout expires, or this
* message consumer is closed. A timeout
of zero never
* expires, and the call blocks indefinitely.
*
* @param timeout
* the timeout value (in milliseconds), a time out of zero never expires.
* @return the next message produced for this message consumer, or null if
* the timeout expires or this message consumer is concurrently
* closed
*/
public Message receive(long timeout) throws JMSException {
checkClosed();
checkMessageListener();
if (timeout == 0) {
return this.receive();
}
sendPullCommand(timeout);
while (timeout > 0) {
MessageDispatch md;
if (info.getPrefetchSize() == 0) {
md = dequeue(-1); // We let the broker let us know when we timeout.
} else {
md = dequeue(timeout);
}
if (md == null)
return null;
beforeMessageIsConsumed(md);
afterMessageIsConsumed(md, false);
return createActiveMQMessage(md);
}
return null;
}
/**
* Receives the next message if one is immediately available.
*
* @return the next message produced for this message consumer, or null if
* one is not available
* @throws JMSException
* if the JMS provider fails to receive the next message due to
* some internal error.
*/
public Message receiveNoWait() throws JMSException {
checkClosed();
checkMessageListener();
sendPullCommand(-1);
MessageDispatch md;
if (info.getPrefetchSize() == 0) {
md = dequeue(-1); // We let the broker let us know when we timeout.
} else {
md = dequeue(0);
}
if (md == null)
return null;
beforeMessageIsConsumed(md);
afterMessageIsConsumed(md, false);
return createActiveMQMessage(md);
}
/**
* Closes the message consumer.
*
* Since a provider may allocate some resources on behalf of a
* MessageConsumer
outside the Java virtual machine, clients should
* close them when they are not needed. Relying on garbage collection to
* eventually reclaim these resources may not be timely enough.
*
* This call blocks until a receive
or message listener in
* progress has completed. A blocked message consumer receive
* call returns null when this message consumer is closed.
*
* @throws JMSException
* if the JMS provider fails to close the consumer due to some
* internal error.
*/
public void close() throws JMSException {
if (!unconsumedMessages.isClosed()) {
dispose();
this.session.syncSendPacket(info.createRemoveCommand());
}
}
void clearMessagesInProgress(){
unconsumedMessages.clear();
}
void deliverAcks(){
MessageAck ack=null;
if(deliveryingAcknowledgements.compareAndSet(false,true)){
if(this.optimizeAcknowledge){
if(!deliveredMessages.isEmpty()){
MessageDispatch md=(MessageDispatch) deliveredMessages.getFirst();
ack=new MessageAck(md,MessageAck.STANDARD_ACK_TYPE,deliveredMessages.size());
deliveredMessages.clear();
ackCounter=0;
}
}
if(ack!=null){
final MessageAck ackToSend=ack;
if(executorService==null){
executorService=Executors.newSingleThreadExecutor();
}
executorService.submit(new Runnable(){
public void run(){
try{
session.asyncSendPacket(ackToSend);
}catch(JMSException e){
log.error("Failed to delivered acknowledgements",e);
}finally{
deliveryingAcknowledgements.set(false);
}
}
});
}else{
deliveryingAcknowledgements.set(false);
}
}
}
public void dispose() throws JMSException {
if (!unconsumedMessages.isClosed()) {
// Do we have any acks we need to send out before closing?
// Ack any delivered messages now. (session may still
// commit/rollback the acks).
deliverAcks();//only processes optimized acknowledgements
if (executorService!=null){
executorService.shutdown();
try {
executorService.awaitTermination(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if ((session.isTransacted() || session.isDupsOkAcknowledge())) {
acknowledge();
}
deliveredMessages.clear();
unconsumedMessages.close();
this.session.removeConsumer(this);
}
}
/**
* @throws IllegalStateException
*/
protected void checkClosed() throws IllegalStateException {
if (unconsumedMessages.isClosed()) {
throw new IllegalStateException("The Consumer is closed");
}
}
/**
* If we have a zero prefetch specified then send a pull command to the broker to pull a message
* we are about to receive
*
*/
protected void sendPullCommand(long timeout) throws JMSException {
if (info.getPrefetchSize() == 0 && unconsumedMessages.isEmpty()) {
MessagePull messagePull = new MessagePull();
messagePull.configure(info);
messagePull.setTimeout(timeout);
session.asyncSendPacket(messagePull);
}
}
protected void checkMessageListener() throws JMSException {
session.checkMessageListener();
}
protected void setOptimizeAcknowledge(boolean value){
if (optimizeAcknowledge && !value){
deliverAcks();
}
optimizeAcknowledge=value;
}
protected void setPrefetchSize(int prefetch){
deliverAcks();
this.info.setCurrentPrefetchSize(prefetch);
}
private void beforeMessageIsConsumed(MessageDispatch md) throws JMSException {
md.setDeliverySequenceId(session.getNextDeliveryId());
if (!session.isDupsOkAcknowledge()) {
deliveredMessages.addFirst(md);
if( session.isTransacted() ) {
ackLater(md,MessageAck.DELIVERED_ACK_TYPE);
}
}
}
private void afterMessageIsConsumed(MessageDispatch md,boolean messageExpired) throws JMSException{
if(unconsumedMessages.isClosed())
return;
if(messageExpired){
ackLater(md,MessageAck.DELIVERED_ACK_TYPE);
}else{
stats.onMessage();
if( session.isTransacted() ) {
} else if(session.isAutoAcknowledge()) {
if(!deliveredMessages.isEmpty()){
if(optimizeAcknowledge){
if(deliveryingAcknowledgements.compareAndSet(false,true)){
ackCounter++;
if(ackCounter>=(info.getCurrentPrefetchSize()*.65)){
MessageAck ack=new MessageAck(md,MessageAck.STANDARD_ACK_TYPE,deliveredMessages.size());
session.asyncSendPacket(ack);
ackCounter=0;
deliveredMessages.clear();
}
deliveryingAcknowledgements.set(false);
}
}else{
MessageAck ack=new MessageAck(md,MessageAck.STANDARD_ACK_TYPE,deliveredMessages.size());
session.asyncSendPacket(ack);
deliveredMessages.clear();
}
}
} else if(session.isDupsOkAcknowledge()){
ackLater(md,MessageAck.STANDARD_ACK_TYPE);
} else if(session.isClientAcknowledge()){
ackLater(md,MessageAck.DELIVERED_ACK_TYPE);
} else{
throw new IllegalStateException("Invalid session state.");
}
}
}
private void ackLater(MessageDispatch md, byte ackType) throws JMSException {
// Don't acknowledge now, but we may need to let the broker know the
// consumer got the message
// to expand the pre-fetch window
if (session.isTransacted()) {
session.doStartTransaction();
if (!synchronizationRegistered) {
synchronizationRegistered = true;
session.getTransactionContext().addSynchronization(new Synchronization() {
public void beforeEnd() throws Exception {
acknowledge();
synchronizationRegistered = false;
}
public void afterCommit() throws Exception {
commit();
synchronizationRegistered = false;
}
public void afterRollback() throws Exception {
rollback();
synchronizationRegistered = false;
}
});
}
}
// The delivered message list is only needed for the recover method
// which is only used with client ack.
deliveredCounter++;
if ((0.5 * info.getPrefetchSize()) <= (deliveredCounter - additionalWindowSize)) {
MessageAck ack = new MessageAck(md, ackType, deliveredCounter);
ack.setTransactionId(session.getTransactionContext().getTransactionId());
session.asyncSendPacket(ack);
additionalWindowSize = deliveredCounter;
// When using DUPS ok, we do a real ack.
if (ackType == MessageAck.STANDARD_ACK_TYPE) {
deliveredCounter = additionalWindowSize = 0;
}
}
}
/**
* Acknowledge all the messages that have been delivered to the client upto
* this point.
*
* @throws JMSException
*/
public void acknowledge() throws JMSException {
if (deliveredMessages.isEmpty())
return;
// Acknowledge the last message.
MessageDispatch lastMd = (MessageDispatch) deliveredMessages.get(0);
MessageAck ack = new MessageAck(lastMd, MessageAck.STANDARD_ACK_TYPE, deliveredMessages.size());
if (session.isTransacted()) {
session.doStartTransaction();
ack.setTransactionId(session.getTransactionContext().getTransactionId());
}
session.asyncSendPacket(ack);
// Adjust the counters
deliveredCounter -= deliveredMessages.size();
additionalWindowSize = Math.max(0, additionalWindowSize - deliveredMessages.size());
if (!session.isTransacted()) {
deliveredMessages.clear();
}
}
public void commit() throws JMSException {
deliveredMessages.clear();
rollbackCounter = 0;
redeliveryDelay = 0;
}
public void rollback() throws JMSException{
synchronized(unconsumedMessages.getMutex()){
if(optimizeAcknowledge){
// remove messages read but not acked at the broker yet through optimizeAcknowledge
for(int i=0;(i 0 )
redeliveryDelay = redeliveryPolicy.getRedeliveryDelay(redeliveryDelay);
rollbackCounter++;
if(redeliveryPolicy.getMaximumRedeliveries() != RedeliveryPolicy.NO_MAXIMUM_REDELIVERIES
&& rollbackCounter>redeliveryPolicy.getMaximumRedeliveries()){
// We need to NACK the messages so that they get sent to the
// DLQ.
// Acknowledge the last message.
MessageDispatch lastMd=(MessageDispatch) deliveredMessages.get(0);
MessageAck ack=new MessageAck(lastMd,MessageAck.POSION_ACK_TYPE,deliveredMessages.size());
session.asyncSendPacket(ack);
// Adjust the window size.
additionalWindowSize=Math.max(0,additionalWindowSize-deliveredMessages.size());
rollbackCounter=0;
redeliveryDelay=0;
}else{
// stop the delivery of messages.
unconsumedMessages.stop();
for(Iterator iter=deliveredMessages.iterator();iter.hasNext();){
MessageDispatch md=(MessageDispatch) iter.next();
md.getMessage().onMessageRolledBack();
unconsumedMessages.enqueueFirst(md);
}
if( redeliveryDelay > 0 ) {
// Start up the delivery again a little later.
Scheduler.executeAfterDelay(new Runnable(){
public void run(){
try{
if(started.get())
start();
}catch(JMSException e){
session.connection.onAsyncException(e);
}
}
},redeliveryDelay);
} else {
start();
}
}
deliveredCounter-=deliveredMessages.size();
deliveredMessages.clear();
}
if(messageListener!=null){
session.redispatch(unconsumedMessages);
}
}
public void dispatch(MessageDispatch md) {
MessageListener listener = this.messageListener;
try {
synchronized(unconsumedMessages.getMutex()){
if (unconsumedMessages.isClosed()) {
return;
}
if (listener == null || !unconsumedMessages.isRunning() ) {
unconsumedMessages.enqueue(md);
if (availableListener != null) {
availableListener.onMessageAvailable(this);
}
return;
}
}
ActiveMQMessage message = createActiveMQMessage(md);
beforeMessageIsConsumed(md);
try {
listener.onMessage(message);
afterMessageIsConsumed(md, false);
} catch (RuntimeException e) {
if ( session.isDupsOkAcknowledge() || session.isAutoAcknowledge() ) {
// Redeliver the message
} else {
// Transacted or Client ack: Deliver the next message.
afterMessageIsConsumed(md, false);
}
log.warn("Exception while processing message: " + e, e);
}
} catch (Exception e) {
session.connection.onAsyncException(e);
}
}
public int getMessageSize() {
return unconsumedMessages.size();
}
public void start() throws JMSException {
if (unconsumedMessages.isClosed()) {
return;
}
started.set(true);
unconsumedMessages.start();
session.executor.wakeup();
}
public void stop() {
started.set(false);
unconsumedMessages.stop();
}
public String toString() {
return "ActiveMQMessageConsumer { value=" +info.getConsumerId()+", started=" +started.get()+" }";
}
/**
* Delivers a message to the message listener.
* @return
* @throws JMSException
*/
public boolean iterate() {
MessageListener listener = this.messageListener;
if( listener!=null ) {
MessageDispatch md = unconsumedMessages.dequeueNoWait();
if( md!=null ) {
try {
ActiveMQMessage message = createActiveMQMessage(md);
beforeMessageIsConsumed(md);
listener.onMessage(message);
afterMessageIsConsumed(md, false);
} catch (JMSException e) {
session.connection.onAsyncException(e);
}
return true;
}
}
return false;
}
public boolean isInUse(ActiveMQTempDestination destination) {
return info.getDestination().equals(destination);
}
}